diff --git a/CHANGELOG.md b/CHANGELOG.md index d026bb5..2353dae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Jackify Changelog +## v0.2.0.8 - Bug Fixes and Improvements +**Release Date:** 2025-12-29 + +### Bug Fixes +- Fixed Configure New/Existing/TTW screens missing Activity tab and progress updates +- Fixed cancel/back buttons crashing in Configure workflows + +### Improvements +- Install directory now auto-appends modlist name when selected from gallery + +### Known Issues +- Mod filter temporarily disabled in gallery due to technical issue (tag and game filters still work) + +--- + ## v0.2.0.7 - Critical Auth Fix **Release Date:** 2025-12-28 diff --git a/jackify/__init__.py b/jackify/__init__.py index c8e98d5..8d5011b 100644 --- a/jackify/__init__.py +++ b/jackify/__init__.py @@ -5,4 +5,4 @@ This package provides both CLI and GUI interfaces for managing Wabbajack modlists natively on Linux systems. """ -__version__ = "0.2.0.7" +__version__ = "0.2.0.8" diff --git a/jackify/frontends/gui/main.py b/jackify/frontends/gui/main.py index 8de945b..77089c8 100644 --- a/jackify/frontends/gui/main.py +++ b/jackify/frontends/gui/main.py @@ -1337,6 +1337,15 @@ class JackifyMainWindow(QMainWindow): self.install_modlist_screen.resize_request.connect(self._on_child_resize_request) except Exception: pass + # Let Configure screens request window resize for expand/collapse + 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 # Add screens to stacked widget self.stacked_widget.addWidget(self.main_menu) # Index 0: Main Menu diff --git a/jackify/frontends/gui/screens/configure_existing_modlist.py b/jackify/frontends/gui/screens/configure_existing_modlist.py index 1a31b4e..f1d7aa9 100644 --- a/jackify/frontends/gui/screens/configure_existing_modlist.py +++ b/jackify/frontends/gui/screens/configure_existing_modlist.py @@ -32,6 +32,7 @@ def debug_print(message): class ConfigureExistingModlistScreen(QWidget): steam_restart_finished = Signal(bool, str) + resize_request = Signal(str) def __init__(self, stacked_widget=None, main_menu_index=0): super().__init__() debug_print("DEBUG: ConfigureExistingModlistScreen __init__ called") @@ -220,27 +221,49 @@ class ConfigureExistingModlistScreen(QWidget): if self.debug: user_config_widget.setStyleSheet("border: 2px solid orange;") user_config_widget.setToolTip("USER_CONFIG_WIDGET") - # Right: Activity window (FileProgressList widget) - # Fixed size policy to prevent shrinking when window expands + # Right: Tabbed interface with Activity and Process Monitor + # Both tabs are always available, user can switch between them + self.process_monitor = QTextEdit() + self.process_monitor.setReadOnly(True) + self.process_monitor.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard) + self.process_monitor.setMinimumSize(QSize(300, 20)) + self.process_monitor.setStyleSheet(f"background: #222; color: {JACKIFY_COLOR_BLUE}; font-family: monospace; font-size: 11px; border: 1px solid #444;") + self.process_monitor_heading = QLabel("[Process Monitor]") + self.process_monitor_heading.setStyleSheet(f"color: {JACKIFY_COLOR_BLUE}; font-size: 13px; margin-bottom: 2px;") + self.process_monitor_heading.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + process_vbox = QVBoxLayout() + process_vbox.setContentsMargins(0, 0, 0, 0) + process_vbox.setSpacing(2) + process_vbox.addWidget(self.process_monitor_heading) + process_vbox.addWidget(self.process_monitor) + process_monitor_widget = QWidget() + process_monitor_widget.setLayout(process_vbox) + process_monitor_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + if self.debug: + process_monitor_widget.setStyleSheet("border: 2px solid purple;") + process_monitor_widget.setToolTip("PROCESS_MONITOR") + self.process_monitor_widget = process_monitor_widget + + # Set up File Progress List (Activity tab) self.file_progress_list.setMinimumSize(QSize(300, 20)) self.file_progress_list.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - activity_widget = QWidget() - activity_layout = QVBoxLayout() - activity_layout.setContentsMargins(0, 0, 0, 0) - activity_layout.setSpacing(0) - activity_layout.addWidget(self.file_progress_list) - activity_widget.setLayout(activity_layout) + # Create tab widget to hold both Activity and Process Monitor + self.activity_tabs = QTabWidget() + self.activity_tabs.setStyleSheet("QTabWidget::pane { background: #222; border: 1px solid #444; } QTabBar::tab { background: #222; color: #ccc; padding: 6px 16px; } QTabBar::tab:selected { background: #333; color: #3fd0ea; } QTabWidget { margin: 0px; padding: 0px; } QTabBar { margin: 0px; padding: 0px; }") + self.activity_tabs.setContentsMargins(0, 0, 0, 0) + self.activity_tabs.setDocumentMode(False) + self.activity_tabs.setTabPosition(QTabWidget.North) if self.debug: - activity_widget.setStyleSheet("border: 2px solid purple;") - activity_widget.setToolTip("ACTIVITY_WINDOW") - upper_hbox.addWidget(user_config_widget, stretch=11) - upper_hbox.addWidget(activity_widget, stretch=9) + self.activity_tabs.setStyleSheet("border: 2px solid cyan;") + self.activity_tabs.setToolTip("ACTIVITY_TABS") - # Keep legacy process monitor hidden (for compatibility with existing code) - self.process_monitor = QTextEdit() - self.process_monitor.setReadOnly(True) - self.process_monitor.setVisible(False) # Hidden in compact mode + # Add both widgets as tabs + self.activity_tabs.addTab(self.file_progress_list, "Activity") + self.activity_tabs.addTab(process_monitor_widget, "Process Monitor") + + upper_hbox.addWidget(user_config_widget, stretch=11) + upper_hbox.addWidget(self.activity_tabs, stretch=9) upper_hbox.setAlignment(Qt.AlignTop) upper_section_widget = QWidget() upper_section_widget.setLayout(upper_hbox) @@ -490,6 +513,42 @@ class ConfigureExistingModlistScreen(QWidget): except Exception: pass + def _handle_progress_update(self, text): + """Handle progress updates - update console, activity window, and progress indicator""" + # Always append to console + self._safe_append_text(text) + + # Parse the message to update UI widgets + message_lower = text.lower() + + # Update progress indicator based on key status messages + if "creating steam shortcut" in message_lower: + self.progress_indicator.set_status("Creating Steam shortcut...", 10) + elif "restarting steam" in message_lower or "restart steam" in message_lower: + self.progress_indicator.set_status("Restarting Steam...", 20) + elif "steam restart" in message_lower and "success" in message_lower: + self.progress_indicator.set_status("Steam restarted successfully", 30) + elif "creating proton prefix" in message_lower or "prefix creation" in message_lower: + self.progress_indicator.set_status("Creating Proton prefix...", 50) + elif "prefix created" in message_lower or "prefix creation" in message_lower and "success" in message_lower: + self.progress_indicator.set_status("Proton prefix created", 70) + elif "verifying" in message_lower: + self.progress_indicator.set_status("Verifying setup...", 80) + elif "steam integration complete" in message_lower or "configuration complete" in message_lower: + self.progress_indicator.set_status("Configuration complete", 95) + elif "complete" in message_lower and not "prefix" in message_lower: + self.progress_indicator.set_status("Finishing up...", 90) + + # Update activity window with generic configuration status + # Only update if message contains meaningful progress (not blank lines or separators) + if text.strip() and not text.strip().startswith('='): + # Show generic "Configuring modlist..." in activity window + self.file_progress_list.update_files( + [], + current_phase="Configuring", + summary_info={"current": 1, "total": 1, "label": "Setting up modlist"} + ) + def _safe_append_text(self, text): """Append text with professional auto-scroll behavior""" # Write all messages to log file @@ -660,7 +719,7 @@ class ConfigureExistingModlistScreen(QWidget): # Create and start the configuration thread self.config_thread = ConfigurationThread(modlist_name, install_dir, resolution) - self.config_thread.progress_update.connect(self._safe_append_text) + self.config_thread.progress_update.connect(self._handle_progress_update) self.config_thread.configuration_complete.connect(self.on_configuration_complete) self.config_thread.error_occurred.connect(self.on_configuration_error) self.config_thread.start() diff --git a/jackify/frontends/gui/screens/configure_new_modlist.py b/jackify/frontends/gui/screens/configure_new_modlist.py index b317603..f02c73d 100644 --- a/jackify/frontends/gui/screens/configure_new_modlist.py +++ b/jackify/frontends/gui/screens/configure_new_modlist.py @@ -98,6 +98,7 @@ class SelectionDialog(QDialog): class ConfigureNewModlistScreen(QWidget): steam_restart_finished = Signal(bool, str) + resize_request = Signal(str) def __init__(self, stacked_widget=None, main_menu_index=0): super().__init__() debug_print("DEBUG: ConfigureNewModlistScreen __init__ called") @@ -300,27 +301,49 @@ class ConfigureNewModlistScreen(QWidget): if self.debug: user_config_widget.setStyleSheet("border: 2px solid orange;") user_config_widget.setToolTip("USER_CONFIG_WIDGET") - # Right: Activity window (FileProgressList widget) - # Fixed size policy to prevent shrinking when window expands + # Right: Tabbed interface with Activity and Process Monitor + # Both tabs are always available, user can switch between them + self.process_monitor = QTextEdit() + self.process_monitor.setReadOnly(True) + self.process_monitor.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard) + self.process_monitor.setMinimumSize(QSize(300, 20)) + self.process_monitor.setStyleSheet(f"background: #222; color: {JACKIFY_COLOR_BLUE}; font-family: monospace; font-size: 11px; border: 1px solid #444;") + self.process_monitor_heading = QLabel("[Process Monitor]") + self.process_monitor_heading.setStyleSheet(f"color: {JACKIFY_COLOR_BLUE}; font-size: 13px; margin-bottom: 2px;") + self.process_monitor_heading.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + process_vbox = QVBoxLayout() + process_vbox.setContentsMargins(0, 0, 0, 0) + process_vbox.setSpacing(2) + process_vbox.addWidget(self.process_monitor_heading) + process_vbox.addWidget(self.process_monitor) + process_monitor_widget = QWidget() + process_monitor_widget.setLayout(process_vbox) + process_monitor_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + if self.debug: + process_monitor_widget.setStyleSheet("border: 2px solid purple;") + process_monitor_widget.setToolTip("PROCESS_MONITOR") + self.process_monitor_widget = process_monitor_widget + + # Set up File Progress List (Activity tab) self.file_progress_list.setMinimumSize(QSize(300, 20)) self.file_progress_list.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - activity_widget = QWidget() - activity_layout = QVBoxLayout() - activity_layout.setContentsMargins(0, 0, 0, 0) - activity_layout.setSpacing(0) - activity_layout.addWidget(self.file_progress_list) - activity_widget.setLayout(activity_layout) + # Create tab widget to hold both Activity and Process Monitor + self.activity_tabs = QTabWidget() + self.activity_tabs.setStyleSheet("QTabWidget::pane { background: #222; border: 1px solid #444; } QTabBar::tab { background: #222; color: #ccc; padding: 6px 16px; } QTabBar::tab:selected { background: #333; color: #3fd0ea; } QTabWidget { margin: 0px; padding: 0px; } QTabBar { margin: 0px; padding: 0px; }") + self.activity_tabs.setContentsMargins(0, 0, 0, 0) + self.activity_tabs.setDocumentMode(False) + self.activity_tabs.setTabPosition(QTabWidget.North) if self.debug: - activity_widget.setStyleSheet("border: 2px solid purple;") - activity_widget.setToolTip("ACTIVITY_WINDOW") - upper_hbox.addWidget(user_config_widget, stretch=11) - upper_hbox.addWidget(activity_widget, stretch=9) + self.activity_tabs.setStyleSheet("border: 2px solid cyan;") + self.activity_tabs.setToolTip("ACTIVITY_TABS") - # Keep legacy process monitor hidden (for compatibility with existing code) - self.process_monitor = QTextEdit() - self.process_monitor.setReadOnly(True) - self.process_monitor.setVisible(False) # Hidden in compact mode + # Add both widgets as tabs + self.activity_tabs.addTab(self.file_progress_list, "Activity") + self.activity_tabs.addTab(process_monitor_widget, "Process Monitor") + + upper_hbox.addWidget(user_config_widget, stretch=11) + upper_hbox.addWidget(self.activity_tabs, stretch=9) upper_hbox.setAlignment(Qt.AlignTop) upper_section_widget = QWidget() upper_section_widget.setLayout(upper_hbox) @@ -570,18 +593,54 @@ class ConfigureNewModlistScreen(QWidget): except Exception: pass + def _handle_progress_update(self, text): + """Handle progress updates - update console, activity window, and progress indicator""" + # Always append to console + self._safe_append_text(text) + + # Parse the message to update UI widgets + message_lower = text.lower() + + # Update progress indicator based on key status messages + if "creating steam shortcut" in message_lower: + self.progress_indicator.set_status("Creating Steam shortcut...", 10) + elif "restarting steam" in message_lower or "restart steam" in message_lower: + self.progress_indicator.set_status("Restarting Steam...", 20) + elif "steam restart" in message_lower and "success" in message_lower: + self.progress_indicator.set_status("Steam restarted successfully", 30) + elif "creating proton prefix" in message_lower or "prefix creation" in message_lower: + self.progress_indicator.set_status("Creating Proton prefix...", 50) + elif "prefix created" in message_lower or "prefix creation" in message_lower and "success" in message_lower: + self.progress_indicator.set_status("Proton prefix created", 70) + elif "verifying" in message_lower: + self.progress_indicator.set_status("Verifying setup...", 80) + elif "steam integration complete" in message_lower or "configuration complete" in message_lower: + self.progress_indicator.set_status("Configuration complete", 95) + elif "complete" in message_lower and not "prefix" in message_lower: + self.progress_indicator.set_status("Finishing up...", 90) + + # Update activity window with generic configuration status + # Only update if message contains meaningful progress (not blank lines or separators) + if text.strip() and not text.strip().startswith('='): + # Show generic "Configuring modlist..." in activity window + self.file_progress_list.update_files( + [], + current_phase="Configuring", + summary_info={"current": 1, "total": 1, "label": "Setting up modlist"} + ) + def _safe_append_text(self, text): """Append text with professional auto-scroll behavior""" # Write all messages to log file self._write_to_log_file(text) - + scrollbar = self.console.verticalScrollBar() # Check if user was at bottom BEFORE adding text was_at_bottom = (scrollbar.value() >= scrollbar.maximum() - 1) # Allow 1px tolerance - + # Add the text self.console.append(text) - + # Auto-scroll if user was at bottom and hasn't manually scrolled # Re-check bottom state after text addition for better reliability if (was_at_bottom and not self._user_manually_scrolled) or \ @@ -635,6 +694,11 @@ class ConfigureNewModlistScreen(QWidget): if hasattr(self, 'file_progress_list'): self.file_progress_list.stop_cpu_tracking() + # Clean up automated prefix thread if running + if hasattr(self, 'automated_prefix_thread') and self.automated_prefix_thread.isRunning(): + self.automated_prefix_thread.terminate() + self.automated_prefix_thread.wait(1000) + # Clean up configuration thread if running if hasattr(self, 'config_thread') and self.config_thread.isRunning(): self.config_thread.terminate() @@ -929,7 +993,7 @@ class ConfigureNewModlistScreen(QWidget): # Create and start the thread self.automated_prefix_thread = AutomatedPrefixThread(modlist_name, install_dir, mo2_exe_path, _is_steamdeck) - self.automated_prefix_thread.progress_update.connect(self._safe_append_text) + self.automated_prefix_thread.progress_update.connect(self._handle_progress_update) self.automated_prefix_thread.workflow_complete.connect(self._on_automated_prefix_complete) self.automated_prefix_thread.error_occurred.connect(self._on_automated_prefix_error) self.automated_prefix_thread.start() @@ -1323,7 +1387,7 @@ class ConfigureNewModlistScreen(QWidget): # Start configuration thread self.config_thread = ConfigThread(updated_context) - self.config_thread.progress_update.connect(self._safe_append_text) + self.config_thread.progress_update.connect(self._handle_progress_update) self.config_thread.configuration_complete.connect(self.on_configuration_complete) self.config_thread.error_occurred.connect(self.on_configuration_error) self.config_thread.start() @@ -1424,7 +1488,7 @@ class ConfigureNewModlistScreen(QWidget): # Create and start the configuration thread self.config_thread = ConfigThread(updated_context) - self.config_thread.progress_update.connect(self._safe_append_text) + self.config_thread.progress_update.connect(self._handle_progress_update) self.config_thread.configuration_complete.connect(self.on_configuration_complete) self.config_thread.error_occurred.connect(self.on_configuration_error) self.config_thread.start() diff --git a/jackify/frontends/gui/screens/install_modlist.py b/jackify/frontends/gui/screens/install_modlist.py index 1bc858a..6b71bd6 100644 --- a/jackify/frontends/gui/screens/install_modlist.py +++ b/jackify/frontends/gui/screens/install_modlist.py @@ -1517,6 +1517,16 @@ class InstallModlistScreen(QWidget): 'force_down': metadata.forceDown } self.modlist_name_edit.setText(metadata.title) + + # Auto-append modlist name to install directory + base_install_dir = self.config_handler.get_modlist_install_base_dir() + if base_install_dir: + # Sanitize modlist title for filesystem use + import re + safe_title = re.sub(r'[<>:"/\\|?*]', '', metadata.title) + safe_title = safe_title.strip() + modlist_install_path = os.path.join(base_install_dir, safe_title) + self.install_dir_edit.setText(modlist_install_path) finally: if cursor_overridden: QApplication.restoreOverrideCursor() @@ -3953,11 +3963,11 @@ class InstallModlistScreen(QWidget): self.retry_automated_workflow_with_new_name(new_name) elif new_name == modlist_name: # Same name - show warning - from jackify.backend.services.message_service import MessageService + from jackify.frontends.gui.services.message_service import MessageService MessageService.warning(self, "Same Name", "Please enter a different name to resolve the conflict.") else: # Empty name - from jackify.backend.services.message_service import MessageService + from jackify.frontends.gui.services.message_service import MessageService MessageService.warning(self, "Invalid Name", "Please enter a valid shortcut name.") def on_cancel(): diff --git a/jackify/frontends/gui/screens/install_ttw.py b/jackify/frontends/gui/screens/install_ttw.py index 0a3b12a..40bc4f4 100644 --- a/jackify/frontends/gui/screens/install_ttw.py +++ b/jackify/frontends/gui/screens/install_ttw.py @@ -306,29 +306,49 @@ class InstallTTWScreen(QWidget): user_config_widget.setStyleSheet("border: 2px solid orange;") user_config_widget.setToolTip("USER_CONFIG_WIDGET") - # Right: Activity window (FileProgressList widget) - # Fixed size policy to prevent shrinking when window expands + # Right: Tabbed interface with Activity and Process Monitor + # Both tabs are always available, user can switch between them self.file_progress_list = FileProgressList() self.file_progress_list.setMinimumSize(QSize(300, 20)) self.file_progress_list.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - activity_widget = QWidget() - activity_layout = QVBoxLayout() - activity_layout.setContentsMargins(0, 0, 0, 0) - activity_layout.setSpacing(0) - activity_layout.addWidget(self.file_progress_list) - activity_widget.setLayout(activity_layout) - if self.debug: - activity_widget.setStyleSheet("border: 2px solid purple;") - activity_widget.setToolTip("ACTIVITY_WINDOW") - - upper_hbox.addWidget(user_config_widget, stretch=11) - upper_hbox.addWidget(activity_widget, stretch=9) - - # Keep legacy process monitor hidden (for compatibility with existing code) self.process_monitor = QTextEdit() self.process_monitor.setReadOnly(True) - self.process_monitor.setVisible(False) + self.process_monitor.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard) + self.process_monitor.setMinimumSize(QSize(300, 20)) + self.process_monitor.setStyleSheet(f"background: #222; color: {JACKIFY_COLOR_BLUE}; font-family: monospace; font-size: 11px; border: 1px solid #444;") + self.process_monitor_heading = QLabel("[Process Monitor]") + self.process_monitor_heading.setStyleSheet(f"color: {JACKIFY_COLOR_BLUE}; font-size: 13px; margin-bottom: 2px;") + self.process_monitor_heading.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + process_vbox = QVBoxLayout() + process_vbox.setContentsMargins(0, 0, 0, 0) + process_vbox.setSpacing(2) + process_vbox.addWidget(self.process_monitor_heading) + process_vbox.addWidget(self.process_monitor) + process_monitor_widget = QWidget() + process_monitor_widget.setLayout(process_vbox) + process_monitor_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + if self.debug: + process_monitor_widget.setStyleSheet("border: 2px solid purple;") + process_monitor_widget.setToolTip("PROCESS_MONITOR") + self.process_monitor_widget = process_monitor_widget + + # Create tab widget to hold both Activity and Process Monitor + self.activity_tabs = QTabWidget() + self.activity_tabs.setStyleSheet("QTabWidget::pane { background: #222; border: 1px solid #444; } QTabBar::tab { background: #222; color: #ccc; padding: 6px 16px; } QTabBar::tab:selected { background: #333; color: #3fd0ea; } QTabWidget { margin: 0px; padding: 0px; } QTabBar { margin: 0px; padding: 0px; }") + self.activity_tabs.setContentsMargins(0, 0, 0, 0) + self.activity_tabs.setDocumentMode(False) + self.activity_tabs.setTabPosition(QTabWidget.North) + if self.debug: + self.activity_tabs.setStyleSheet("border: 2px solid cyan;") + self.activity_tabs.setToolTip("ACTIVITY_TABS") + + # Add both widgets as tabs + self.activity_tabs.addTab(self.file_progress_list, "Activity") + self.activity_tabs.addTab(process_monitor_widget, "Process Monitor") + + upper_hbox.addWidget(user_config_widget, stretch=11) + upper_hbox.addWidget(self.activity_tabs, stretch=9) upper_hbox.setAlignment(Qt.AlignTop) self.upper_section_widget = QWidget() self.upper_section_widget.setLayout(upper_hbox) @@ -2815,11 +2835,11 @@ class InstallTTWScreen(QWidget): self.retry_automated_workflow_with_new_name(new_name) elif new_name == modlist_name: # Same name - show warning - from jackify.backend.services.message_service import MessageService + from jackify.frontends.gui.services.message_service import MessageService MessageService.warning(self, "Same Name", "Please enter a different name to resolve the conflict.") else: # Empty name - from jackify.backend.services.message_service import MessageService + from jackify.frontends.gui.services.message_service import MessageService MessageService.warning(self, "Invalid Name", "Please enter a valid shortcut name.") def on_cancel(): diff --git a/jackify/frontends/gui/screens/modlist_gallery.py b/jackify/frontends/gui/screens/modlist_gallery.py index 7901446..0b74cc9 100644 --- a/jackify/frontends/gui/screens/modlist_gallery.py +++ b/jackify/frontends/gui/screens/modlist_gallery.py @@ -933,27 +933,28 @@ class ModlistGalleryDialog(QDialog): # Add spacing between Tags and Mods sections layout.addSpacing(8) - # Mod filter - mods_label = QLabel("Mods:") - layout.addWidget(mods_label) - - self.mod_search = QLineEdit() - self.mod_search.setPlaceholderText("Search mods...") - self.mod_search.setStyleSheet("QLineEdit { background: #2a2a2a; color: #fff; border: 1px solid #555; padding: 4px; }") - self.mod_search.textChanged.connect(self._filter_mods_list) - # Prevent Enter from triggering default button (which would close dialog) - self.mod_search.returnPressed.connect(lambda: self.mod_search.clearFocus()) - layout.addWidget(self.mod_search) - - self.mods_list = QListWidget() - self.mods_list.setSelectionMode(QListWidget.MultiSelection) - self.mods_list.setMaximumHeight(150) - self.mods_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # Remove horizontal scrollbar - self.mods_list.setStyleSheet("QListWidget { background: #2a2a2a; color: #fff; border: 1px solid #555; }") - self.mods_list.itemSelectionChanged.connect(self._apply_filters) - layout.addWidget(self.mods_list) - - self.all_mods_list = [] # Store all mods for filtering + # Mod filter - TEMPORARILY DISABLED (not working correctly in v0.2.0.8) + # TODO: Re-enable once mod search index issue is resolved + # mods_label = QLabel("Mods:") + # layout.addWidget(mods_label) + # + # self.mod_search = QLineEdit() + # self.mod_search.setPlaceholderText("Search mods...") + # self.mod_search.setStyleSheet("QLineEdit { background: #2a2a2a; color: #fff; border: 1px solid #555; padding: 4px; }") + # self.mod_search.textChanged.connect(self._filter_mods_list) + # # Prevent Enter from triggering default button (which would close dialog) + # self.mod_search.returnPressed.connect(lambda: self.mod_search.clearFocus()) + # layout.addWidget(self.mod_search) + # + # self.mods_list = QListWidget() + # self.mods_list.setSelectionMode(QListWidget.MultiSelection) + # self.mods_list.setMaximumHeight(150) + # self.mods_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # Remove horizontal scrollbar + # self.mods_list.setStyleSheet("QListWidget { background: #2a2a2a; color: #fff; border: 1px solid #555; }") + # self.mods_list.itemSelectionChanged.connect(self._apply_filters) + # layout.addWidget(self.mods_list) + # + # self.all_mods_list = [] # Store all mods for filtering layout.addStretch() @@ -1156,9 +1157,9 @@ class ModlistGalleryDialog(QDialog): if index >= 0: self.game_combo.setCurrentIndex(index) - # Populate tag and mod filters + # Populate tag filter (mod filter temporarily disabled) self._populate_tag_filter() - self._populate_mod_filter() + # self._populate_mod_filter() # TEMPORARILY DISABLED # Create cards immediately (will show placeholders for images not in cache) self._create_all_cards() @@ -1228,9 +1229,9 @@ class ModlistGalleryDialog(QDialog): if index >= 0: self.game_combo.setCurrentIndex(index) - # Populate tag and mod filters + # Populate tag filter (mod filter temporarily disabled) self._populate_tag_filter() - self._populate_mod_filter() + # self._populate_mod_filter() # TEMPORARILY DISABLED # Create cards immediately (will show placeholders for images not in cache) self._create_all_cards() @@ -1340,58 +1341,64 @@ class ModlistGalleryDialog(QDialog): def _populate_mod_filter(self): """Populate mod filter with all available mods from search index""" - all_mods = set() - # Track which mods come from NSFW modlists only - mods_from_nsfw_only = set() - mods_from_sfw = set() - modlists_with_mods = 0 - - for modlist in self.all_modlists: - if hasattr(modlist, 'mods') and modlist.mods: - modlists_with_mods += 1 - for mod in modlist.mods: - all_mods.add(mod) - if modlist.nsfw: - mods_from_nsfw_only.add(mod) - else: - mods_from_sfw.add(mod) - - # Mods that are ONLY in NSFW modlists (not in any SFW modlists) - self.nsfw_only_mods = mods_from_nsfw_only - mods_from_sfw - - self.all_mods_list = sorted(all_mods) - - self._filter_mods_list("") # Populate with all mods initially + # TEMPORARILY DISABLED - mod filter feature removed in v0.2.0.8 + return + + # all_mods = set() + # # Track which mods come from NSFW modlists only + # mods_from_nsfw_only = set() + # mods_from_sfw = set() + # modlists_with_mods = 0 + # + # for modlist in self.all_modlists: + # if hasattr(modlist, 'mods') and modlist.mods: + # modlists_with_mods += 1 + # for mod in modlist.mods: + # all_mods.add(mod) + # if modlist.nsfw: + # mods_from_nsfw_only.add(mod) + # else: + # mods_from_sfw.add(mod) + # + # # Mods that are ONLY in NSFW modlists (not in any SFW modlists) + # self.nsfw_only_mods = mods_from_nsfw_only - mods_from_sfw + # + # self.all_mods_list = sorted(all_mods) + # + # self._filter_mods_list("") # Populate with all mods initially def _filter_mods_list(self, search_text: str = ""): """Filter the mods list based on search text and NSFW checkbox""" + # TEMPORARILY DISABLED - mod filter feature removed in v0.2.0.8 + return + # Get search text from the widget if not provided - if not search_text and hasattr(self, 'mod_search'): - search_text = self.mod_search.text() - - self.mods_list.clear() - search_lower = search_text.lower().strip() - - # Start with all mods or filtered by search - if search_lower: - filtered_mods = [m for m in self.all_mods_list if search_lower in m.lower()] - else: - filtered_mods = self.all_mods_list - - # Filter out NSFW-only mods if NSFW checkbox is not checked - if not self.show_nsfw.isChecked(): - filtered_mods = [m for m in filtered_mods if m not in getattr(self, 'nsfw_only_mods', set())] - - # Limit to first 500 results for performance - for mod in filtered_mods[:500]: - self.mods_list.addItem(mod) - - if len(filtered_mods) > 500: - self.mods_list.addItem(f"... and {len(filtered_mods) - 500} more (refine search)") + # if not search_text and hasattr(self, 'mod_search'): + # search_text = self.mod_search.text() + # + # self.mods_list.clear() + # search_lower = search_text.lower().strip() + # + # # Start with all mods or filtered by search + # if search_lower: + # filtered_mods = [m for m in self.all_mods_list if search_lower in m.lower()] + # else: + # filtered_mods = self.all_mods_list + # + # # Filter out NSFW-only mods if NSFW checkbox is not checked + # if not self.show_nsfw.isChecked(): + # filtered_mods = [m for m in filtered_mods if m not in getattr(self, 'nsfw_only_mods', set())] + # + # # Limit to first 500 results for performance + # for mod in filtered_mods[:500]: + # self.mods_list.addItem(mod) + # + # if len(filtered_mods) > 500: + # self.mods_list.addItem(f"... and {len(filtered_mods) - 500} more (refine search)") def _on_nsfw_toggled(self, checked: bool): """Handle NSFW checkbox toggle - refresh mod list and apply filters""" - self._filter_mods_list() # Refresh mod list based on NSFW state + # self._filter_mods_list() # TEMPORARILY DISABLED - Refresh mod list based on NSFW state self._apply_filters() # Apply all filters def _set_filter_controls_enabled(self, enabled: bool): @@ -1402,8 +1409,8 @@ class ModlistGalleryDialog(QDialog): self.show_nsfw.setEnabled(enabled) self.hide_unavailable.setEnabled(enabled) self.tags_list.setEnabled(enabled) - self.mod_search.setEnabled(enabled) - self.mods_list.setEnabled(enabled) + # self.mod_search.setEnabled(enabled) # TEMPORARILY DISABLED + # self.mods_list.setEnabled(enabled) # TEMPORARILY DISABLED def _apply_filters(self): """Apply current filters to modlist display""" @@ -1459,10 +1466,10 @@ class ModlistGalleryDialog(QDialog): ) ] - # Mod filter - modlist must contain ALL selected mods - selected_mods = [item.text() for item in self.mods_list.selectedItems()] - if selected_mods: - filtered = [m for m in filtered if m.mods and all(mod in m.mods for mod in selected_mods)] + # Mod filter - TEMPORARILY DISABLED (not working correctly in v0.2.0.8) + # selected_mods = [item.text() for item in self.mods_list.selectedItems()] + # if selected_mods: + # filtered = [m for m in filtered if m.mods and all(mod in m.mods for mod in selected_mods)] self.filtered_modlists = filtered self._update_grid()