From 427ed128baf32ac3b2fd5bd725e221d39748efb0 Mon Sep 17 00:00:00 2001 From: Demohstens Date: Sun, 30 Nov 2025 15:32:40 +0100 Subject: [PATCH 1/5] refactor: moved search bar to own file in /views/widgets/ --- src/tagstudio/qt/ts_qt.py | 26 +++---- src/tagstudio/qt/views/main_window.py | 55 ++------------ .../qt/views/widgets/search_bar_widget.py | 74 +++++++++++++++++++ 3 files changed, 93 insertions(+), 62 deletions(-) create mode 100644 src/tagstudio/qt/views/widgets/search_bar_widget.py diff --git a/src/tagstudio/qt/ts_qt.py b/src/tagstudio/qt/ts_qt.py index 34dec16fa..0506a4793 100644 --- a/src/tagstudio/qt/ts_qt.py +++ b/src/tagstudio/qt/ts_qt.py @@ -561,7 +561,7 @@ def create_about_modal(): # endregion - self.main_window.search_field.textChanged.connect(self.update_completions_list) + self.main_window.search_bar.search_field.textChanged.connect(self.update_completions_list) self.archived_updated.connect( lambda hidden: self.update_badges( @@ -624,7 +624,7 @@ def init_library_window(self): def _update_browsing_state(): try: self.update_browsing_state( - BrowsingState.from_search_query(self.main_window.search_field.text()) + BrowsingState.from_search_query(self.main_window.search_bar.search_field.text()) .with_sorting_mode(self.main_window.sorting_mode) .with_sorting_direction(self.main_window.sorting_direction) .with_show_hidden_entries(self.main_window.show_hidden_entries) @@ -632,15 +632,15 @@ def _update_browsing_state(): except ParsingError as e: self.main_window.status_bar.showMessage( f"{Translations['status.results.invalid_syntax']} " - f'"{self.main_window.search_field.text()}"' + f'"{self.main_window.search_bar.search_field.text()}"' ) logger.error("[QtDriver] Could not update BrowsingState", error=e) # Search Button - self.main_window.search_button.clicked.connect(_update_browsing_state) + self.main_window.search_bar.search_button.clicked.connect(_update_browsing_state) # Search Field - self.main_window.search_field.returnPressed.connect(_update_browsing_state) + self.main_window.search_bar.search_field.returnPressed.connect(_update_browsing_state) # Sorting Dropdowns self.main_window.sorting_mode_combobox.setCurrentIndex( @@ -666,8 +666,8 @@ def _update_browsing_state(): self.show_hidden_entries_callback ) - self.main_window.back_button.clicked.connect(lambda: self.navigation_callback(-1)) - self.main_window.forward_button.clicked.connect(lambda: self.navigation_callback(1)) + self.main_window.search_bar.back_button.clicked.connect(lambda: self.navigation_callback(-1)) + self.main_window.search_bar.forward_button.clicked.connect(lambda: self.navigation_callback(1)) # NOTE: Putting this early will result in a white non-responsive # window until everything is loaded. Consider adding a splash screen @@ -738,7 +738,7 @@ def close_library(self, is_shutdown: bool = False): # Reset library state self.main_window.preview_panel.set_selection(self.selected) - self.main_window.search_field.setText("") + self.main_window.search_bar.search_field.setText("") scrollbar: QScrollArea = self.main_window.entry_scroll_area scrollbar.verticalScrollBar().setValue(0) self.__reset_navigation() @@ -1326,7 +1326,7 @@ def update_completions_list(self, text: str) -> None: "tag_id:", "special:untagged", ] - self.main_window.search_field_completion_list.setStringList(completion_list) + self.main_window.search_bar.search_field_completion_list.setStringList(completion_list) if not matches: return @@ -1375,11 +1375,11 @@ def update_completions_list(self, text: str) -> None: ) update_completion_list: bool = ( - completion_list != self.main_window.search_field_completion_list.stringList() - or self.main_window.search_field_completion_list == [] + completion_list != self.main_window.search_bar.search_field_completion_list.stringList() + or self.main_window.search_bar.search_field_completion_list == [] ) if update_completion_list: - self.main_window.search_field_completion_list.setStringList(completion_list) + self.main_window.search_bar.search_field_completion_list.setStringList(completion_list) def update_thumbs(self): """Update search thumbnails.""" @@ -1450,7 +1450,7 @@ def update_browsing_state(self, state: BrowsingState | None = None) -> None: if state: self.browsing_history.push(state) - self.main_window.search_field.setText(self.browsing_history.current.query or "") + self.main_window.search_bar.search_field.setText(self.browsing_history.current.query or "") # inform user about running search self.main_window.status_bar.showMessage(Translations["status.library_search_query"]) diff --git a/src/tagstudio/qt/views/main_window.py b/src/tagstudio/qt/views/main_window.py index a4c0485a5..9868d91c6 100644 --- a/src/tagstudio/qt/views/main_window.py +++ b/src/tagstudio/qt/views/main_window.py @@ -42,6 +42,7 @@ from tagstudio.qt.mixed.landing import LandingWidget from tagstudio.qt.mixed.pagination import Pagination from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color +from tagstudio.qt.views.widgets.search_bar_widget import SearchBarWidget from tagstudio.qt.mnemonics import assign_mnemonics from tagstudio.qt.models.palette import ColorType, get_tag_color from tagstudio.qt.platform_strings import trash_term @@ -457,13 +458,7 @@ def __init__(self, driver: "QtDriver", parent: QWidget | None = None) -> None: # region Type declarations for variables that will be initialized in methods # initialized in setup_search_bar - self.search_bar_layout: QHBoxLayout - self.back_button: QPushButton - self.forward_button: QPushButton - self.search_field: QLineEdit - self.search_field_completion_list: QStringListModel - self.search_field_completer: QCompleter - self.search_button: QPushButton + self.search_bar: SearchBarWidget # initialized in setup_extra_input_bar self.extra_input_layout: QHBoxLayout @@ -534,48 +529,10 @@ def setup_central_widget(self, driver: "QtDriver"): self.setCentralWidget(self.central_widget) def setup_search_bar(self): - """Sets up Nav Buttons, Search Field, Search Button.""" - self.search_bar_layout = QHBoxLayout() - self.search_bar_layout.setObjectName("search_bar_layout") - self.search_bar_layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize) - - self.back_button = QPushButton(self.central_widget) - back_icon: Image.Image = self.rm.get("bxs-left-arrow") # pyright: ignore[reportAssignmentType] - back_icon = theme_fg_overlay(back_icon, use_alpha=False) - self.back_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(back_icon))) - self.back_button.setObjectName("back_button") - self.back_button.setMinimumSize(QSize(32, 32)) - self.back_button.setMaximumSize(QSize(32, 16777215)) - self.search_bar_layout.addWidget(self.back_button) - - self.forward_button = QPushButton(self.central_widget) - forward_icon: Image.Image = self.rm.get("bxs-right-arrow") # pyright: ignore[reportAssignmentType] - forward_icon = theme_fg_overlay(forward_icon, use_alpha=False) - self.forward_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(forward_icon))) - self.forward_button.setIconSize(QSize(16, 16)) - self.forward_button.setObjectName("forward_button") - self.forward_button.setMinimumSize(QSize(32, 32)) - self.forward_button.setMaximumSize(QSize(32, 16777215)) - self.search_bar_layout.addWidget(self.forward_button) - - self.search_field = QLineEdit(self.central_widget) - self.search_field.setPlaceholderText(Translations["home.search_entries"]) - self.search_field.setObjectName("search_field") - self.search_field.setMinimumSize(QSize(0, 32)) - self.search_field_completion_list = QStringListModel() - self.search_field_completer = QCompleter( - self.search_field_completion_list, self.search_field - ) - self.search_field_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) - self.search_field.setCompleter(self.search_field_completer) - self.search_bar_layout.addWidget(self.search_field) - - self.search_button = QPushButton(Translations["home.search"], self.central_widget) - self.search_button.setObjectName("search_button") - self.search_button.setMinimumSize(QSize(0, 32)) - self.search_bar_layout.addWidget(self.search_button) - - self.central_layout.addLayout(self.search_bar_layout, 3, 0, 1, 1) + self.search_bar = SearchBarWidget(self.central_widget) + # Restrict the size, as otherwise the widget will expand vertically. + self.search_bar.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) + self.central_layout.addWidget(self.search_bar, 3, 0, 1, 1) def setup_extra_input_bar(self): """Sets up inputs for sorting settings and thumbnail size.""" diff --git a/src/tagstudio/qt/views/widgets/search_bar_widget.py b/src/tagstudio/qt/views/widgets/search_bar_widget.py new file mode 100644 index 000000000..1a4e6e9af --- /dev/null +++ b/src/tagstudio/qt/views/widgets/search_bar_widget.py @@ -0,0 +1,74 @@ +import typing + +from PIL import Image, ImageQt + +from PIL import Image, ImageQt +from PySide6 import QtCore +from PySide6.QtCore import QMetaObject, QSize, QStringListModel, Qt, QEvent +from PySide6.QtGui import QAction, QColor, QPixmap +from PySide6.QtWidgets import ( + QWidget, + QHBoxLayout, + QLayout, + QPushButton, + QCompleter, + QHBoxLayout, + QLayout, + QLineEdit, + QPushButton, + QWidget,) + + +from tagstudio.qt.resource_manager import ResourceManager +from tagstudio.qt.translations import Translations +from tagstudio.qt.helpers.color_overlay import theme_fg_overlay + +class SearchBarWidget(QWidget): + back_button: QPushButton + resource_manager: ResourceManager + + def __init__(self, parent: QWidget): + super().__init__(parent) + self.resource_manager = ResourceManager() + + layout = QHBoxLayout() + layout.setObjectName("search_bar_layout") + layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize) + + self.back_button = QPushButton(self) + back_icon: Image.Image = self.resource_manager.get("bxs-left-arrow") # pyright: ignore[reportAssignmentType] + back_icon = theme_fg_overlay(back_icon, use_alpha=False) + self.back_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(back_icon))) + self.back_button.setObjectName("back_button") + self.back_button.setMinimumSize(QSize(32, 32)) + self.back_button.setMaximumSize(QSize(32, 16777215)) + layout.addWidget(self.back_button) + + self.forward_button = QPushButton(self) + forward_icon: Image.Image = self.resource_manager.get("bxs-right-arrow") # pyright: ignore[reportAssignmentType] + forward_icon = theme_fg_overlay(forward_icon, use_alpha=False) + self.forward_button.setIcon(QPixmap.fromImage(ImageQt.ImageQt(forward_icon))) + self.forward_button.setIconSize(QSize(16, 16)) + self.forward_button.setObjectName("forward_button") + self.forward_button.setMinimumSize(QSize(32, 32)) + self.forward_button.setMaximumSize(QSize(32, 16777215)) + layout.addWidget(self.forward_button) + + self.search_field = QLineEdit(self) + self.search_field.setPlaceholderText(Translations["home.search_entries"]) + self.search_field.setObjectName("search_field") + self.search_field.setMinimumSize(QSize(0, 32)) + self.search_field_completion_list = QStringListModel() + self.search_field_completer = QCompleter( + self.search_field_completion_list, self.search_field + ) + self.search_field_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) + self.search_field.setCompleter(self.search_field_completer) + layout.addWidget(self.search_field) + + self.search_button = QPushButton(Translations["home.search"], self) + self.search_button.setObjectName("search_button") + self.search_button.setMinimumSize(QSize(0, 32)) + layout.addWidget(self.search_button) + + self.setLayout(layout) From c80cea5f346fc43377e48c60c6b2a29ec213de83 Mon Sep 17 00:00:00 2001 From: Demohstens Date: Sat, 6 Dec 2025 15:05:38 +0100 Subject: [PATCH 2/5] refactor: renamed widget -> view --- src/tagstudio/qt/views/main_window.py | 2 +- .../views/widgets/{search_bar_widget.py => search_bar_view.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/tagstudio/qt/views/widgets/{search_bar_widget.py => search_bar_view.py} (100%) diff --git a/src/tagstudio/qt/views/main_window.py b/src/tagstudio/qt/views/main_window.py index 9868d91c6..599c4ee10 100644 --- a/src/tagstudio/qt/views/main_window.py +++ b/src/tagstudio/qt/views/main_window.py @@ -42,7 +42,7 @@ from tagstudio.qt.mixed.landing import LandingWidget from tagstudio.qt.mixed.pagination import Pagination from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color -from tagstudio.qt.views.widgets.search_bar_widget import SearchBarWidget +from tagstudio.qt.views.widgets.search_bar_view import SearchBarWidget from tagstudio.qt.mnemonics import assign_mnemonics from tagstudio.qt.models.palette import ColorType, get_tag_color from tagstudio.qt.platform_strings import trash_term diff --git a/src/tagstudio/qt/views/widgets/search_bar_widget.py b/src/tagstudio/qt/views/widgets/search_bar_view.py similarity index 100% rename from src/tagstudio/qt/views/widgets/search_bar_widget.py rename to src/tagstudio/qt/views/widgets/search_bar_view.py From 57632f5802bb0d75739069f6e75e9c9b98aef387 Mon Sep 17 00:00:00 2001 From: Demohstens Date: Sat, 6 Dec 2025 15:09:09 +0100 Subject: [PATCH 3/5] refactor: Moved MainMenuBar --- src/tagstudio/qt/views/main_window.py | 404 +----------------- .../qt/views/widgets/main_menu_bar_view.py | 402 +++++++++++++++++ 2 files changed, 405 insertions(+), 401 deletions(-) create mode 100644 src/tagstudio/qt/views/widgets/main_menu_bar_view.py diff --git a/src/tagstudio/qt/views/main_window.py b/src/tagstudio/qt/views/main_window.py index 599c4ee10..3ac53b4b2 100644 --- a/src/tagstudio/qt/views/main_window.py +++ b/src/tagstudio/qt/views/main_window.py @@ -4,28 +4,18 @@ import typing -from collections.abc import Callable -from pathlib import Path import structlog -from PIL import Image, ImageQt -from PySide6 import QtCore -from PySide6.QtCore import QMetaObject, QSize, QStringListModel, Qt -from PySide6.QtGui import QAction, QColor, QPixmap +from PySide6.QtCore import QMetaObject, Qt +from PySide6.QtGui import QColor from PySide6.QtWidgets import ( QCheckBox, QComboBox, - QCompleter, QFrame, QGridLayout, QHBoxLayout, QLabel, - QLayout, - QLineEdit, QMainWindow, - QMenu, - QMenuBar, - QPushButton, QScrollArea, QSizePolicy, QSpacerItem, @@ -35,20 +25,17 @@ QWidget, ) -from tagstudio.core.enums import ShowFilepathOption from tagstudio.core.library.alchemy.enums import SortingModeEnum, TagColorEnum from tagstudio.qt.controllers.preview_panel_controller import PreviewPanel -from tagstudio.qt.helpers.color_overlay import theme_fg_overlay from tagstudio.qt.mixed.landing import LandingWidget from tagstudio.qt.mixed.pagination import Pagination from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color from tagstudio.qt.views.widgets.search_bar_view import SearchBarWidget -from tagstudio.qt.mnemonics import assign_mnemonics from tagstudio.qt.models.palette import ColorType, get_tag_color -from tagstudio.qt.platform_strings import trash_term from tagstudio.qt.resource_manager import ResourceManager from tagstudio.qt.thumb_grid_layout import ThumbGridLayout from tagstudio.qt.translations import Translations +from tagstudio.qt.views.widgets.main_menu_bar_view import MainMenuBar # Only import for type checking/autocompletion, will not be imported at runtime. if typing.TYPE_CHECKING: @@ -57,391 +44,6 @@ logger = structlog.get_logger(__name__) - -class MainMenuBar(QMenuBar): - file_menu: QMenu - open_library_action: QAction - open_recent_library_menu: QMenu - save_library_backup_action: QAction - settings_action: QAction - open_on_start_action: QAction - refresh_dir_action: QAction - close_library_action: QAction - - edit_menu: QMenu - new_tag_action: QAction - select_all_action: QAction - select_inverse_action: QAction - clear_select_action: QAction - copy_fields_action: QAction - paste_fields_action: QAction - add_tag_to_selected_action: QAction - delete_file_action: QAction - ignore_modal_action: QAction - tag_manager_action: QAction - color_manager_action: QAction - - view_menu: QMenu - show_filenames_action: QAction - - tools_menu: QMenu - fix_unlinked_entries_action: QAction - fix_ignored_entries_action: QAction - fix_dupe_files_action: QAction - clear_thumb_cache_action: QAction - - macros_menu: QMenu - folders_to_tags_action: QAction - - help_menu: QMenu - about_action: QAction - - def __init__(self, parent: QWidget | None = None): - super().__init__(parent) - - self.setup_file_menu() - self.setup_edit_menu() - self.setup_view_menu() - self.setup_tools_menu() - self.setup_macros_menu() - self.setup_help_menu() - - def setup_file_menu(self): - self.file_menu = QMenu(Translations["menu.file"], self) - - # Open/Create Library - self.open_library_action = QAction(Translations["menu.file.open_create_library"], self) - self.open_library_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), - QtCore.Qt.Key.Key_O, - ) - ) - self.open_library_action.setToolTip("Ctrl+O") - self.file_menu.addAction(self.open_library_action) - - # Open Recent - self.open_recent_library_menu = QMenu(Translations["menu.file.open_recent_library"], self) - self.file_menu.addMenu(self.open_recent_library_menu) - - # Save Library Backup - self.save_library_backup_action = QAction(Translations["menu.file.save_backup"], self) - self.save_library_backup_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier( - QtCore.Qt.KeyboardModifier.ControlModifier - | QtCore.Qt.KeyboardModifier.ShiftModifier - ), - QtCore.Qt.Key.Key_S, - ) - ) - self.save_library_backup_action.setStatusTip("Ctrl+Shift+S") - self.save_library_backup_action.setEnabled(False) - self.file_menu.addAction(self.save_library_backup_action) - - self.file_menu.addSeparator() - - # Settings... - self.settings_action = QAction(Translations["menu.settings"], self) - self.file_menu.addAction(self.settings_action) - - # Open Library on Start - self.open_on_start_action = QAction(Translations["settings.open_library_on_start"], self) - self.open_on_start_action.setCheckable(True) - self.file_menu.addAction(self.open_on_start_action) - - self.file_menu.addSeparator() - - # Refresh Directories - self.refresh_dir_action = QAction(Translations["menu.file.refresh_directories"], self) - self.refresh_dir_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), - QtCore.Qt.Key.Key_R, - ) - ) - self.refresh_dir_action.setStatusTip("Ctrl+R") - self.refresh_dir_action.setEnabled(False) - self.file_menu.addAction(self.refresh_dir_action) - - self.file_menu.addSeparator() - - # Close Library - self.close_library_action = QAction(Translations["menu.file.close_library"], self) - self.close_library_action.setEnabled(False) - self.file_menu.addAction(self.close_library_action) - - self.file_menu.addSeparator() - - assign_mnemonics(self.file_menu) - self.addMenu(self.file_menu) - - def setup_edit_menu(self): - self.edit_menu = QMenu(Translations["generic.edit_alt"], self) - - # New Tag - self.new_tag_action = QAction(Translations["menu.edit.new_tag"], self) - self.new_tag_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), - QtCore.Qt.Key.Key_T, - ) - ) - self.new_tag_action.setToolTip("Ctrl+T") - self.new_tag_action.setEnabled(False) - self.edit_menu.addAction(self.new_tag_action) - - self.edit_menu.addSeparator() - - # Select All - self.select_all_action = QAction(Translations["select.all"], self) - self.select_all_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), - QtCore.Qt.Key.Key_A, - ) - ) - self.select_all_action.setToolTip("Ctrl+A") - self.select_all_action.setEnabled(False) - self.edit_menu.addAction(self.select_all_action) - - # Invert Selection - self.select_inverse_action = QAction(Translations["select.inverse"], self) - self.select_inverse_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier( - QtCore.Qt.KeyboardModifier.ControlModifier - ^ QtCore.Qt.KeyboardModifier.ShiftModifier - ), - QtCore.Qt.Key.Key_I, - ) - ) - self.select_inverse_action.setToolTip("Ctrl+Shift+I") - self.select_inverse_action.setEnabled(False) - self.edit_menu.addAction(self.select_inverse_action) - - # Clear Selection - self.clear_select_action = QAction(Translations["select.clear"], self) - self.clear_select_action.setShortcut(QtCore.Qt.Key.Key_Escape) - self.clear_select_action.setToolTip("Esc") - self.clear_select_action.setEnabled(False) - self.edit_menu.addAction(self.clear_select_action) - - # Copy Fields - self.copy_fields_action = QAction(Translations["edit.copy_fields"], self) - self.copy_fields_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), - QtCore.Qt.Key.Key_C, - ) - ) - self.copy_fields_action.setToolTip("Ctrl+C") - self.copy_fields_action.setEnabled(False) - self.edit_menu.addAction(self.copy_fields_action) - - # Paste Fields - self.paste_fields_action = QAction(Translations["edit.paste_fields"], self) - self.paste_fields_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), - QtCore.Qt.Key.Key_V, - ) - ) - self.paste_fields_action.setToolTip("Ctrl+V") - self.paste_fields_action.setEnabled(False) - self.edit_menu.addAction(self.paste_fields_action) - - # Add Tag to Selected - self.add_tag_to_selected_action = QAction(Translations["select.add_tag_to_selected"], self) - self.add_tag_to_selected_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier( - QtCore.Qt.KeyboardModifier.ControlModifier - ^ QtCore.Qt.KeyboardModifier.ShiftModifier - ), - QtCore.Qt.Key.Key_T, - ) - ) - self.add_tag_to_selected_action.setToolTip("Ctrl+Shift+T") - self.add_tag_to_selected_action.setEnabled(False) - self.edit_menu.addAction(self.add_tag_to_selected_action) - - self.edit_menu.addSeparator() - - # Move Files to trash - self.delete_file_action = QAction( - Translations.format("menu.delete_selected_files_ambiguous", trash_term=trash_term()), - self, - ) - self.delete_file_action.setShortcut(QtCore.Qt.Key.Key_Delete) - self.delete_file_action.setEnabled(False) - self.edit_menu.addAction(self.delete_file_action) - - self.edit_menu.addSeparator() - - # Ignore Files and Directories (.ts_ignore System) - self.ignore_modal_action = QAction(Translations["menu.edit.ignore_files"], self) - self.ignore_modal_action.setEnabled(False) - self.edit_menu.addAction(self.ignore_modal_action) - - # Manage Tags - self.tag_manager_action = QAction(Translations["menu.edit.manage_tags"], self) - self.tag_manager_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), - QtCore.Qt.Key.Key_M, - ) - ) - self.tag_manager_action.setEnabled(False) - self.tag_manager_action.setToolTip("Ctrl+M") - self.edit_menu.addAction(self.tag_manager_action) - - # Color Manager - self.color_manager_action = QAction(Translations["edit.color_manager"], self) - self.color_manager_action.setEnabled(False) - self.edit_menu.addAction(self.color_manager_action) - - assign_mnemonics(self.edit_menu) - self.addMenu(self.edit_menu) - - def setup_view_menu(self): - self.view_menu = QMenu(Translations["menu.view"], self) - - self.library_info_action = QAction(Translations["menu.view.library_info"]) - self.view_menu.addAction(self.library_info_action) - - # show_libs_list_action = QAction(Translations["settings.show_recent_libraries"], menu_bar) - # show_libs_list_action.setCheckable(True) - # show_libs_list_action.setChecked(self.settings.show_library_list) - - self.show_filenames_action = QAction(Translations["settings.show_filenames_in_grid"], self) - self.show_filenames_action.setCheckable(True) - self.view_menu.addAction(self.show_filenames_action) - - self.view_menu.addSeparator() - - self.increase_thumbnail_size_action = QAction( - Translations["menu.view.increase_thumbnail_size"], self - ) - self.increase_thumbnail_size_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), - QtCore.Qt.Key.Key_Plus, - ) - ) - self.increase_thumbnail_size_action.setToolTip("Ctrl++") - self.view_menu.addAction(self.increase_thumbnail_size_action) - - self.decrease_thumbnail_size_action = QAction( - Translations["menu.view.decrease_thumbnail_size"], self - ) - self.decrease_thumbnail_size_action.setShortcut( - QtCore.QKeyCombination( - QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), - QtCore.Qt.Key.Key_Minus, - ) - ) - self.decrease_thumbnail_size_action.setToolTip("Ctrl+-") - self.view_menu.addAction(self.decrease_thumbnail_size_action) - - self.view_menu.addSeparator() - - assign_mnemonics(self.view_menu) - self.addMenu(self.view_menu) - - def setup_tools_menu(self): - self.tools_menu = QMenu(Translations["menu.tools"], self) - - # Fix Unlinked Entries - self.fix_unlinked_entries_action = QAction( - Translations["menu.tools.fix_unlinked_entries"], self - ) - self.fix_unlinked_entries_action.setEnabled(False) - self.tools_menu.addAction(self.fix_unlinked_entries_action) - - # Fix Ignored Entries - self.fix_ignored_entries_action = QAction( - Translations["menu.tools.fix_ignored_entries"], self - ) - self.fix_ignored_entries_action.setEnabled(False) - self.tools_menu.addAction(self.fix_ignored_entries_action) - - # Fix Duplicate Files - self.fix_dupe_files_action = QAction(Translations["menu.tools.fix_duplicate_files"], self) - self.fix_dupe_files_action.setEnabled(False) - self.tools_menu.addAction(self.fix_dupe_files_action) - - self.tools_menu.addSeparator() - - # Clear Thumbnail Cache - self.clear_thumb_cache_action = QAction( - Translations["settings.clear_thumb_cache.title"], self - ) - self.clear_thumb_cache_action.setEnabled(False) - self.tools_menu.addAction(self.clear_thumb_cache_action) - - assign_mnemonics(self.tools_menu) - self.addMenu(self.tools_menu) - - def setup_macros_menu(self): - self.macros_menu = QMenu(Translations["menu.macros"], self) - - self.folders_to_tags_action = QAction(Translations["menu.macros.folders_to_tags"], self) - self.folders_to_tags_action.setEnabled(False) - self.macros_menu.addAction(self.folders_to_tags_action) - - assign_mnemonics(self.macros_menu) - self.addMenu(self.macros_menu) - - def setup_help_menu(self): - self.help_menu = QMenu(Translations["menu.help"], self) - - self.about_action = QAction(Translations["menu.help.about"], self) - self.help_menu.addAction(self.about_action) - - assign_mnemonics(self.help_menu) - self.addMenu(self.help_menu) - - def rebuild_open_recent_library_menu( - self, - libraries: list[Path], - show_filepath: ShowFilepathOption, - open_library_callback: Callable[[Path], None], - clear_libraries_callback: Callable[[], None], - ): - actions: list[QAction] = [] - for path in libraries: - action = QAction(self.open_recent_library_menu) - if show_filepath == ShowFilepathOption.SHOW_FULL_PATHS: - action.setText(str(path)) - else: - action.setText(str(path.name)) - action.triggered.connect(lambda checked=False, p=path: open_library_callback(p)) - actions.append(action) - - clear_recent_action = QAction( - Translations["menu.file.clear_recent_libraries"], self.open_recent_library_menu - ) - clear_recent_action.triggered.connect(clear_libraries_callback) - actions.append(clear_recent_action) - - # Clear previous actions - for action in self.open_recent_library_menu.actions(): - self.open_recent_library_menu.removeAction(action) - - # Add new actions - for action in actions: - self.open_recent_library_menu.addAction(action) - - # Only enable add "clear recent" if there are still recent libraries. - if len(actions) > 1: - self.open_recent_library_menu.setDisabled(False) - self.open_recent_library_menu.addSeparator() - self.open_recent_library_menu.addAction(clear_recent_action) - else: - self.open_recent_library_menu.setDisabled(True) - - # View Component class MainWindow(QMainWindow): THUMB_SIZES: list[tuple[str, int]] = [ diff --git a/src/tagstudio/qt/views/widgets/main_menu_bar_view.py b/src/tagstudio/qt/views/widgets/main_menu_bar_view.py new file mode 100644 index 000000000..d4a432b3b --- /dev/null +++ b/src/tagstudio/qt/views/widgets/main_menu_bar_view.py @@ -0,0 +1,402 @@ +# Copyright (C) 2025 Travis Abendshien (CyanVoxel). +# Licensed under the GPL-3.0 License. +# Created for TagStudio: https://github.com/CyanVoxel/TagStudio + +from collections.abc import Callable +from pathlib import Path + +from PySide6 import QtCore +from PySide6.QtGui import QAction +from PySide6.QtWidgets import ( + QMenu, + QMenuBar, + QWidget, +) + +from tagstudio.core.enums import ShowFilepathOption +from tagstudio.qt.mnemonics import assign_mnemonics +from tagstudio.qt.platform_strings import trash_term +from tagstudio.qt.translations import Translations + +class MainMenuBar(QMenuBar): + file_menu: QMenu + open_library_action: QAction + open_recent_library_menu: QMenu + save_library_backup_action: QAction + settings_action: QAction + open_on_start_action: QAction + refresh_dir_action: QAction + close_library_action: QAction + + edit_menu: QMenu + new_tag_action: QAction + select_all_action: QAction + select_inverse_action: QAction + clear_select_action: QAction + copy_fields_action: QAction + paste_fields_action: QAction + add_tag_to_selected_action: QAction + delete_file_action: QAction + ignore_modal_action: QAction + tag_manager_action: QAction + color_manager_action: QAction + + view_menu: QMenu + show_filenames_action: QAction + + tools_menu: QMenu + fix_unlinked_entries_action: QAction + fix_ignored_entries_action: QAction + fix_dupe_files_action: QAction + clear_thumb_cache_action: QAction + + macros_menu: QMenu + folders_to_tags_action: QAction + + help_menu: QMenu + about_action: QAction + + def __init__(self, parent: QWidget | None = None): + super().__init__(parent) + + self.setup_file_menu() + self.setup_edit_menu() + self.setup_view_menu() + self.setup_tools_menu() + self.setup_macros_menu() + self.setup_help_menu() + + def setup_file_menu(self): + self.file_menu = QMenu(Translations["menu.file"], self) + + # Open/Create Library + self.open_library_action = QAction(Translations["menu.file.open_create_library"], self) + self.open_library_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), + QtCore.Qt.Key.Key_O, + ) + ) + self.open_library_action.setToolTip("Ctrl+O") + self.file_menu.addAction(self.open_library_action) + + # Open Recent + self.open_recent_library_menu = QMenu(Translations["menu.file.open_recent_library"], self) + self.file_menu.addMenu(self.open_recent_library_menu) + + # Save Library Backup + self.save_library_backup_action = QAction(Translations["menu.file.save_backup"], self) + self.save_library_backup_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier( + QtCore.Qt.KeyboardModifier.ControlModifier + | QtCore.Qt.KeyboardModifier.ShiftModifier + ), + QtCore.Qt.Key.Key_S, + ) + ) + self.save_library_backup_action.setStatusTip("Ctrl+Shift+S") + self.save_library_backup_action.setEnabled(False) + self.file_menu.addAction(self.save_library_backup_action) + + self.file_menu.addSeparator() + + # Settings... + self.settings_action = QAction(Translations["menu.settings"], self) + self.file_menu.addAction(self.settings_action) + + # Open Library on Start + self.open_on_start_action = QAction(Translations["settings.open_library_on_start"], self) + self.open_on_start_action.setCheckable(True) + self.file_menu.addAction(self.open_on_start_action) + + self.file_menu.addSeparator() + + # Refresh Directories + self.refresh_dir_action = QAction(Translations["menu.file.refresh_directories"], self) + self.refresh_dir_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), + QtCore.Qt.Key.Key_R, + ) + ) + self.refresh_dir_action.setStatusTip("Ctrl+R") + self.refresh_dir_action.setEnabled(False) + self.file_menu.addAction(self.refresh_dir_action) + + self.file_menu.addSeparator() + + # Close Library + self.close_library_action = QAction(Translations["menu.file.close_library"], self) + self.close_library_action.setEnabled(False) + self.file_menu.addAction(self.close_library_action) + + self.file_menu.addSeparator() + + assign_mnemonics(self.file_menu) + self.addMenu(self.file_menu) + + def setup_edit_menu(self): + self.edit_menu = QMenu(Translations["generic.edit_alt"], self) + + # New Tag + self.new_tag_action = QAction(Translations["menu.edit.new_tag"], self) + self.new_tag_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), + QtCore.Qt.Key.Key_T, + ) + ) + self.new_tag_action.setToolTip("Ctrl+T") + self.new_tag_action.setEnabled(False) + self.edit_menu.addAction(self.new_tag_action) + + self.edit_menu.addSeparator() + + # Select All + self.select_all_action = QAction(Translations["select.all"], self) + self.select_all_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), + QtCore.Qt.Key.Key_A, + ) + ) + self.select_all_action.setToolTip("Ctrl+A") + self.select_all_action.setEnabled(False) + self.edit_menu.addAction(self.select_all_action) + + # Invert Selection + self.select_inverse_action = QAction(Translations["select.inverse"], self) + self.select_inverse_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier( + QtCore.Qt.KeyboardModifier.ControlModifier + ^ QtCore.Qt.KeyboardModifier.ShiftModifier + ), + QtCore.Qt.Key.Key_I, + ) + ) + self.select_inverse_action.setToolTip("Ctrl+Shift+I") + self.select_inverse_action.setEnabled(False) + self.edit_menu.addAction(self.select_inverse_action) + + # Clear Selection + self.clear_select_action = QAction(Translations["select.clear"], self) + self.clear_select_action.setShortcut(QtCore.Qt.Key.Key_Escape) + self.clear_select_action.setToolTip("Esc") + self.clear_select_action.setEnabled(False) + self.edit_menu.addAction(self.clear_select_action) + + # Copy Fields + self.copy_fields_action = QAction(Translations["edit.copy_fields"], self) + self.copy_fields_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), + QtCore.Qt.Key.Key_C, + ) + ) + self.copy_fields_action.setToolTip("Ctrl+C") + self.copy_fields_action.setEnabled(False) + self.edit_menu.addAction(self.copy_fields_action) + + # Paste Fields + self.paste_fields_action = QAction(Translations["edit.paste_fields"], self) + self.paste_fields_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), + QtCore.Qt.Key.Key_V, + ) + ) + self.paste_fields_action.setToolTip("Ctrl+V") + self.paste_fields_action.setEnabled(False) + self.edit_menu.addAction(self.paste_fields_action) + + # Add Tag to Selected + self.add_tag_to_selected_action = QAction(Translations["select.add_tag_to_selected"], self) + self.add_tag_to_selected_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier( + QtCore.Qt.KeyboardModifier.ControlModifier + ^ QtCore.Qt.KeyboardModifier.ShiftModifier + ), + QtCore.Qt.Key.Key_T, + ) + ) + self.add_tag_to_selected_action.setToolTip("Ctrl+Shift+T") + self.add_tag_to_selected_action.setEnabled(False) + self.edit_menu.addAction(self.add_tag_to_selected_action) + + self.edit_menu.addSeparator() + + # Move Files to trash + self.delete_file_action = QAction( + Translations.format("menu.delete_selected_files_ambiguous", trash_term=trash_term()), + self, + ) + self.delete_file_action.setShortcut(QtCore.Qt.Key.Key_Delete) + self.delete_file_action.setEnabled(False) + self.edit_menu.addAction(self.delete_file_action) + + self.edit_menu.addSeparator() + + # Ignore Files and Directories (.ts_ignore System) + self.ignore_modal_action = QAction(Translations["menu.edit.ignore_files"], self) + self.ignore_modal_action.setEnabled(False) + self.edit_menu.addAction(self.ignore_modal_action) + + # Manage Tags + self.tag_manager_action = QAction(Translations["menu.edit.manage_tags"], self) + self.tag_manager_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), + QtCore.Qt.Key.Key_M, + ) + ) + self.tag_manager_action.setEnabled(False) + self.tag_manager_action.setToolTip("Ctrl+M") + self.edit_menu.addAction(self.tag_manager_action) + + # Color Manager + self.color_manager_action = QAction(Translations["edit.color_manager"], self) + self.color_manager_action.setEnabled(False) + self.edit_menu.addAction(self.color_manager_action) + + assign_mnemonics(self.edit_menu) + self.addMenu(self.edit_menu) + + def setup_view_menu(self): + self.view_menu = QMenu(Translations["menu.view"], self) + + self.library_info_action = QAction(Translations["menu.view.library_info"]) + self.view_menu.addAction(self.library_info_action) + + # show_libs_list_action = QAction(Translations["settings.show_recent_libraries"], menu_bar) + # show_libs_list_action.setCheckable(True) + # show_libs_list_action.setChecked(self.settings.show_library_list) + + self.show_filenames_action = QAction(Translations["settings.show_filenames_in_grid"], self) + self.show_filenames_action.setCheckable(True) + self.view_menu.addAction(self.show_filenames_action) + + self.view_menu.addSeparator() + + self.increase_thumbnail_size_action = QAction( + Translations["menu.view.increase_thumbnail_size"], self + ) + self.increase_thumbnail_size_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), + QtCore.Qt.Key.Key_Plus, + ) + ) + self.increase_thumbnail_size_action.setToolTip("Ctrl++") + self.view_menu.addAction(self.increase_thumbnail_size_action) + + self.decrease_thumbnail_size_action = QAction( + Translations["menu.view.decrease_thumbnail_size"], self + ) + self.decrease_thumbnail_size_action.setShortcut( + QtCore.QKeyCombination( + QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier), + QtCore.Qt.Key.Key_Minus, + ) + ) + self.decrease_thumbnail_size_action.setToolTip("Ctrl+-") + self.view_menu.addAction(self.decrease_thumbnail_size_action) + + self.view_menu.addSeparator() + + assign_mnemonics(self.view_menu) + self.addMenu(self.view_menu) + + def setup_tools_menu(self): + self.tools_menu = QMenu(Translations["menu.tools"], self) + + # Fix Unlinked Entries + self.fix_unlinked_entries_action = QAction( + Translations["menu.tools.fix_unlinked_entries"], self + ) + self.fix_unlinked_entries_action.setEnabled(False) + self.tools_menu.addAction(self.fix_unlinked_entries_action) + + # Fix Ignored Entries + self.fix_ignored_entries_action = QAction( + Translations["menu.tools.fix_ignored_entries"], self + ) + self.fix_ignored_entries_action.setEnabled(False) + self.tools_menu.addAction(self.fix_ignored_entries_action) + + # Fix Duplicate Files + self.fix_dupe_files_action = QAction(Translations["menu.tools.fix_duplicate_files"], self) + self.fix_dupe_files_action.setEnabled(False) + self.tools_menu.addAction(self.fix_dupe_files_action) + + self.tools_menu.addSeparator() + + # Clear Thumbnail Cache + self.clear_thumb_cache_action = QAction( + Translations["settings.clear_thumb_cache.title"], self + ) + self.clear_thumb_cache_action.setEnabled(False) + self.tools_menu.addAction(self.clear_thumb_cache_action) + + assign_mnemonics(self.tools_menu) + self.addMenu(self.tools_menu) + + def setup_macros_menu(self): + self.macros_menu = QMenu(Translations["menu.macros"], self) + + self.folders_to_tags_action = QAction(Translations["menu.macros.folders_to_tags"], self) + self.folders_to_tags_action.setEnabled(False) + self.macros_menu.addAction(self.folders_to_tags_action) + + assign_mnemonics(self.macros_menu) + self.addMenu(self.macros_menu) + + def setup_help_menu(self): + self.help_menu = QMenu(Translations["menu.help"], self) + + self.about_action = QAction(Translations["menu.help.about"], self) + self.help_menu.addAction(self.about_action) + + assign_mnemonics(self.help_menu) + self.addMenu(self.help_menu) + + def rebuild_open_recent_library_menu( + self, + libraries: list[Path], + show_filepath: ShowFilepathOption, + open_library_callback: Callable[[Path], None], + clear_libraries_callback: Callable[[], None], + ): + actions: list[QAction] = [] + for path in libraries: + action = QAction(self.open_recent_library_menu) + if show_filepath == ShowFilepathOption.SHOW_FULL_PATHS: + action.setText(str(path)) + else: + action.setText(str(path.name)) + action.triggered.connect(lambda checked=False, p=path: open_library_callback(p)) + actions.append(action) + + clear_recent_action = QAction( + Translations["menu.file.clear_recent_libraries"], self.open_recent_library_menu + ) + clear_recent_action.triggered.connect(clear_libraries_callback) + actions.append(clear_recent_action) + + # Clear previous actions + for action in self.open_recent_library_menu.actions(): + self.open_recent_library_menu.removeAction(action) + + # Add new actions + for action in actions: + self.open_recent_library_menu.addAction(action) + + # Only enable add "clear recent" if there are still recent libraries. + if len(actions) > 1: + self.open_recent_library_menu.setDisabled(False) + self.open_recent_library_menu.addSeparator() + self.open_recent_library_menu.addAction(clear_recent_action) + else: + self.open_recent_library_menu.setDisabled(True) From 1bc818ac1d42fbaf98c5f626271f6996a6fcd175 Mon Sep 17 00:00:00 2001 From: Demohstens Date: Sat, 6 Dec 2025 15:29:06 +0100 Subject: [PATCH 4/5] Refactor: renamed extra_input_bar; moved to seperate class. Moved THUMB_SIZES const to models. --- src/tagstudio/qt/models/thumb_sizes.py | 10 ++ src/tagstudio/qt/ts_qt.py | 27 ++-- src/tagstudio/qt/views/main_window.py | 135 ++---------------- .../widgets/content_display_toolbar_view.py | 124 ++++++++++++++++ 4 files changed, 161 insertions(+), 135 deletions(-) create mode 100644 src/tagstudio/qt/models/thumb_sizes.py create mode 100644 src/tagstudio/qt/views/widgets/content_display_toolbar_view.py diff --git a/src/tagstudio/qt/models/thumb_sizes.py b/src/tagstudio/qt/models/thumb_sizes.py new file mode 100644 index 000000000..0a5294b42 --- /dev/null +++ b/src/tagstudio/qt/models/thumb_sizes.py @@ -0,0 +1,10 @@ +from tagstudio.qt.translations import Translations + +THUMB_SIZES: list[tuple[str, int]] = [ + (Translations["home.thumbnail_size.extra_large"], 256), + (Translations["home.thumbnail_size.large"], 192), + (Translations["home.thumbnail_size.medium"], 128), + (Translations["home.thumbnail_size.small"], 96), + (Translations["home.thumbnail_size.mini"], 76), + ] + diff --git a/src/tagstudio/qt/ts_qt.py b/src/tagstudio/qt/ts_qt.py index 0506a4793..bc0b8efa7 100644 --- a/src/tagstudio/qt/ts_qt.py +++ b/src/tagstudio/qt/ts_qt.py @@ -90,6 +90,7 @@ from tagstudio.qt.mixed.tag_database import TagDatabasePanel from tagstudio.qt.mixed.tag_search import TagSearchModal from tagstudio.qt.models.palette import ColorType, UiColor, get_ui_color +from tagstudio.qt.models.thumb_sizes import THUMB_SIZES from tagstudio.qt.platform_strings import trash_term from tagstudio.qt.previews.vendored.ffmpeg import FFMPEG_CMD, FFPROBE_CMD from tagstudio.qt.resource_manager import ResourceManager @@ -484,18 +485,18 @@ def on_show_filenames_action(checked: bool): ) def on_decrease_thumbnail_size_action(): - new_val = self.main_window.thumb_size_combobox.currentIndex() + 1 - if not (new_val + 1) > len(self.main_window.THUMB_SIZES): - self.main_window.thumb_size_combobox.setCurrentIndex(new_val) + new_val = self.main_window.content_display_toolbar.thumb_size_combobox.currentIndex() + 1 + if not (new_val + 1) > len(THUMB_SIZES): + self.main_window.content_display_toolbar.thumb_size_combobox.setCurrentIndex(new_val) self.main_window.menu_bar.decrease_thumbnail_size_action.triggered.connect( on_decrease_thumbnail_size_action ) def on_increase_thumbnail_size_action(): - new_val = self.main_window.thumb_size_combobox.currentIndex() - 1 + new_val = self.main_window.content_display_toolbar.thumb_size_combobox.currentIndex() - 1 if not new_val < 0: - self.main_window.thumb_size_combobox.setCurrentIndex(new_val) + self.main_window.content_display_toolbar.thumb_size_combobox.setCurrentIndex(new_val) self.main_window.menu_bar.increase_thumbnail_size_action.triggered.connect( on_increase_thumbnail_size_action @@ -643,26 +644,26 @@ def _update_browsing_state(): self.main_window.search_bar.search_field.returnPressed.connect(_update_browsing_state) # Sorting Dropdowns - self.main_window.sorting_mode_combobox.setCurrentIndex( + self.main_window.content_display_toolbar.sorting_mode_combobox.setCurrentIndex( list(SortingModeEnum).index(self.browsing_history.current.sorting_mode) ) - self.main_window.sorting_mode_combobox.currentIndexChanged.connect( + self.main_window.content_display_toolbar.sorting_mode_combobox.currentIndexChanged.connect( self.sorting_mode_callback ) - self.main_window.sorting_direction_combobox.currentIndexChanged.connect( + self.main_window.content_display_toolbar.sorting_direction_combobox.currentIndexChanged.connect( self.sorting_direction_callback ) # Thumbnail Size ComboBox - self.main_window.thumb_size_combobox.setCurrentIndex(2) # Default: Medium - self.main_window.thumb_size_combobox.currentIndexChanged.connect( - lambda: self.thumb_size_callback(self.main_window.thumb_size_combobox.currentIndex()) + self.main_window.content_display_toolbar.thumb_size_combobox.setCurrentIndex(2) # Default: Medium + self.main_window.content_display_toolbar.thumb_size_combobox.currentIndexChanged.connect( + lambda: self.thumb_size_callback(self.main_window.content_display_toolbar.thumb_size_combobox.currentIndex()) ) # Exclude hidden entries checkbox - self.main_window.show_hidden_entries_checkbox.setChecked(False) # Default: No - self.main_window.show_hidden_entries_checkbox.stateChanged.connect( + self.main_window.content_display_toolbar.show_hidden_entries_checkbox.setChecked(False) # Default: No + self.main_window.content_display_toolbar.show_hidden_entries_checkbox.stateChanged.connect( self.show_hidden_entries_callback ) diff --git a/src/tagstudio/qt/views/main_window.py b/src/tagstudio/qt/views/main_window.py index 3ac53b4b2..14113d9ef 100644 --- a/src/tagstudio/qt/views/main_window.py +++ b/src/tagstudio/qt/views/main_window.py @@ -7,35 +7,28 @@ import structlog from PySide6.QtCore import QMetaObject, Qt -from PySide6.QtGui import QColor from PySide6.QtWidgets import ( - QCheckBox, - QComboBox, QFrame, QGridLayout, QHBoxLayout, - QLabel, QMainWindow, QScrollArea, QSizePolicy, - QSpacerItem, QSplitter, QStatusBar, QVBoxLayout, QWidget, ) -from tagstudio.core.library.alchemy.enums import SortingModeEnum, TagColorEnum +from tagstudio.core.library.alchemy.enums import SortingModeEnum from tagstudio.qt.controllers.preview_panel_controller import PreviewPanel from tagstudio.qt.mixed.landing import LandingWidget from tagstudio.qt.mixed.pagination import Pagination -from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color from tagstudio.qt.views.widgets.search_bar_view import SearchBarWidget -from tagstudio.qt.models.palette import ColorType, get_tag_color from tagstudio.qt.resource_manager import ResourceManager from tagstudio.qt.thumb_grid_layout import ThumbGridLayout -from tagstudio.qt.translations import Translations from tagstudio.qt.views.widgets.main_menu_bar_view import MainMenuBar +from tagstudio.qt.views.widgets.content_display_toolbar_view import ContentDisplayToolbar # Only import for type checking/autocompletion, will not be imported at runtime. if typing.TYPE_CHECKING: @@ -46,14 +39,6 @@ # View Component class MainWindow(QMainWindow): - THUMB_SIZES: list[tuple[str, int]] = [ - (Translations["home.thumbnail_size.extra_large"], 256), - (Translations["home.thumbnail_size.large"], 192), - (Translations["home.thumbnail_size.medium"], 128), - (Translations["home.thumbnail_size.small"], 96), - (Translations["home.thumbnail_size.mini"], 76), - ] - def __init__(self, driver: "QtDriver", parent: QWidget | None = None) -> None: super().__init__(parent) self.rm = ResourceManager() @@ -62,11 +47,8 @@ def __init__(self, driver: "QtDriver", parent: QWidget | None = None) -> None: # initialized in setup_search_bar self.search_bar: SearchBarWidget - # initialized in setup_extra_input_bar - self.extra_input_layout: QHBoxLayout - self.sorting_mode_combobox: QComboBox - self.sorting_direction_combobox: QComboBox - self.thumb_size_combobox: QComboBox + # initialized in setup_content_display_toolbar + self.content_display_toolbar: ContentDisplayToolbar # initialized in setup_content self.content_layout: QHBoxLayout @@ -126,7 +108,7 @@ def setup_central_widget(self, driver: "QtDriver"): self.central_layout.setObjectName("central_layout") self.setup_search_bar() - self.setup_extra_input_bar() + self.setup_content_display_toolbar() self.setup_content(driver) self.setCentralWidget(self.central_widget) @@ -136,103 +118,12 @@ def setup_search_bar(self): self.search_bar.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) self.central_layout.addWidget(self.search_bar, 3, 0, 1, 1) - def setup_extra_input_bar(self): + def setup_content_display_toolbar(self): """Sets up inputs for sorting settings and thumbnail size.""" - self.extra_input_layout = QHBoxLayout() - self.extra_input_layout.setObjectName("extra_input_layout") - - primary_color = QColor(get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)) - border_color = get_border_color(primary_color) - highlight_color = get_highlight_color(primary_color) - text_color: QColor = get_text_color(primary_color, highlight_color) - - ## Show hidden entries checkbox - self.show_hidden_entries_widget = QWidget() - self.show_hidden_entries_layout = QHBoxLayout(self.show_hidden_entries_widget) - self.show_hidden_entries_layout.setStretch(1, 1) - self.show_hidden_entries_layout.setContentsMargins(0, 0, 0, 0) - self.show_hidden_entries_layout.setSpacing(6) - self.show_hidden_entries_layout.setAlignment(Qt.AlignmentFlag.AlignLeft) - self.show_hidden_entries_title = QLabel(Translations["home.show_hidden_entries"]) - self.show_hidden_entries_checkbox = QCheckBox() - self.show_hidden_entries_checkbox.setFixedSize(22, 22) - - self.show_hidden_entries_checkbox.setStyleSheet( - f"QCheckBox{{" - f"background: rgba{primary_color.toTuple()};" - f"color: rgba{text_color.toTuple()};" - f"border-color: rgba{border_color.toTuple()};" - f"border-radius: 6px;" - f"border-style:solid;" - f"border-width: 2px;" - f"}}" - f"QCheckBox::indicator{{" - f"width: 10px;" - f"height: 10px;" - f"border-radius: 2px;" - f"margin: 4px;" - f"}}" - f"QCheckBox::indicator:checked{{" - f"background: rgba{text_color.toTuple()};" - f"}}" - f"QCheckBox::hover{{" - f"border-color: rgba{highlight_color.toTuple()};" - f"}}" - f"QCheckBox::focus{{" - f"border-color: rgba{highlight_color.toTuple()};" - f"outline:none;" - f"}}" - ) - - self.show_hidden_entries_checkbox.setChecked(False) # Default: No - - self.show_hidden_entries_layout.addWidget(self.show_hidden_entries_checkbox) - self.show_hidden_entries_layout.addWidget(self.show_hidden_entries_title) - - self.extra_input_layout.addWidget(self.show_hidden_entries_widget) + self.content_display_toolbar = ContentDisplayToolbar(self) + self.content_display_toolbar.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) - ## Spacer - self.extra_input_layout.addItem( - QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) - ) - - ## Sorting Mode Dropdown - self.sorting_mode_combobox = QComboBox(self.central_widget) - self.sorting_mode_combobox.setObjectName("sorting_mode_combobox") - for sort_mode in SortingModeEnum: - self.sorting_mode_combobox.addItem(Translations[sort_mode.value], sort_mode) - self.extra_input_layout.addWidget(self.sorting_mode_combobox) - - ## Sorting Direction Dropdown - self.sorting_direction_combobox = QComboBox(self.central_widget) - self.sorting_direction_combobox.setObjectName("sorting_direction_combobox") - self.sorting_direction_combobox.addItem( - Translations["sorting.direction.ascending"], userData=True - ) - self.sorting_direction_combobox.addItem( - Translations["sorting.direction.descending"], userData=False - ) - self.sorting_direction_combobox.setCurrentIndex(1) # Default: Descending - self.extra_input_layout.addWidget(self.sorting_direction_combobox) - - ## Thumbnail Size placeholder - self.thumb_size_combobox = QComboBox(self.central_widget) - self.thumb_size_combobox.setObjectName("thumb_size_combobox") - self.thumb_size_combobox.setPlaceholderText(Translations["home.thumbnail_size"]) - self.thumb_size_combobox.setCurrentText("") - size_policy = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed) - size_policy.setHorizontalStretch(0) - size_policy.setVerticalStretch(0) - size_policy.setHeightForWidth(self.thumb_size_combobox.sizePolicy().hasHeightForWidth()) - self.thumb_size_combobox.setSizePolicy(size_policy) - self.thumb_size_combobox.setMinimumWidth(128) - self.thumb_size_combobox.setMaximumWidth(352) - self.extra_input_layout.addWidget(self.thumb_size_combobox) - for size in MainWindow.THUMB_SIZES: - self.thumb_size_combobox.addItem(size[0], size[1]) - self.thumb_size_combobox.setCurrentIndex(2) # Default: Medium - - self.central_layout.addLayout(self.extra_input_layout, 5, 0, 1, 1) + self.central_layout.addWidget(self.content_display_toolbar, 5, 0, 1, 1) def setup_content(self, driver: "QtDriver"): self.content_layout = QHBoxLayout() @@ -315,18 +206,18 @@ def toggle_landing_page(self, enabled: bool): @property def sorting_mode(self) -> SortingModeEnum: """What to sort by.""" - return self.sorting_mode_combobox.currentData() + return self.content_display_toolbar.sorting_mode_combobox.currentData() @property def sorting_direction(self) -> bool: """Whether to Sort the results in ascending order.""" - return self.sorting_direction_combobox.currentData() + return self.content_display_toolbar.sorting_direction_combobox.currentData() @property def thumb_size(self) -> int: - return self.thumb_size_combobox.currentData() + return self.content_display_toolbar.thumb_size_combobox.currentData() @property def show_hidden_entries(self) -> bool: """Whether to show entries tagged with hidden tags.""" - return self.show_hidden_entries_checkbox.isChecked() + return self.content_display_toolbar.show_hidden_entries_checkbox.isChecked() diff --git a/src/tagstudio/qt/views/widgets/content_display_toolbar_view.py b/src/tagstudio/qt/views/widgets/content_display_toolbar_view.py new file mode 100644 index 000000000..1f7c682c0 --- /dev/null +++ b/src/tagstudio/qt/views/widgets/content_display_toolbar_view.py @@ -0,0 +1,124 @@ +import typing + +from PySide6.QtCore import Qt +from PySide6.QtGui import QColor +from PySide6.QtWidgets import ( + QWidget, + QHBoxLayout, + QHBoxLayout, + QCheckBox, + QComboBox, + QLabel, + QWidget, + QSpacerItem, + QSizePolicy + ) +from tagstudio.core.library.alchemy.enums import SortingModeEnum, TagColorEnum +from tagstudio.qt.translations import Translations +from tagstudio.qt.models.thumb_sizes import THUMB_SIZES +from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color +from tagstudio.qt.models.palette import ColorType, get_tag_color + + +class ContentDisplayToolbar(QWidget): + show_hidden_entries_widget: QWidget + show_hidden_entries_layout: QHBoxLayout + + + def __init__(self, parent: QWidget): + super().__init__(parent) + layout = QHBoxLayout() + layout.setObjectName("content_display_toolbar") + + primary_color = QColor(get_tag_color(ColorType.PRIMARY, TagColorEnum.DEFAULT)) + border_color = get_border_color(primary_color) + highlight_color = get_highlight_color(primary_color) + text_color: QColor = get_text_color(primary_color, highlight_color) + + ## Show hidden entries checkbox + self.show_hidden_entries_widget = QWidget() + self.show_hidden_entries_layout = QHBoxLayout(self.show_hidden_entries_widget) + self.show_hidden_entries_layout.setStretch(1, 1) + self.show_hidden_entries_layout.setContentsMargins(0, 0, 0, 0) + self.show_hidden_entries_layout.setSpacing(6) + self.show_hidden_entries_layout.setAlignment(Qt.AlignmentFlag.AlignLeft) + self.show_hidden_entries_title = QLabel(Translations["home.show_hidden_entries"]) + self.show_hidden_entries_checkbox = QCheckBox() + self.show_hidden_entries_checkbox.setFixedSize(22, 22) + + self.show_hidden_entries_checkbox.setStyleSheet( + f"QCheckBox{{" + f"background: rgba{primary_color.toTuple()};" + f"color: rgba{text_color.toTuple()};" + f"border-color: rgba{border_color.toTuple()};" + f"border-radius: 6px;" + f"border-style:solid;" + f"border-width: 2px;" + f"}}" + f"QCheckBox::indicator{{" + f"width: 10px;" + f"height: 10px;" + f"border-radius: 2px;" + f"margin: 4px;" + f"}}" + f"QCheckBox::indicator:checked{{" + f"background: rgba{text_color.toTuple()};" + f"}}" + f"QCheckBox::hover{{" + f"border-color: rgba{highlight_color.toTuple()};" + f"}}" + f"QCheckBox::focus{{" + f"border-color: rgba{highlight_color.toTuple()};" + f"outline:none;" + f"}}" + ) + + self.show_hidden_entries_checkbox.setChecked(False) # Default: No + + self.show_hidden_entries_layout.addWidget(self.show_hidden_entries_checkbox) + self.show_hidden_entries_layout.addWidget(self.show_hidden_entries_title) + + layout.addWidget(self.show_hidden_entries_widget) + + ## Spacer + layout.addItem( + QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + ) + + ## Sorting Mode Dropdown + self.sorting_mode_combobox = QComboBox(self) + self.sorting_mode_combobox.setObjectName("sorting_mode_combobox") + for sort_mode in SortingModeEnum: + self.sorting_mode_combobox.addItem(Translations[sort_mode.value], sort_mode) + layout.addWidget(self.sorting_mode_combobox) + + ## Sorting Direction Dropdown + self.sorting_direction_combobox = QComboBox(self) + self.sorting_direction_combobox.setObjectName("sorting_direction_combobox") + self.sorting_direction_combobox.addItem( + Translations["sorting.direction.ascending"], userData=True + ) + self.sorting_direction_combobox.addItem( + Translations["sorting.direction.descending"], userData=False + ) + self.sorting_direction_combobox.setCurrentIndex(1) # Default: Descending + layout.addWidget(self.sorting_direction_combobox) + + ## Thumbnail Size placeholder + self.thumb_size_combobox = QComboBox(self) + self.thumb_size_combobox.setObjectName("thumb_size_combobox") + self.thumb_size_combobox.setPlaceholderText(Translations["home.thumbnail_size"]) + self.thumb_size_combobox.setCurrentText("") + size_policy = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.thumb_size_combobox.sizePolicy().hasHeightForWidth()) + self.thumb_size_combobox.setSizePolicy(size_policy) + self.thumb_size_combobox.setMinimumWidth(128) + self.thumb_size_combobox.setMaximumWidth(352) + layout.addWidget(self.thumb_size_combobox) + for size in THUMB_SIZES: + self.thumb_size_combobox.addItem(size[0], size[1]) + self.thumb_size_combobox.setCurrentIndex(2) # Default: Medium + + self.setLayout(layout) \ No newline at end of file From f56915c27aa01e436d27e08e42c33a3c3b4453aa Mon Sep 17 00:00:00 2001 From: Demohstens Date: Sat, 6 Dec 2025 15:35:39 +0100 Subject: [PATCH 5/5] refactor: cleaned up imports, etc --- src/tagstudio/qt/views/main_window.py | 4 ++-- .../widgets/content_display_toolbar_view.py | 17 +++++++---------- .../qt/views/widgets/search_bar_view.py | 19 +++++-------------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/tagstudio/qt/views/main_window.py b/src/tagstudio/qt/views/main_window.py index 14113d9ef..2bdca76b1 100644 --- a/src/tagstudio/qt/views/main_window.py +++ b/src/tagstudio/qt/views/main_window.py @@ -24,11 +24,11 @@ from tagstudio.qt.controllers.preview_panel_controller import PreviewPanel from tagstudio.qt.mixed.landing import LandingWidget from tagstudio.qt.mixed.pagination import Pagination -from tagstudio.qt.views.widgets.search_bar_view import SearchBarWidget from tagstudio.qt.resource_manager import ResourceManager from tagstudio.qt.thumb_grid_layout import ThumbGridLayout -from tagstudio.qt.views.widgets.main_menu_bar_view import MainMenuBar from tagstudio.qt.views.widgets.content_display_toolbar_view import ContentDisplayToolbar +from tagstudio.qt.views.widgets.search_bar_view import SearchBarWidget +from tagstudio.qt.views.widgets.main_menu_bar_view import MainMenuBar # Only import for type checking/autocompletion, will not be imported at runtime. if typing.TYPE_CHECKING: diff --git a/src/tagstudio/qt/views/widgets/content_display_toolbar_view.py b/src/tagstudio/qt/views/widgets/content_display_toolbar_view.py index 1f7c682c0..4a4c5c296 100644 --- a/src/tagstudio/qt/views/widgets/content_display_toolbar_view.py +++ b/src/tagstudio/qt/views/widgets/content_display_toolbar_view.py @@ -1,28 +1,25 @@ -import typing - from PySide6.QtCore import Qt from PySide6.QtGui import QColor from PySide6.QtWidgets import ( - QWidget, - QHBoxLayout, - QHBoxLayout, QCheckBox, QComboBox, + QHBoxLayout, QLabel, - QWidget, QSpacerItem, - QSizePolicy + QSizePolicy, + QWidget ) from tagstudio.core.library.alchemy.enums import SortingModeEnum, TagColorEnum -from tagstudio.qt.translations import Translations -from tagstudio.qt.models.thumb_sizes import THUMB_SIZES from tagstudio.qt.mixed.tag_widget import get_border_color, get_highlight_color, get_text_color from tagstudio.qt.models.palette import ColorType, get_tag_color +from tagstudio.qt.models.thumb_sizes import THUMB_SIZES +from tagstudio.qt.translations import Translations + class ContentDisplayToolbar(QWidget): - show_hidden_entries_widget: QWidget show_hidden_entries_layout: QHBoxLayout + show_hidden_entries_widget: QWidget def __init__(self, parent: QWidget): diff --git a/src/tagstudio/qt/views/widgets/search_bar_view.py b/src/tagstudio/qt/views/widgets/search_bar_view.py index 1a4e6e9af..19374dee9 100644 --- a/src/tagstudio/qt/views/widgets/search_bar_view.py +++ b/src/tagstudio/qt/views/widgets/search_bar_view.py @@ -1,27 +1,18 @@ -import typing - -from PIL import Image, ImageQt - from PIL import Image, ImageQt -from PySide6 import QtCore -from PySide6.QtCore import QMetaObject, QSize, QStringListModel, Qt, QEvent -from PySide6.QtGui import QAction, QColor, QPixmap +from PySide6.QtCore import QSize, QStringListModel, Qt +from PySide6.QtGui import QPixmap from PySide6.QtWidgets import ( - QWidget, - QHBoxLayout, - QLayout, - QPushButton, QCompleter, QHBoxLayout, QLayout, QLineEdit, QPushButton, - QWidget,) - + QWidget, + ) +from tagstudio.qt.helpers.color_overlay import theme_fg_overlay from tagstudio.qt.resource_manager import ResourceManager from tagstudio.qt.translations import Translations -from tagstudio.qt.helpers.color_overlay import theme_fg_overlay class SearchBarWidget(QWidget): back_button: QPushButton