mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-17 12:27:46 +02:00
Sync from development - prepare for v0.3.0
This commit is contained in:
73
jackify/frontends/gui/mixins/main_window_backend.py
Normal file
73
jackify/frontends/gui/mixins/main_window_backend.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Main window backend initialization mixin.
|
||||
System info, config, modlist service, protontricks service, resource limits.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from jackify.backend.models.configuration import SystemInfo
|
||||
from jackify.backend.services.modlist_service import ModlistService
|
||||
|
||||
|
||||
def _debug_print(message):
|
||||
from jackify.backend.handlers.config_handler import ConfigHandler
|
||||
ch = ConfigHandler()
|
||||
if ch.get('debug_mode', False):
|
||||
print(message)
|
||||
|
||||
|
||||
class MainWindowBackendMixin:
|
||||
"""Mixin for backend service initialization."""
|
||||
|
||||
def _initialize_backend(self):
|
||||
from jackify.shared.steam_utils import detect_steam_installation_types
|
||||
is_flatpak, is_native = detect_steam_installation_types()
|
||||
self.system_info = SystemInfo(
|
||||
is_steamdeck=self._is_steamdeck(),
|
||||
is_flatpak_steam=is_flatpak,
|
||||
is_native_steam=is_native
|
||||
)
|
||||
self._apply_resource_limits()
|
||||
from jackify.backend.handlers.config_handler import ConfigHandler
|
||||
self.config_handler = ConfigHandler()
|
||||
self.backend_services = {'modlist_service': ModlistService(self.system_info)}
|
||||
self.gui_services = {}
|
||||
from jackify.backend.services.protontricks_detection_service import ProtontricksDetectionService
|
||||
self.protontricks_service = ProtontricksDetectionService(steamdeck=self.system_info.is_steamdeck)
|
||||
from jackify.backend.services.update_service import UpdateService
|
||||
from jackify import __version__
|
||||
self.update_service = UpdateService(__version__)
|
||||
_debug_print(f"GUI Backend initialized - Steam Deck: {self.system_info.is_steamdeck}")
|
||||
|
||||
def _is_steamdeck(self):
|
||||
try:
|
||||
if os.path.exists("/etc/os-release"):
|
||||
with open("/etc/os-release", "r") as f:
|
||||
content = f.read()
|
||||
if "steamdeck" in content:
|
||||
return True
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _apply_resource_limits(self):
|
||||
try:
|
||||
from jackify.backend.services.resource_manager import ResourceManager
|
||||
resource_manager = ResourceManager()
|
||||
success = resource_manager.apply_recommended_limits()
|
||||
if success:
|
||||
status = resource_manager.get_limit_status()
|
||||
if status['target_achieved']:
|
||||
_debug_print(f"Resource limits optimized: file descriptors set to {status['current_soft']}")
|
||||
else:
|
||||
print(f"Resource limits improved: file descriptors increased to {status['current_soft']} (target: {status['target_limit']})")
|
||||
else:
|
||||
status = resource_manager.get_limit_status()
|
||||
print(f"Warning: Could not optimize resource limits: current file descriptors={status['current_soft']}, target={status['target_limit']}")
|
||||
from jackify.backend.handlers.config_handler import ConfigHandler
|
||||
config_handler = ConfigHandler()
|
||||
if config_handler.get('debug_mode', False):
|
||||
instructions = resource_manager.get_manual_increase_instructions()
|
||||
print(f"Manual increase instructions available for {instructions['distribution']}")
|
||||
except Exception as e:
|
||||
print(f"Warning: Error applying resource limits: {e}")
|
||||
117
jackify/frontends/gui/mixins/main_window_dialogs.py
Normal file
117
jackify/frontends/gui/mixins/main_window_dialogs.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""
|
||||
Main window dialogs and cleanup mixin.
|
||||
Settings, About, open URL, cleanup_processes, closeEvent.
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from jackify.frontends.gui.dialogs.settings_dialog import SettingsDialog
|
||||
|
||||
|
||||
class MainWindowDialogsMixin:
|
||||
"""Mixin for settings/about dialogs, open URL, and cleanup."""
|
||||
|
||||
def open_settings_dialog(self):
|
||||
try:
|
||||
if self._settings_dialog is not None:
|
||||
try:
|
||||
if self._settings_dialog.isVisible():
|
||||
self._settings_dialog.raise_()
|
||||
self._settings_dialog.activateWindow()
|
||||
return
|
||||
else:
|
||||
self._settings_dialog = None
|
||||
except RuntimeError:
|
||||
self._settings_dialog = None
|
||||
dlg = SettingsDialog(self)
|
||||
self._settings_dialog = dlg
|
||||
|
||||
def on_dialog_finished():
|
||||
self._settings_dialog = None
|
||||
dlg.finished.connect(on_dialog_finished)
|
||||
dlg.exec()
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Exception in open_settings_dialog: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self._settings_dialog = None
|
||||
|
||||
def open_about_dialog(self):
|
||||
try:
|
||||
from jackify.frontends.gui.dialogs.about_dialog import AboutDialog
|
||||
if self._about_dialog is not None:
|
||||
try:
|
||||
if self._about_dialog.isVisible():
|
||||
self._about_dialog.raise_()
|
||||
self._about_dialog.activateWindow()
|
||||
return
|
||||
else:
|
||||
self._about_dialog = None
|
||||
except RuntimeError:
|
||||
self._about_dialog = None
|
||||
dlg = AboutDialog(self.system_info, self)
|
||||
self._about_dialog = dlg
|
||||
|
||||
def on_dialog_finished():
|
||||
self._about_dialog = None
|
||||
dlg.finished.connect(on_dialog_finished)
|
||||
dlg.exec()
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Exception in open_about_dialog: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self._about_dialog = None
|
||||
|
||||
def _open_url(self, url: str):
|
||||
env = os.environ.copy()
|
||||
appimage_vars = [
|
||||
'LD_LIBRARY_PATH', 'PYTHONPATH', 'PYTHONHOME',
|
||||
'QT_PLUGIN_PATH', 'QML2_IMPORT_PATH',
|
||||
]
|
||||
if 'APPIMAGE' in env or 'APPDIR' in env:
|
||||
for var in appimage_vars:
|
||||
env.pop(var, None)
|
||||
subprocess.Popen(
|
||||
['xdg-open', url],
|
||||
env=env,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
start_new_session=True
|
||||
)
|
||||
|
||||
def cleanup_processes(self):
|
||||
try:
|
||||
if hasattr(self, '_update_thread') and self._update_thread is not None:
|
||||
if self._update_thread.isRunning():
|
||||
self._update_thread.quit()
|
||||
self._update_thread.wait(2000)
|
||||
self._update_thread = None
|
||||
if hasattr(self, '_gallery_cache_preload_thread') and self._gallery_cache_preload_thread is not None:
|
||||
if self._gallery_cache_preload_thread.isRunning():
|
||||
self._gallery_cache_preload_thread.quit()
|
||||
self._gallery_cache_preload_thread.wait(2000)
|
||||
self._gallery_cache_preload_thread = None
|
||||
for service in self.gui_services.values():
|
||||
if hasattr(service, 'cleanup'):
|
||||
service.cleanup()
|
||||
screens = [
|
||||
self.modlist_tasks_screen, self.install_modlist_screen,
|
||||
self.configure_new_modlist_screen, self.configure_existing_modlist_screen,
|
||||
]
|
||||
for screen in screens:
|
||||
if hasattr(screen, 'cleanup_processes'):
|
||||
screen.cleanup_processes()
|
||||
elif hasattr(screen, 'cleanup'):
|
||||
screen.cleanup()
|
||||
try:
|
||||
subprocess.run(['pkill', '-f', 'jackify-engine'], timeout=5, capture_output=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Error during cleanup: {e}")
|
||||
|
||||
def closeEvent(self, event):
|
||||
self._save_geometry_on_quit()
|
||||
self.cleanup_processes()
|
||||
event.accept()
|
||||
207
jackify/frontends/gui/mixins/main_window_geometry.py
Normal file
207
jackify/frontends/gui/mixins/main_window_geometry.py
Normal file
@@ -0,0 +1,207 @@
|
||||
"""
|
||||
Main window geometry and resize mixin.
|
||||
Window flags, save/restore geometry, compact mode, responsive minimum, resize handling.
|
||||
"""
|
||||
|
||||
from PySide6.QtWidgets import QMainWindow, QApplication
|
||||
from PySide6.QtCore import Qt, QTimer, QRect
|
||||
|
||||
from jackify.frontends.gui.utils import get_screen_geometry, set_responsive_minimum
|
||||
|
||||
ENABLE_WINDOW_HEIGHT_ANIMATION = False
|
||||
|
||||
|
||||
def _debug_print(message):
|
||||
from jackify.backend.handlers.config_handler import ConfigHandler
|
||||
ch = ConfigHandler()
|
||||
if ch.get('debug_mode', False):
|
||||
print(message)
|
||||
|
||||
|
||||
class MainWindowGeometryMixin:
|
||||
"""Mixin for window geometry, save/restore, compact mode, and resize behavior."""
|
||||
|
||||
def _apply_standard_window_flags(self):
|
||||
window_flags = self.windowFlags()
|
||||
window_flags |= (
|
||||
Qt.Window
|
||||
| Qt.WindowTitleHint
|
||||
| Qt.WindowSystemMenuHint
|
||||
| Qt.WindowMinimizeButtonHint
|
||||
| Qt.WindowMaximizeButtonHint
|
||||
| Qt.WindowCloseButtonHint
|
||||
)
|
||||
window_flags &= ~Qt.CustomizeWindowHint
|
||||
self.setWindowFlags(window_flags)
|
||||
|
||||
def _restore_geometry(self):
|
||||
width, height = self._calculate_initial_window_size()
|
||||
height = min(height, self._compact_height)
|
||||
self.resize(width, height)
|
||||
self._center_on_screen(width, height)
|
||||
|
||||
def _save_geometry_on_quit(self):
|
||||
if self._is_compact_mode():
|
||||
self._save_geometry()
|
||||
else:
|
||||
from PySide6.QtCore import QSettings
|
||||
settings = QSettings("Jackify", "Jackify")
|
||||
settings.remove("windowGeometry")
|
||||
|
||||
def _is_compact_mode(self) -> bool:
|
||||
try:
|
||||
if hasattr(self, 'install_modlist_screen') and hasattr(self.install_modlist_screen, 'show_details_checkbox'):
|
||||
if self.install_modlist_screen.show_details_checkbox.isChecked():
|
||||
return False
|
||||
if hasattr(self, 'install_ttw_screen') and hasattr(self.install_ttw_screen, 'show_details_checkbox'):
|
||||
if self.install_ttw_screen.show_details_checkbox.isChecked():
|
||||
return False
|
||||
if hasattr(self, 'configure_new_modlist_screen') and hasattr(self.configure_new_modlist_screen, 'show_details_checkbox'):
|
||||
if self.configure_new_modlist_screen.show_details_checkbox.isChecked():
|
||||
return False
|
||||
if hasattr(self, 'configure_existing_modlist_screen') and hasattr(self.configure_existing_modlist_screen, 'show_details_checkbox'):
|
||||
if self.configure_existing_modlist_screen.show_details_checkbox.isChecked():
|
||||
return False
|
||||
except Exception:
|
||||
pass
|
||||
return True
|
||||
|
||||
def _save_geometry(self):
|
||||
from PySide6.QtCore import QSettings
|
||||
settings = QSettings("Jackify", "Jackify")
|
||||
settings.setValue("windowGeometry", self.saveGeometry())
|
||||
|
||||
def apply_responsive_minimum(self, min_width: int = 1100, min_height: int = 600):
|
||||
set_responsive_minimum(self, min_width=min_width, min_height=min_height, margin=self._window_margin)
|
||||
|
||||
def _calculate_initial_window_size(self):
|
||||
_, _, screen_width, screen_height = get_screen_geometry(self)
|
||||
if not screen_width or not screen_height:
|
||||
return (self._base_min_width, self._base_min_height)
|
||||
width = min(
|
||||
max(self._base_min_width, int(screen_width * 0.85)),
|
||||
screen_width - self._window_margin
|
||||
)
|
||||
height = min(
|
||||
max(self._base_min_height, int(screen_height * 0.75)),
|
||||
screen_height - self._window_margin
|
||||
)
|
||||
return (width, height)
|
||||
|
||||
def _center_on_screen(self, width: int, height: int):
|
||||
_, _, screen_width, screen_height = get_screen_geometry(self)
|
||||
if not screen_width or not screen_height:
|
||||
return
|
||||
x = max(0, (screen_width - width) // 2)
|
||||
y = max(0, (screen_height - height) // 2)
|
||||
self.move(x, y)
|
||||
|
||||
def _ensure_within_available_geometry(self):
|
||||
from PySide6.QtCore import QRect
|
||||
_, _, screen_width, screen_height = get_screen_geometry(self)
|
||||
if not screen_width or not screen_height:
|
||||
return
|
||||
current_geometry = self.geometry()
|
||||
new_width = min(current_geometry.width(), screen_width - self._window_margin)
|
||||
new_height = min(current_geometry.height(), screen_height - self._window_margin)
|
||||
new_width = max(new_width, self.minimumWidth())
|
||||
new_height = max(new_height, self.minimumHeight())
|
||||
new_x = min(max(current_geometry.x(), 0), screen_width - new_width)
|
||||
new_y = min(max(current_geometry.y(), 0), screen_height - new_height)
|
||||
self.setGeometry(new_x, new_y, new_width, new_height)
|
||||
|
||||
def _on_resize_event_geometry(self, event):
|
||||
super().resizeEvent(event)
|
||||
if self._is_compact_mode():
|
||||
if not hasattr(self, '_geometry_save_timer'):
|
||||
self._geometry_save_timer = QTimer()
|
||||
self._geometry_save_timer.setSingleShot(True)
|
||||
self._geometry_save_timer.timeout.connect(self._save_geometry)
|
||||
self._geometry_save_timer.stop()
|
||||
self._geometry_save_timer.start(500)
|
||||
|
||||
def _geometry_show_event(self, event):
|
||||
super().showEvent(event)
|
||||
if not self._initial_show_adjusted:
|
||||
self._initial_show_adjusted = True
|
||||
if not (hasattr(self, 'system_info') and self.system_info.is_steamdeck):
|
||||
self.setWindowState(Qt.WindowNoState)
|
||||
self.apply_responsive_minimum(self._base_min_width, self._base_min_height)
|
||||
self._ensure_within_available_geometry()
|
||||
|
||||
def _maintain_fullscreen_on_deck(self, index):
|
||||
if hasattr(self, 'system_info') and self.system_info.is_steamdeck:
|
||||
if not self.isMaximized():
|
||||
self.showMaximized()
|
||||
|
||||
def _on_child_resize_request(self, mode: str):
|
||||
_debug_print(f"DEBUG: _on_child_resize_request called with mode='{mode}', current_size={self.size()}")
|
||||
try:
|
||||
if self.system_info and self.system_info.is_steamdeck:
|
||||
_debug_print("DEBUG: Steam Deck detected, ignoring resize request")
|
||||
try:
|
||||
if hasattr(self, 'install_ttw_screen') and self.install_ttw_screen.show_details_checkbox:
|
||||
self.install_ttw_screen.show_details_checkbox.setVisible(False)
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
if mode == "expand":
|
||||
target_height = self._compact_height + self._details_extra_height
|
||||
self._resize_height(target_height)
|
||||
elif mode == "collapse" or mode == "compact":
|
||||
self._resize_height(self._compact_height)
|
||||
else:
|
||||
self.apply_responsive_minimum(self._base_min_width, self._base_min_height)
|
||||
|
||||
def _resize_height(self, requested_height: int):
|
||||
target_height = self._clamp_height_to_screen(requested_height)
|
||||
self.apply_responsive_minimum(self._base_min_width, self._base_min_height)
|
||||
if ENABLE_WINDOW_HEIGHT_ANIMATION:
|
||||
self._animate_height(target_height)
|
||||
return
|
||||
geom = self.geometry()
|
||||
new_y = geom.y()
|
||||
_, _, _, screen_height = get_screen_geometry(self)
|
||||
max_bottom = max(self._base_min_height, screen_height - self._window_margin)
|
||||
if new_y + target_height > max_bottom:
|
||||
new_y = max(0, max_bottom - target_height)
|
||||
self._programmatic_resize = True
|
||||
self.setGeometry(geom.x(), new_y, geom.width(), target_height)
|
||||
QTimer.singleShot(100, lambda: setattr(self, '_programmatic_resize', False))
|
||||
|
||||
def _clamp_height_to_screen(self, requested_height: int) -> int:
|
||||
_, _, _, screen_height = get_screen_geometry(self)
|
||||
available = max(self._base_min_height, screen_height - self._window_margin)
|
||||
return max(self._base_min_height, min(requested_height, available))
|
||||
|
||||
def _animate_height(self, target_height: int, duration_ms: int = 180):
|
||||
try:
|
||||
from PySide6.QtCore import QEasingCurve, QPropertyAnimation, QRect
|
||||
except Exception:
|
||||
before = self.size()
|
||||
self._programmatic_resize = True
|
||||
self.resize(self.size().width(), target_height)
|
||||
_debug_print(f"DEBUG: Animated fallback resize from {before} to {self.size()}")
|
||||
QTimer.singleShot(100, lambda: setattr(self, '_programmatic_resize', False))
|
||||
return
|
||||
start_rect = self.geometry()
|
||||
end_rect = QRect(start_rect.x(), start_rect.y(), start_rect.width(), self._clamp_height_to_screen(target_height))
|
||||
screen = QApplication.primaryScreen()
|
||||
if screen:
|
||||
screen_geometry = screen.availableGeometry()
|
||||
would_be_bottom = start_rect.y() + target_height
|
||||
if would_be_bottom > screen_geometry.bottom():
|
||||
new_y = screen_geometry.bottom() - target_height
|
||||
if new_y < screen_geometry.top():
|
||||
new_y = screen_geometry.top()
|
||||
end_rect.moveTop(new_y)
|
||||
self._resize_anim = QPropertyAnimation(self, b"geometry")
|
||||
self._resize_anim.setDuration(duration_ms)
|
||||
self._resize_anim.setEasingCurve(QEasingCurve.OutCubic)
|
||||
self._resize_anim.setStartValue(start_rect)
|
||||
self._resize_anim.setEndValue(end_rect)
|
||||
self._programmatic_resize = True
|
||||
self._resize_anim.finished.connect(lambda: setattr(self, '_programmatic_resize', False))
|
||||
self._resize_anim.start()
|
||||
102
jackify/frontends/gui/mixins/main_window_startup.py
Normal file
102
jackify/frontends/gui/mixins/main_window_startup.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""
|
||||
Main window startup and background tasks mixin.
|
||||
Gallery cache preload, protontricks check, update check.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from PySide6.QtCore import QThread, Signal, QTimer
|
||||
from PySide6.QtWidgets import QDialog
|
||||
|
||||
|
||||
def _debug_print(message):
|
||||
from jackify.backend.handlers.config_handler import ConfigHandler
|
||||
ch = ConfigHandler()
|
||||
if ch.get('debug_mode', False):
|
||||
print(message)
|
||||
|
||||
|
||||
class MainWindowStartupMixin:
|
||||
"""Mixin for startup and background tasks."""
|
||||
|
||||
def _start_gallery_cache_preload(self):
|
||||
from PySide6.QtCore import QThread, Signal
|
||||
|
||||
class GalleryCachePreloadThread(QThread):
|
||||
finished_signal = Signal(bool, str)
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
from jackify.backend.services.modlist_gallery_service import ModlistGalleryService
|
||||
service = ModlistGalleryService()
|
||||
metadata = service.fetch_modlist_metadata(
|
||||
include_validation=False,
|
||||
include_search_index=True,
|
||||
sort_by="title",
|
||||
force_refresh=False
|
||||
)
|
||||
if metadata:
|
||||
modlists_with_mods = sum(1 for m in metadata.modlists if hasattr(m, 'mods') and m.mods)
|
||||
if modlists_with_mods > 0:
|
||||
_debug_print(f"Gallery cache ready ({modlists_with_mods} modlists with mods)")
|
||||
else:
|
||||
_debug_print("Gallery cache updated")
|
||||
else:
|
||||
_debug_print("Failed to load gallery cache")
|
||||
except Exception as e:
|
||||
_debug_print(f"Gallery cache preload error: {str(e)}")
|
||||
|
||||
self._gallery_cache_preload_thread = GalleryCachePreloadThread()
|
||||
self._gallery_cache_preload_thread.start()
|
||||
_debug_print("Started background gallery cache preload")
|
||||
|
||||
def _check_protontricks_on_startup(self):
|
||||
try:
|
||||
method = self.config_handler.get('component_installation_method', 'winetricks')
|
||||
if method != 'system_protontricks':
|
||||
_debug_print(f"Skipping protontricks check (current method: {method}).")
|
||||
return
|
||||
is_installed, installation_type, details = self.protontricks_service.detect_protontricks()
|
||||
if not is_installed:
|
||||
print(f"Protontricks not found: {details}")
|
||||
from jackify.frontends.gui.dialogs.protontricks_error_dialog import ProtontricksErrorDialog
|
||||
dialog = ProtontricksErrorDialog(self.protontricks_service, self)
|
||||
result = dialog.exec()
|
||||
if result == QDialog.Rejected:
|
||||
print("User chose to exit due to missing protontricks")
|
||||
sys.exit(1)
|
||||
else:
|
||||
_debug_print(f"Protontricks detected: {details}")
|
||||
except Exception as e:
|
||||
print(f"Error checking protontricks: {e}")
|
||||
|
||||
def _check_for_updates_on_startup(self):
|
||||
try:
|
||||
_debug_print("Checking for updates on startup...")
|
||||
|
||||
class UpdateCheckThread(QThread):
|
||||
update_available = Signal(object)
|
||||
|
||||
def __init__(self, update_service):
|
||||
super().__init__()
|
||||
self.update_service = update_service
|
||||
|
||||
def run(self):
|
||||
update_info = self.update_service.check_for_updates()
|
||||
if update_info:
|
||||
self.update_available.emit(update_info)
|
||||
|
||||
def on_update_available(update_info):
|
||||
_debug_print(f"Update available: v{update_info.version}")
|
||||
|
||||
def show_update_dialog():
|
||||
from jackify.frontends.gui.dialogs.update_dialog import UpdateDialog
|
||||
dialog = UpdateDialog(update_info, self.update_service, self)
|
||||
dialog.exec()
|
||||
QTimer.singleShot(1000, show_update_dialog)
|
||||
|
||||
self._update_thread = UpdateCheckThread(self.update_service)
|
||||
self._update_thread.update_available.connect(on_update_available)
|
||||
self._update_thread.start()
|
||||
except Exception as e:
|
||||
_debug_print(f"Error setting up update check: {e}")
|
||||
187
jackify/frontends/gui/mixins/main_window_ui.py
Normal file
187
jackify/frontends/gui/mixins/main_window_ui.py
Normal file
@@ -0,0 +1,187 @@
|
||||
"""
|
||||
Main window UI setup mixin.
|
||||
Stacked widget, screens, bottom bar, screen change handling.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QLabel, QVBoxLayout, QHBoxLayout,
|
||||
QStackedWidget, QSizePolicy,
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
from jackify import __version__
|
||||
from jackify.frontends.gui.shared_theme import DEBUG_BORDERS
|
||||
from jackify.frontends.gui.widgets.feature_placeholder import FeaturePlaceholder
|
||||
|
||||
|
||||
def _debug_print(message):
|
||||
from jackify.backend.handlers.config_handler import ConfigHandler
|
||||
ch = ConfigHandler()
|
||||
if ch.get('debug_mode', False):
|
||||
print(message)
|
||||
|
||||
|
||||
class MainWindowUIMixin:
|
||||
"""Mixin for main window UI: stacked widget, screens, bottom bar."""
|
||||
|
||||
def _setup_ui(self, dev_mode=False):
|
||||
self.stacked_widget = QStackedWidget()
|
||||
from jackify.frontends.gui.screens import (
|
||||
MainMenu, ModlistTasksScreen, AdditionalTasksScreen,
|
||||
InstallModlistScreen, ConfigureNewModlistScreen, ConfigureExistingModlistScreen,
|
||||
)
|
||||
from jackify.frontends.gui.screens.install_ttw import InstallTTWScreen
|
||||
from jackify.frontends.gui.screens.wabbajack_installer import WabbajackInstallerScreen
|
||||
|
||||
self.main_menu = MainMenu(stacked_widget=self.stacked_widget, dev_mode=dev_mode)
|
||||
self.feature_placeholder = FeaturePlaceholder(stacked_widget=self.stacked_widget)
|
||||
self.modlist_tasks_screen = ModlistTasksScreen(
|
||||
stacked_widget=self.stacked_widget, main_menu_index=0, dev_mode=dev_mode
|
||||
)
|
||||
self.additional_tasks_screen = AdditionalTasksScreen(
|
||||
stacked_widget=self.stacked_widget, main_menu_index=0, system_info=self.system_info
|
||||
)
|
||||
self.install_modlist_screen = InstallModlistScreen(
|
||||
stacked_widget=self.stacked_widget, main_menu_index=0, system_info=self.system_info
|
||||
)
|
||||
self.configure_new_modlist_screen = ConfigureNewModlistScreen(
|
||||
stacked_widget=self.stacked_widget, main_menu_index=0, system_info=self.system_info
|
||||
)
|
||||
self.configure_existing_modlist_screen = ConfigureExistingModlistScreen(
|
||||
stacked_widget=self.stacked_widget, main_menu_index=0, system_info=self.system_info
|
||||
)
|
||||
self.install_ttw_screen = InstallTTWScreen(
|
||||
stacked_widget=self.stacked_widget, main_menu_index=0, system_info=self.system_info
|
||||
)
|
||||
self.wabbajack_installer_screen = WabbajackInstallerScreen(
|
||||
stacked_widget=self.stacked_widget, additional_tasks_index=3, system_info=self.system_info
|
||||
)
|
||||
|
||||
try:
|
||||
self.install_ttw_screen.resize_request.connect(self._on_child_resize_request)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self.install_modlist_screen.resize_request.connect(self._on_child_resize_request)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self.configure_new_modlist_screen.resize_request.connect(self._on_child_resize_request)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self.configure_existing_modlist_screen.resize_request.connect(self._on_child_resize_request)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self.wabbajack_installer_screen.resize_request.connect(self._on_child_resize_request)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.stacked_widget.addWidget(self.main_menu)
|
||||
self.stacked_widget.addWidget(self.feature_placeholder)
|
||||
self.stacked_widget.addWidget(self.modlist_tasks_screen)
|
||||
self.stacked_widget.addWidget(self.additional_tasks_screen)
|
||||
self.stacked_widget.addWidget(self.install_modlist_screen)
|
||||
self.stacked_widget.addWidget(self.install_ttw_screen)
|
||||
self.stacked_widget.addWidget(self.configure_new_modlist_screen)
|
||||
self.stacked_widget.addWidget(self.wabbajack_installer_screen)
|
||||
self.stacked_widget.addWidget(self.configure_existing_modlist_screen)
|
||||
|
||||
self.stacked_widget.currentChanged.connect(self._debug_screen_change)
|
||||
self.stacked_widget.currentChanged.connect(self._maintain_fullscreen_on_deck)
|
||||
|
||||
bottom_bar = QWidget()
|
||||
bottom_bar_layout = QHBoxLayout()
|
||||
bottom_bar_layout.setContentsMargins(10, 2, 10, 2)
|
||||
bottom_bar_layout.setSpacing(0)
|
||||
bottom_bar.setLayout(bottom_bar_layout)
|
||||
bottom_bar.setFixedHeight(32)
|
||||
bottom_bar_style = "background-color: #181818; border-top: 1px solid #222;"
|
||||
if DEBUG_BORDERS:
|
||||
bottom_bar_style += " border: 2px solid lime;"
|
||||
bottom_bar.setStyleSheet(bottom_bar_style)
|
||||
|
||||
version_label = QLabel(f"Jackify v{__version__}")
|
||||
version_label.setStyleSheet("color: #bbb; font-size: 13px;")
|
||||
bottom_bar_layout.addWidget(version_label, alignment=Qt.AlignLeft)
|
||||
bottom_bar_layout.addStretch(1)
|
||||
kofi_link = QLabel('<a href="#" style="color:#72A5F2; text-decoration:none;">Support on Ko-fi</a>')
|
||||
kofi_link.setStyleSheet("color: #72A5F2; font-size: 13px;")
|
||||
kofi_link.setTextInteractionFlags(Qt.TextBrowserInteraction)
|
||||
kofi_link.setOpenExternalLinks(False)
|
||||
kofi_link.linkActivated.connect(lambda: self._open_url("https://ko-fi.com/omni1"))
|
||||
kofi_link.setToolTip("Support Jackify development")
|
||||
bottom_bar_layout.addWidget(kofi_link, alignment=Qt.AlignCenter)
|
||||
bottom_bar_layout.addStretch(1)
|
||||
settings_btn = QLabel('<a href="#" style="color:#6cf; text-decoration:none;">Settings</a>')
|
||||
settings_btn.setStyleSheet("color: #6cf; font-size: 13px; padding-right: 8px;")
|
||||
settings_btn.setTextInteractionFlags(Qt.TextBrowserInteraction)
|
||||
settings_btn.setOpenExternalLinks(False)
|
||||
settings_btn.linkActivated.connect(self.open_settings_dialog)
|
||||
bottom_bar_layout.addWidget(settings_btn, alignment=Qt.AlignRight)
|
||||
about_btn = QLabel('<a href="#" style="color:#6cf; text-decoration:none;">About</a>')
|
||||
about_btn.setStyleSheet("color: #6cf; font-size: 13px; padding-right: 8px;")
|
||||
about_btn.setTextInteractionFlags(Qt.TextBrowserInteraction)
|
||||
about_btn.setOpenExternalLinks(False)
|
||||
about_btn.linkActivated.connect(self.open_about_dialog)
|
||||
bottom_bar_layout.addWidget(about_btn, alignment=Qt.AlignRight)
|
||||
|
||||
central_widget = QWidget()
|
||||
main_layout = QVBoxLayout()
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
main_layout.addWidget(self.stacked_widget)
|
||||
main_layout.addWidget(bottom_bar)
|
||||
self.stacked_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
central_widget.setLayout(main_layout)
|
||||
self.setCentralWidget(central_widget)
|
||||
self.stacked_widget.setCurrentIndex(0)
|
||||
self._check_protontricks_on_startup()
|
||||
|
||||
def _debug_screen_change(self, index):
|
||||
try:
|
||||
idx = int(index) if index is not None else 0
|
||||
widget = self.stacked_widget.widget(idx)
|
||||
except (OverflowError, TypeError, ValueError):
|
||||
widget = self.stacked_widget.currentWidget()
|
||||
idx = None
|
||||
if widget and hasattr(widget, 'reset_screen_to_defaults'):
|
||||
widget.reset_screen_to_defaults()
|
||||
from jackify.backend.handlers.config_handler import ConfigHandler
|
||||
config_handler = ConfigHandler()
|
||||
if not config_handler.get('debug_mode', False):
|
||||
return
|
||||
if idx is None:
|
||||
return
|
||||
try:
|
||||
screen_names = {
|
||||
0: "Main Menu",
|
||||
1: "Feature Placeholder",
|
||||
2: "Modlist Tasks Menu",
|
||||
3: "Additional Tasks Menu",
|
||||
4: "Install Modlist Screen",
|
||||
5: "Install TTW Screen",
|
||||
6: "Configure New Modlist",
|
||||
7: "Wabbajack Installer",
|
||||
8: "Configure Existing Modlist",
|
||||
}
|
||||
screen_name = screen_names.get(idx, f"Unknown Screen (Index {idx})")
|
||||
widget = self.stacked_widget.widget(idx)
|
||||
except (OverflowError, TypeError, ValueError):
|
||||
return
|
||||
widget_class = widget.__class__.__name__ if widget else "None"
|
||||
print(f"[DEBUG] Screen changed to Index {idx}: {screen_name} (Widget: {widget_class})", file=sys.stderr)
|
||||
if idx == 4:
|
||||
print(" Install Modlist Screen details:", file=sys.stderr)
|
||||
print(f" - Widget type: {type(widget)}", file=sys.stderr)
|
||||
print(f" - Widget file: {widget.__class__.__module__}", file=sys.stderr)
|
||||
if hasattr(widget, 'windowTitle'):
|
||||
print(f" - Window title: {widget.windowTitle()}", file=sys.stderr)
|
||||
if hasattr(widget, 'layout'):
|
||||
layout = widget.layout()
|
||||
if layout:
|
||||
print(f" - Layout type: {type(layout)}", file=sys.stderr)
|
||||
print(f" - Layout children count: {layout.count()}", file=sys.stderr)
|
||||
Reference in New Issue
Block a user