mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-07-21 21:16:20 +02:00
refactor(ui): refactor PanelModal and PanelWidget into MVC Modal, ModalView, and ModalContent classes
This commit is contained in:
@@ -73,8 +73,8 @@ class EditFieldTemplateModal(EditFieldTemplateModalView):
|
||||
|
||||
self.name_field.setStyleSheet(line_edit_style() if is_empty else "")
|
||||
|
||||
if self.panel_save_button is not None:
|
||||
self.panel_save_button.setDisabled(is_empty)
|
||||
if self.save_button is not None:
|
||||
self.save_button.setDisabled(is_empty)
|
||||
|
||||
def __on_type_changed(self, index: int):
|
||||
old_type = self.__field_type
|
||||
|
||||
@@ -14,10 +14,11 @@ from tagstudio.core.library.alchemy.fields import BaseFieldTemplate
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.qt.controllers.edit_field_template_modal import EditFieldTemplateModal
|
||||
from tagstudio.qt.controllers.field_template_widget_controller import FieldTemplateWidget
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.controllers.suggest_box import SuggestBox
|
||||
from tagstudio.qt.controllers.underlined_widget import UnderlinedWidget
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal, PanelWidget
|
||||
from tagstudio.qt.views.suggest_box_view import SuggestBoxView
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -61,7 +62,7 @@ class FieldSuggestBox(SuggestBox[BaseFieldTemplate]):
|
||||
# since the user needs to decide what type of field it should be before it's created.
|
||||
query: str = self.layout().search_field.text()
|
||||
panel = EditFieldTemplateModal()
|
||||
modal = PanelModal(
|
||||
modal = Modal(
|
||||
panel,
|
||||
Translations["field_template.new"],
|
||||
Translations["field_template.new"],
|
||||
@@ -76,9 +77,7 @@ class FieldSuggestBox(SuggestBox[BaseFieldTemplate]):
|
||||
@override
|
||||
def _on_item_edit(self, item: BaseFieldTemplate) -> None:
|
||||
panel: EditFieldTemplateModal = EditFieldTemplateModal(item)
|
||||
modal: PanelModal = PanelModal(
|
||||
panel, item.name, Translations["field_template.edit"], is_savable=True
|
||||
)
|
||||
modal: Modal = Modal(panel, item.name, Translations["field_template.edit"], is_savable=True)
|
||||
|
||||
modal.saved.connect(lambda: self._edit_item(panel))
|
||||
modal.show()
|
||||
@@ -124,7 +123,7 @@ class FieldSuggestBox(SuggestBox[BaseFieldTemplate]):
|
||||
)
|
||||
|
||||
@override
|
||||
def _create_item_from_modal(self, edit_item_panel: PanelWidget) -> None:
|
||||
def _create_item_from_modal(self, edit_item_panel: ModalContent) -> None:
|
||||
if isinstance(edit_item_panel, EditFieldTemplateModal):
|
||||
template: BaseFieldTemplate = edit_item_panel.build_field_template()
|
||||
self._lib.add_field_template(template)
|
||||
@@ -135,7 +134,7 @@ class FieldSuggestBox(SuggestBox[BaseFieldTemplate]):
|
||||
self._on_search_query_changed(self.layout().search_field.text())
|
||||
|
||||
@override
|
||||
def _edit_item(self, edit_item_panel: PanelWidget) -> None:
|
||||
def _edit_item(self, edit_item_panel: ModalContent) -> None:
|
||||
if not isinstance(edit_item_panel, EditFieldTemplateModal):
|
||||
return
|
||||
|
||||
|
||||
@@ -13,15 +13,16 @@ from tagstudio.core.library.alchemy.fields import BaseFieldTemplate
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.qt.controllers.edit_field_template_modal import EditFieldTemplateModal
|
||||
from tagstudio.qt.controllers.field_template_widget_controller import FieldTemplateWidget
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.controllers.search_panel_controller import SearchPanel
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.field_template_search_panel_view import FieldTemplateSearchPanelView
|
||||
from tagstudio.qt.views.panel_modal import PanelModal, PanelWidget
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class FieldTemplateSearchModal(PanelModal):
|
||||
class FieldTemplateSearchModal(Modal):
|
||||
def __init__(
|
||||
self,
|
||||
library: Library,
|
||||
@@ -33,11 +34,7 @@ class FieldTemplateSearchModal(PanelModal):
|
||||
is_field_template_chooser,
|
||||
view=FieldTemplateSearchPanelView(is_field_template_chooser),
|
||||
)
|
||||
super().__init__(
|
||||
self.search_panel,
|
||||
Translations["field.add.plural"],
|
||||
is_savable=has_save,
|
||||
)
|
||||
super().__init__(self.search_panel, Translations["field.add.plural"], is_savable=has_save)
|
||||
|
||||
|
||||
class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
@@ -76,7 +73,7 @@ class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
logger.info("[FieldTemplateSearch] Create and Add Field Template", name=query)
|
||||
|
||||
panel: EditFieldTemplateModal = EditFieldTemplateModal()
|
||||
modal: PanelModal = PanelModal(
|
||||
modal: Modal = Modal(
|
||||
panel,
|
||||
Translations["field_template.new"],
|
||||
Translations["field_template.new"],
|
||||
@@ -93,7 +90,7 @@ class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
def on_item_edit(self, item: BaseFieldTemplate) -> None:
|
||||
|
||||
panel: EditFieldTemplateModal = EditFieldTemplateModal(item)
|
||||
modal: PanelModal = PanelModal(
|
||||
modal: Modal = Modal(
|
||||
panel,
|
||||
item.name,
|
||||
Translations["field_template.edit"],
|
||||
@@ -157,7 +154,7 @@ class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
)
|
||||
|
||||
@override
|
||||
def create_item(self, edit_item_panel: PanelWidget, choose_item: bool = False) -> None:
|
||||
def create_item(self, edit_item_panel: ModalContent, choose_item: bool = False) -> None:
|
||||
|
||||
if isinstance(edit_item_panel, EditFieldTemplateModal):
|
||||
template: BaseFieldTemplate = edit_item_panel.build_field_template()
|
||||
@@ -171,7 +168,7 @@ class FieldTemplateSearchPanel(SearchPanel[BaseFieldTemplate]):
|
||||
self.on_search_query_changed(self.get_search_query())
|
||||
|
||||
@override
|
||||
def edit_item(self, edit_item_panel: PanelWidget) -> None:
|
||||
def edit_item(self, edit_item_panel: ModalContent) -> None:
|
||||
if not isinstance(edit_item_panel, EditFieldTemplateModal):
|
||||
return
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
|
||||
import contextlib
|
||||
from typing import Any, override
|
||||
|
||||
import structlog
|
||||
from PySide6 import QtGui
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtWidgets import QWidget
|
||||
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.views.modal_view import ModalView
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class Modal(QWidget):
|
||||
"""A generic modal window widget with common signals and styling."""
|
||||
|
||||
done = Signal()
|
||||
saved = Signal()
|
||||
saved_data = Signal(type(Any))
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
content_widget: ModalContent,
|
||||
title: str = "",
|
||||
window_title: str | None = None,
|
||||
is_savable: bool = False,
|
||||
inline_title: bool = True,
|
||||
):
|
||||
super().__init__()
|
||||
self.setWindowTitle(title if window_title is None else window_title)
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setLayout(
|
||||
ModalView(
|
||||
content_widget=content_widget,
|
||||
title=title,
|
||||
is_savable=is_savable,
|
||||
inline_title=inline_title,
|
||||
)
|
||||
)
|
||||
|
||||
# [Done]
|
||||
# - OR -
|
||||
# [Cancel] [Save]
|
||||
if not is_savable:
|
||||
done_button = self.layout().content_widget.done_button
|
||||
if done_button:
|
||||
done_button.clicked.connect(self.hide)
|
||||
done_button.clicked.connect(self.done.emit)
|
||||
else:
|
||||
cancel_button = self.layout().content_widget.cancel_button
|
||||
if cancel_button:
|
||||
cancel_button.clicked.connect(self.hide)
|
||||
cancel_button.clicked.connect(content_widget.reset)
|
||||
|
||||
save_button = self.layout().content_widget.save_button
|
||||
if save_button:
|
||||
save_button.clicked.connect(self.hide)
|
||||
save_button.clicked.connect(self.saved.emit)
|
||||
save_button.clicked.connect(
|
||||
lambda: self.saved_data.emit(content_widget.saved_data())
|
||||
)
|
||||
|
||||
content_widget.parent_post_init()
|
||||
|
||||
@override
|
||||
def closeEvent(self, event: QtGui.QCloseEvent) -> None:
|
||||
with contextlib.suppress(AttributeError):
|
||||
cancel_button = self.layout().content_widget.cancel_button
|
||||
if cancel_button:
|
||||
cancel_button.click()
|
||||
with contextlib.suppress(AttributeError):
|
||||
done_button = self.layout().content_widget.done_button
|
||||
if done_button:
|
||||
done_button.click()
|
||||
event.accept()
|
||||
|
||||
@override
|
||||
def layout(self) -> ModalView:
|
||||
"""Return the typed layout for this widget."""
|
||||
return super().layout() # pyright: ignore[reportReturnType]
|
||||
@@ -0,0 +1,47 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
|
||||
from typing import Any, override
|
||||
|
||||
import structlog
|
||||
from PySide6 import QtCore, QtGui
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import QPushButton, QWidget
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class ModalContent(QWidget):
|
||||
"""Base class for widgets that go inside a Modal widget."""
|
||||
|
||||
save_button: QPushButton | None = None
|
||||
cancel_button: QPushButton | None = None
|
||||
done_button: QPushButton | None = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def saved_data(self) -> Any: # pyright: ignore[reportExplicitAny]
|
||||
return None
|
||||
|
||||
def reset(self) -> None:
|
||||
pass
|
||||
|
||||
def parent_post_init(self) -> None:
|
||||
pass
|
||||
|
||||
@override
|
||||
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
|
||||
if event.key() == QtCore.Qt.Key.Key_Escape:
|
||||
if self.cancel_button:
|
||||
self.cancel_button.click()
|
||||
elif self.done_button:
|
||||
self.done_button.click()
|
||||
elif event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter:
|
||||
if self.save_button:
|
||||
self.save_button.click()
|
||||
elif self.done_button:
|
||||
self.done_button.click()
|
||||
else: # Other key presses
|
||||
super().keyPressEvent(event)
|
||||
@@ -27,11 +27,11 @@ from tagstudio.core.library.alchemy.models import Entry
|
||||
from tagstudio.core.utils.ffmpeg_status import FfmpegStatus, FfprobeStatus
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.controllers.edit_text_controller import EditText
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.mixed.datetime_picker import DatetimePicker
|
||||
from tagstudio.qt.mixed.field_containers import FieldContainers
|
||||
from tagstudio.qt.mixed.file_attributes import FileAttributeData
|
||||
from tagstudio.qt.translations import FIELD_TYPE_KEYS, Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal
|
||||
from tagstudio.qt.views.preview_panel_view import PreviewPanelView
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -183,7 +183,7 @@ class PreviewPanel(QWidget):
|
||||
field_name_key: str = FIELD_TYPE_KEYS.get(field.class_name, "field_type.unknown")
|
||||
|
||||
if type(field) is TextField:
|
||||
edit_modal = PanelModal(
|
||||
edit_modal = Modal(
|
||||
EditText(field.name, field.value, field.is_multiline),
|
||||
window_title=f"{Translations['field.edit']} ({Translations[field_name_key]})",
|
||||
is_savable=True,
|
||||
@@ -194,7 +194,7 @@ class PreviewPanel(QWidget):
|
||||
)
|
||||
edit_modal.show()
|
||||
elif type(field) is DatetimeField:
|
||||
edit_modal = PanelModal(
|
||||
edit_modal = Modal(
|
||||
DatetimePicker(self._driver, field.name, field.value or dt.now()),
|
||||
window_title=f"{Translations['field.edit']} ({Translations[field_name_key]})",
|
||||
is_savable=True,
|
||||
|
||||
@@ -11,8 +11,8 @@ from PySide6.QtGui import QShowEvent
|
||||
from PySide6.QtWidgets import QVBoxLayout, QWidget
|
||||
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.search_panel_view import SearchPanelView
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
@@ -40,7 +40,7 @@ def _item_name(item: object) -> str:
|
||||
raise AttributeError()
|
||||
|
||||
|
||||
class SearchPanel[T](PanelWidget):
|
||||
class SearchPanel[T](ModalContent):
|
||||
item_chosen = Signal(int)
|
||||
|
||||
def __init__(
|
||||
@@ -234,8 +234,8 @@ class SearchPanel[T](PanelWidget):
|
||||
else:
|
||||
self.view.focus_search_box(select_all=True)
|
||||
|
||||
def create_item(self, edit_item_panel: PanelWidget, choose_item: bool = False) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
def create_item(self, edit_item_panel: ModalContent, choose_item: bool = False) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
raise NotImplementedError()
|
||||
|
||||
def edit_item(self, edit_item_panel: PanelWidget) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
def edit_item(self, edit_item_panel: ModalContent) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -10,8 +10,8 @@ from PySide6.QtWidgets import QGraphicsOpacityEffect, QWidget
|
||||
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.qt.controllers.autofill_line_edit import QtCore, QtGui
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.controllers.underlined_widget import UnderlinedWidget
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
autofill_line_edit_style,
|
||||
autofill_line_edit_top_style,
|
||||
@@ -197,10 +197,10 @@ class SuggestBox[T](QWidget):
|
||||
self.done.emit()
|
||||
self.hide_and_reset()
|
||||
|
||||
def _create_item_from_modal(self, edit_item_panel: PanelWidget) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
def _create_item_from_modal(self, edit_item_panel: ModalContent) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
raise NotImplementedError()
|
||||
|
||||
def _edit_item(self, edit_item_panel: PanelWidget) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
def _edit_item(self, edit_item_panel: ModalContent) -> None: # pyright: ignore[reportUnusedParameter]
|
||||
raise NotImplementedError()
|
||||
|
||||
@override
|
||||
|
||||
@@ -12,8 +12,8 @@ from tagstudio.core.enums import TagClickActionOption
|
||||
from tagstudio.core.library.alchemy.enums import BrowsingState
|
||||
from tagstudio.core.library.alchemy.models import Tag
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.mixed.build_tag import BuildTagPanel
|
||||
from tagstudio.qt.views.panel_modal import PanelModal
|
||||
from tagstudio.qt.views.tag_box_view import TagBoxWidgetView
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -74,7 +74,7 @@ class TagBoxWidget(TagBoxWidgetView):
|
||||
def _on_edit(self, tag: Tag) -> None:
|
||||
build_tag_panel = BuildTagPanel(self.__driver.lib, tag=tag)
|
||||
|
||||
edit_modal = PanelModal(
|
||||
edit_modal = Modal(
|
||||
build_tag_panel,
|
||||
self.__driver.lib.tag_display_name(tag),
|
||||
"Edit Tag",
|
||||
|
||||
@@ -12,18 +12,17 @@ from tagstudio.core.constants import RESERVED_TAG_END, RESERVED_TAG_START
|
||||
from tagstudio.core.library.alchemy.enums import BrowsingState
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.models import Tag
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.controllers.search_panel_controller import SearchPanel
|
||||
from tagstudio.qt.mixed.tag_widget import TagWidget
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal, PanelWidget
|
||||
from tagstudio.qt.views.tag_search_panel_view import TagSearchPanelView
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class TagSearchModal(PanelModal):
|
||||
tsp: "TagSearchPanel"
|
||||
|
||||
class TagSearchModal(Modal):
|
||||
def __init__(
|
||||
self,
|
||||
library: Library,
|
||||
@@ -33,16 +32,9 @@ class TagSearchModal(PanelModal):
|
||||
has_save: bool = False,
|
||||
):
|
||||
self.tsp = TagSearchPanel(
|
||||
library,
|
||||
exclude,
|
||||
is_tag_chooser,
|
||||
view=TagSearchPanelView(is_tag_chooser),
|
||||
)
|
||||
super().__init__(
|
||||
widget=self.tsp,
|
||||
title=title,
|
||||
is_savable=has_save,
|
||||
library, exclude, is_tag_chooser, view=TagSearchPanelView(is_tag_chooser)
|
||||
)
|
||||
super().__init__(content_widget=self.tsp, title=title, is_savable=has_save)
|
||||
|
||||
|
||||
class TagSearchPanel(SearchPanel[Tag]):
|
||||
@@ -82,7 +74,7 @@ class TagSearchPanel(SearchPanel[Tag]):
|
||||
query: str = self.get_search_query()
|
||||
|
||||
panel: BuildTagPanel = BuildTagPanel(self.__lib)
|
||||
modal: PanelModal = PanelModal(
|
||||
modal: Modal = Modal(
|
||||
panel,
|
||||
Translations["tag.new"],
|
||||
Translations["tag.add"] if add_to_entry else Translations["tag.new"],
|
||||
@@ -101,7 +93,7 @@ class TagSearchPanel(SearchPanel[Tag]):
|
||||
from tagstudio.qt.mixed.build_tag import BuildTagPanel # here due to circular imports
|
||||
|
||||
edit_tag_panel: BuildTagPanel = BuildTagPanel(self.__lib, tag=item)
|
||||
edit_tag_modal: PanelModal = PanelModal(
|
||||
edit_tag_modal: Modal = Modal(
|
||||
edit_tag_panel,
|
||||
self.__lib.tag_display_name(item),
|
||||
Translations["tag.edit"],
|
||||
@@ -188,7 +180,7 @@ class TagSearchPanel(SearchPanel[Tag]):
|
||||
tag_widget.search_for_tag_action.setEnabled(False)
|
||||
|
||||
@override
|
||||
def create_item(self, edit_item_panel: PanelWidget, choose_item: bool = False) -> None:
|
||||
def create_item(self, edit_item_panel: ModalContent, choose_item: bool = False) -> None:
|
||||
# TODO: Move this to a top-level import
|
||||
from tagstudio.qt.mixed.build_tag import BuildTagPanel # here due to circular imports
|
||||
|
||||
@@ -206,7 +198,7 @@ class TagSearchPanel(SearchPanel[Tag]):
|
||||
self.on_search_query_changed(self.get_search_query())
|
||||
|
||||
@override
|
||||
def edit_item(self, edit_item_panel: PanelWidget) -> None:
|
||||
def edit_item(self, edit_item_panel: ModalContent) -> None:
|
||||
# TODO: Move this to a top-level import
|
||||
from tagstudio.qt.mixed.build_tag import BuildTagPanel # here due to circular imports
|
||||
|
||||
|
||||
@@ -13,12 +13,13 @@ from PySide6.QtWidgets import QGraphicsOpacityEffect, QWidget
|
||||
from tagstudio.core.library.alchemy.enums import BrowsingState
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.models import Tag
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.controllers.suggest_box import SuggestBox
|
||||
from tagstudio.qt.controllers.underlined_widget import UnderlinedWidget
|
||||
from tagstudio.qt.mixed.build_tag import BuildTagPanel
|
||||
from tagstudio.qt.mixed.tag_widget import TagWidget
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal, PanelWidget
|
||||
from tagstudio.qt.views.suggest_box_view import SuggestBoxView
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -69,7 +70,7 @@ class TagSuggestBox(SuggestBox[Tag]):
|
||||
|
||||
if self._driver.settings.edit_tag_on_create:
|
||||
panel: BuildTagPanel = BuildTagPanel(self._lib)
|
||||
modal: PanelModal = PanelModal(
|
||||
modal: Modal = Modal(
|
||||
panel, Translations["tag.new"], Translations["tag.new"], is_savable=True
|
||||
)
|
||||
if query.strip():
|
||||
@@ -86,7 +87,7 @@ class TagSuggestBox(SuggestBox[Tag]):
|
||||
@override
|
||||
def _on_item_edit(self, item: Tag) -> None:
|
||||
edit_tag_panel: BuildTagPanel = BuildTagPanel(self._lib, tag=item)
|
||||
edit_tag_modal: PanelModal = PanelModal(
|
||||
edit_tag_modal: Modal = Modal(
|
||||
edit_tag_panel,
|
||||
self._lib.tag_display_name(item),
|
||||
Translations["tag.edit"],
|
||||
@@ -146,7 +147,7 @@ class TagSuggestBox(SuggestBox[Tag]):
|
||||
tag_widget.search_for_tag_action.setEnabled(True)
|
||||
|
||||
@override
|
||||
def _create_item_from_modal(self, edit_item_panel: PanelWidget) -> None:
|
||||
def _create_item_from_modal(self, edit_item_panel: ModalContent) -> None:
|
||||
if isinstance(edit_item_panel, BuildTagPanel):
|
||||
tag: Tag = edit_item_panel.build_tag()
|
||||
self._lib.add_tag(
|
||||
@@ -159,7 +160,7 @@ class TagSuggestBox(SuggestBox[Tag]):
|
||||
self._on_search_query_changed(self.layout().search_field.text())
|
||||
|
||||
@override
|
||||
def _edit_item(self, edit_item_panel: PanelWidget) -> None:
|
||||
def _edit_item(self, edit_item_panel: ModalContent) -> None:
|
||||
if not isinstance(edit_item_panel, BuildTagPanel):
|
||||
return
|
||||
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
|
||||
from typing import override
|
||||
|
||||
import structlog
|
||||
from PySide6 import QtCore, QtGui
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtWidgets import (
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QListWidget,
|
||||
QListWidgetItem,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.qt.translations import FIELD_TYPE_KEYS, Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
# NOTE: This class doesn't inherit from PanelWidget? Seems like it predates that system?
|
||||
class AddFieldModal(QWidget):
|
||||
done = Signal(list)
|
||||
|
||||
def __init__(self, library: Library):
|
||||
# [Done]
|
||||
# - OR -
|
||||
# [Cancel] [Save]
|
||||
super().__init__()
|
||||
self.lib = library
|
||||
self.setWindowTitle(Translations["field.add"])
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setMinimumSize(400, 300)
|
||||
self.root_layout = QVBoxLayout(self)
|
||||
self.root_layout.setContentsMargins(6, 6, 6, 6)
|
||||
|
||||
self.title_widget = QLabel(header(Translations["field.add"], 3))
|
||||
self.title_widget.setObjectName("fieldTitle")
|
||||
self.title_widget.setWordWrap(True)
|
||||
self.title_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.list_widget = QListWidget()
|
||||
|
||||
self.button_container = QWidget()
|
||||
self.button_layout = QHBoxLayout(self.button_container)
|
||||
self.button_layout.setContentsMargins(6, 6, 6, 6)
|
||||
self.button_layout.addStretch(1)
|
||||
|
||||
self.cancel_button = QPushButton(Translations["generic.cancel"])
|
||||
self.cancel_button.clicked.connect(self.hide)
|
||||
self.button_layout.addWidget(self.cancel_button)
|
||||
|
||||
self.save_button = QPushButton(Translations["generic.add"])
|
||||
self.save_button.setDefault(True)
|
||||
self.save_button.clicked.connect(self.hide)
|
||||
self.save_button.clicked.connect(
|
||||
lambda: (
|
||||
# get userData for each selected item
|
||||
self.done.emit(self.list_widget.selectedItems())
|
||||
)
|
||||
)
|
||||
self.button_layout.addWidget(self.save_button)
|
||||
|
||||
self.root_layout.addWidget(self.title_widget)
|
||||
self.root_layout.addWidget(self.list_widget)
|
||||
self.root_layout.addStretch(1)
|
||||
self.root_layout.addWidget(self.button_container)
|
||||
|
||||
@override
|
||||
def show(self):
|
||||
self.list_widget.clear()
|
||||
for field_template in self.lib.field_templates:
|
||||
field_name_key: str = FIELD_TYPE_KEYS.get(
|
||||
field_template.class_name, "field_type.unknown"
|
||||
)
|
||||
item = QListWidgetItem(f"{field_template.name} ({Translations[field_name_key]})")
|
||||
item.setData(Qt.ItemDataRole.UserRole, field_template)
|
||||
self.list_widget.addItem(item)
|
||||
self.list_widget.setFocus()
|
||||
self.list_widget.setCurrentRow(0)
|
||||
|
||||
super().show()
|
||||
|
||||
@override
|
||||
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
|
||||
if event.key() == QtCore.Qt.Key.Key_Escape:
|
||||
self.cancel_button.click()
|
||||
elif event.key() in (QtCore.Qt.Key.Key_Enter, QtCore.Qt.Key.Key_Return):
|
||||
self.save_button.click()
|
||||
else: # Other key presses
|
||||
pass
|
||||
return super().keyPressEvent(event)
|
||||
@@ -24,10 +24,10 @@ from tagstudio.core.library.alchemy.enums import TagColorEnum
|
||||
from tagstudio.core.library.alchemy.library import Library, slugify
|
||||
from tagstudio.core.library.alchemy.models import TagColorGroup
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.mixed.tag_color_preview import TagColorPreview
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
checkbox_style,
|
||||
line_edit_style,
|
||||
@@ -37,7 +37,7 @@ from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class BuildColorPanel(PanelWidget):
|
||||
class BuildColorPanel(ModalContent):
|
||||
on_edit = Signal(TagColorGroup)
|
||||
|
||||
def __init__(self, library: Library, color_group: TagColorGroup):
|
||||
@@ -254,8 +254,8 @@ class BuildColorPanel(PanelWidget):
|
||||
self.slug_field.setText(slug)
|
||||
self.update_preview_text()
|
||||
|
||||
if self.panel_save_button is not None:
|
||||
self.panel_save_button.setDisabled(is_name_empty)
|
||||
if self.save_button is not None:
|
||||
self.save_button.setDisabled(is_name_empty)
|
||||
|
||||
def build_color(self) -> tuple[TagColorGroup, TagColorGroup]:
|
||||
name = self.name_field.text()
|
||||
|
||||
@@ -13,14 +13,14 @@ from PySide6.QtWidgets import QLabel, QLineEdit, QVBoxLayout, QWidget
|
||||
from tagstudio.core.constants import RESERVED_NAMESPACE_PREFIX
|
||||
from tagstudio.core.library.alchemy.library import Library, ReservedNamespaceError, slugify
|
||||
from tagstudio.core.library.alchemy.models import Namespace
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import line_edit_style
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class BuildNamespacePanel(PanelWidget):
|
||||
class BuildNamespacePanel(ModalContent):
|
||||
on_edit = Signal(Namespace)
|
||||
|
||||
def __init__(self, library: Library, namespace: Namespace | None = None):
|
||||
@@ -118,8 +118,8 @@ class BuildNamespacePanel(PanelWidget):
|
||||
|
||||
self.slug_field.setText(slug)
|
||||
|
||||
if self.panel_save_button is not None:
|
||||
self.panel_save_button.setDisabled(is_name_empty)
|
||||
if self.save_button is not None:
|
||||
self.save_button.setDisabled(is_name_empty)
|
||||
|
||||
def no_collide(self, slug: str) -> str:
|
||||
"""Return a slug name that's verified not to collide with other known namespace slugs."""
|
||||
|
||||
@@ -28,12 +28,13 @@ from PySide6.QtWidgets import (
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.models import Tag, TagAlias, TagColorGroup
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.controllers.tag_search_panel_controller import TagSearchModal
|
||||
from tagstudio.qt.mixed.tag_color_preview import TagColorPreview
|
||||
from tagstudio.qt.mixed.tag_color_selection import TagColorSelection
|
||||
from tagstudio.qt.mixed.tag_widget import TagWidget
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal, PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
checkbox_style,
|
||||
colored_radio_button_style,
|
||||
@@ -73,7 +74,7 @@ class CustomTableItem(QLineEdit):
|
||||
super().keyPressEvent(arg__1)
|
||||
|
||||
|
||||
class BuildTagPanel(PanelWidget):
|
||||
class BuildTagPanel(ModalContent):
|
||||
on_edit = Signal(Tag)
|
||||
|
||||
def __init__(self, library: Library, tag: Tag | None = None) -> None:
|
||||
@@ -201,7 +202,7 @@ class BuildTagPanel(PanelWidget):
|
||||
self.color_button = TagColorPreview(self.lib, None)
|
||||
self.tag_color_selection = TagColorSelection(self.lib)
|
||||
chose_tag_color_title = Translations["tag.choose_color"]
|
||||
self.choose_color_modal = PanelModal(
|
||||
self.choose_color_modal = Modal(
|
||||
self.tag_color_selection, chose_tag_color_title, chose_tag_color_title
|
||||
)
|
||||
self.choose_color_modal.done.connect(
|
||||
@@ -372,7 +373,7 @@ class BuildTagPanel(PanelWidget):
|
||||
|
||||
def on_parent_tag_edit(tag: Tag) -> None:
|
||||
build_tag_panel = BuildTagPanel(self.lib, tag=tag)
|
||||
edit_modal = PanelModal(
|
||||
edit_modal = Modal(
|
||||
build_tag_panel,
|
||||
self.lib.tag_display_name(tag),
|
||||
"Edit Tag",
|
||||
@@ -422,7 +423,7 @@ class BuildTagPanel(PanelWidget):
|
||||
while self.aliases_table.rowCount() > 0:
|
||||
self.aliases_table.removeRow(0)
|
||||
|
||||
last: QWidget | None = self.panel_save_button
|
||||
last: QWidget | None = self.save_button
|
||||
aliases = list(self.aliases)
|
||||
alias_names = [a.name for a in aliases]
|
||||
sorted_aliases = sorted(aliases, key=lambda x: alias_names[aliases.index(x)])
|
||||
@@ -493,8 +494,8 @@ class BuildTagPanel(PanelWidget):
|
||||
is_empty = not self.name_field.text().strip()
|
||||
self.name_field.setStyleSheet(line_edit_style() if is_empty else "")
|
||||
|
||||
if self.panel_save_button is not None:
|
||||
self.panel_save_button.setDisabled(is_empty)
|
||||
if self.save_button is not None:
|
||||
self.save_button.setDisabled(is_empty)
|
||||
|
||||
def build_tag(self) -> Tag:
|
||||
tag = self.tag
|
||||
@@ -515,8 +516,8 @@ class BuildTagPanel(PanelWidget):
|
||||
self.setTabOrder(self.shorthand_field, self.aliases_add_button)
|
||||
self.setTabOrder(self.aliases_add_button, self.parent_tags_add_button)
|
||||
self.setTabOrder(self.parent_tags_add_button, self.color_button)
|
||||
self.setTabOrder(self.color_button, unwrap(self.panel_cancel_button))
|
||||
self.setTabOrder(unwrap(self.panel_cancel_button), unwrap(self.panel_save_button))
|
||||
self.setTabOrder(unwrap(self.panel_save_button), self.aliases_table.cellWidget(0, 1))
|
||||
self.setTabOrder(self.color_button, unwrap(self.cancel_button))
|
||||
self.setTabOrder(unwrap(self.cancel_button), unwrap(self.save_button))
|
||||
self.setTabOrder(unwrap(self.save_button), self.aliases_table.cellWidget(0, 1))
|
||||
self.name_field.selectAll()
|
||||
self.name_field.setFocus()
|
||||
|
||||
@@ -12,12 +12,12 @@ from PySide6.QtWidgets import QMessageBox, QPushButton
|
||||
from tagstudio.core.constants import RESERVED_NAMESPACE_PREFIX
|
||||
from tagstudio.core.library.alchemy.models import TagColorGroup
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.mixed.build_color import BuildColorPanel
|
||||
from tagstudio.qt.mixed.field_widget import FieldWidget
|
||||
from tagstudio.qt.mixed.tag_color_label import TagColorLabel
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.layouts.flow_layout import FlowLayout
|
||||
from tagstudio.qt.views.panel_modal import PanelModal
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import add_button_style
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -102,7 +102,7 @@ class ColorBoxWidget(FieldWidget):
|
||||
def edit_color(self, color_group: TagColorGroup):
|
||||
build_color_panel = BuildColorPanel(self.lib, color_group)
|
||||
|
||||
self.edit_modal = PanelModal(
|
||||
self.edit_modal = Modal(
|
||||
build_color_panel,
|
||||
"Edit Color",
|
||||
is_savable=True,
|
||||
|
||||
@@ -9,7 +9,7 @@ from typing import cast, override
|
||||
from PySide6.QtCore import QDateTime
|
||||
from PySide6.QtWidgets import QDateTimeEdit, QLineEdit, QVBoxLayout
|
||||
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import title_line_edit_style
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -39,7 +39,7 @@ def qdtf2dtf(dtf: str) -> str:
|
||||
return out
|
||||
|
||||
|
||||
class DatetimePicker(PanelWidget):
|
||||
class DatetimePicker(ModalContent):
|
||||
def __init__(self, driver: "QtDriver", name: str, datetime: dt | str):
|
||||
super().__init__()
|
||||
self.setMinimumSize(300, 60)
|
||||
|
||||
@@ -30,12 +30,12 @@ from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.models import Entry, Tag
|
||||
from tagstudio.core.utils.types import unwrap
|
||||
from tagstudio.qt.controllers.edit_text_controller import EditText
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.controllers.tag_box_controller import TagBoxWidget
|
||||
from tagstudio.qt.mixed.datetime_picker import DatetimePicker
|
||||
from tagstudio.qt.mixed.field_widget import FieldContainer
|
||||
from tagstudio.qt.mixed.text_field import TextContainerWidget
|
||||
from tagstudio.qt.translations import FIELD_TYPE_KEYS, Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import inset_container_style
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -291,7 +291,7 @@ class FieldContainers(QWidget):
|
||||
container.set_inner_widget(inner_widget)
|
||||
|
||||
if not is_mixed:
|
||||
edit_modal = PanelModal(
|
||||
edit_modal = Modal(
|
||||
EditText(field.name, field.value, field.is_multiline),
|
||||
window_title=f"{Translations['field.edit']} ({Translations[field_name_key]})",
|
||||
is_savable=True,
|
||||
@@ -329,7 +329,7 @@ class FieldContainers(QWidget):
|
||||
container.set_inner_widget(inner_widget)
|
||||
|
||||
if not is_mixed:
|
||||
edit_modal = PanelModal(
|
||||
edit_modal = Modal(
|
||||
DatetimePicker(self.driver, field.name, field.value or dt.now()),
|
||||
window_title=f"{Translations['field.edit']} ({Translations[field_name_key]})",
|
||||
is_savable=True,
|
||||
|
||||
@@ -20,6 +20,8 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from tagstudio.core.enums import ShowFilepathOption, TagClickActionOption
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.global_settings import (
|
||||
DEFAULT_CACHED_THUMB_RES,
|
||||
DEFAULT_THUMB_CACHE_SIZE,
|
||||
@@ -30,7 +32,6 @@ from tagstudio.qt.global_settings import (
|
||||
Theme,
|
||||
)
|
||||
from tagstudio.qt.translations import DEFAULT_TRANSLATION, LANGUAGES, Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal, PanelWidget
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from tagstudio.qt.ts_qt import QtDriver
|
||||
@@ -38,7 +39,7 @@ if TYPE_CHECKING:
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class SettingsPanel(PanelWidget):
|
||||
class SettingsPanel(ModalContent):
|
||||
driver: "QtDriver"
|
||||
|
||||
filepath_option_map: dict[ShowFilepathOption, str] = {
|
||||
@@ -427,15 +428,15 @@ class SettingsPanel(PanelWidget):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_modal(cls, driver: "QtDriver") -> PanelModal:
|
||||
def build_modal(cls, driver: "QtDriver") -> Modal:
|
||||
settings_panel = cls(driver)
|
||||
|
||||
modal = PanelModal(
|
||||
widget=settings_panel,
|
||||
modal = Modal(
|
||||
content_widget=settings_panel,
|
||||
window_title=Translations["settings.title"],
|
||||
is_savable=True,
|
||||
)
|
||||
modal.saved.connect(lambda: settings_panel.update_settings(driver))
|
||||
modal.title_widget.setVisible(False)
|
||||
modal.layout().title_label.setVisible(False)
|
||||
|
||||
return modal
|
||||
|
||||
@@ -23,11 +23,11 @@ from PySide6.QtWidgets import (
|
||||
|
||||
from tagstudio.core.constants import RESERVED_NAMESPACE_PREFIX
|
||||
from tagstudio.core.enums import Theme
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.mixed.build_namespace import BuildNamespacePanel
|
||||
from tagstudio.qt.mixed.color_box import ColorBoxWidget
|
||||
from tagstudio.qt.mixed.field_widget import FieldContainer
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelModal
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
@@ -38,7 +38,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class TagColorManager(QWidget):
|
||||
create_namespace_modal: PanelModal | None = None
|
||||
create_namespace_modal: Modal | None = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -174,7 +174,7 @@ class TagColorManager(QWidget):
|
||||
def create_namespace(self):
|
||||
build_namespace_panel = BuildNamespacePanel(self.lib)
|
||||
|
||||
self.create_namespace_modal = PanelModal(
|
||||
self.create_namespace_modal = Modal(
|
||||
build_namespace_panel,
|
||||
Translations["namespace.create.title"],
|
||||
is_savable=True,
|
||||
|
||||
@@ -20,21 +20,22 @@ from PySide6.QtWidgets import (
|
||||
from tagstudio.core.library.alchemy.enums import TagColorEnum
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.models import TagColorGroup
|
||||
from tagstudio.qt.mixed.tag_widget import (
|
||||
get_tag_border_color,
|
||||
get_tag_highlight_color,
|
||||
get_tag_text_color,
|
||||
)
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.models.palette import ColorType, get_tag_color
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.layouts.flow_layout import FlowLayout
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import color_swatch_style, header
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
color_swatch_style,
|
||||
get_tag_border_color,
|
||||
get_tag_highlight_color,
|
||||
get_tag_text_color,
|
||||
header,
|
||||
)
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class TagColorSelection(PanelWidget):
|
||||
class TagColorSelection(ModalContent):
|
||||
def __init__(self, library: Library):
|
||||
super().__init__()
|
||||
self.lib = library
|
||||
|
||||
+16
-16
@@ -61,6 +61,7 @@ from tagstudio.qt.controllers.field_template_search_panel_controller import Fiel
|
||||
from tagstudio.qt.controllers.fix_ignored_modal_controller import FixIgnoredEntriesModal
|
||||
from tagstudio.qt.controllers.ignore_modal_controller import IgnoreModal
|
||||
from tagstudio.qt.controllers.library_info_window_controller import LibraryInfoWindow
|
||||
from tagstudio.qt.controllers.modal import Modal
|
||||
from tagstudio.qt.controllers.tag_search_panel_controller import TagSearchModal
|
||||
from tagstudio.qt.controllers.update_available_message_box import UpdateAvailableMessageBox
|
||||
from tagstudio.qt.global_settings import DEFAULT_GLOBAL_SETTINGS_PATH, GlobalSettings, Theme
|
||||
@@ -84,7 +85,6 @@ from tagstudio.qt.utils.file_deleter import delete_file
|
||||
from tagstudio.qt.utils.function_iterator import FunctionIterator
|
||||
from tagstudio.qt.views.field_template_search_panel_view import FieldTemplateSearchPanelView
|
||||
from tagstudio.qt.views.main_window import MainWindow
|
||||
from tagstudio.qt.views.panel_modal import PanelModal
|
||||
from tagstudio.qt.views.splash import SplashScreen
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
@@ -167,12 +167,12 @@ class QtDriver(DriverMixin, QObject):
|
||||
favorite_updated = Signal(bool)
|
||||
archived_updated = Signal(bool)
|
||||
|
||||
tag_manager_panel: PanelModal | None = None
|
||||
tag_manager: Modal | None = None
|
||||
color_manager_panel: TagColorManager | None = None
|
||||
field_template_manager_panel: PanelModal | None = None
|
||||
ignore_modal: PanelModal | None = None
|
||||
add_tag_modal: PanelModal | None = None
|
||||
add_field_modal: PanelModal | None = None
|
||||
field_template_manager: Modal | None = None
|
||||
ignore_modal: Modal | None = None
|
||||
add_tag_modal: Modal | None = None
|
||||
add_field_modal: Modal | None = None
|
||||
folders_modal: FoldersToTagsModal
|
||||
about_modal: AboutModal
|
||||
unlinked_modal: FixUnlinkedEntriesModal
|
||||
@@ -365,14 +365,14 @@ class QtDriver(DriverMixin, QObject):
|
||||
self.app.setDesktopFileName("tagstudio")
|
||||
|
||||
# Initialize the Tag Manager panel
|
||||
self.tag_manager_panel = TagSearchModal(
|
||||
self.tag_manager = TagSearchModal(
|
||||
self.lib,
|
||||
title=Translations["tag_manager.title"],
|
||||
is_tag_chooser=False,
|
||||
)
|
||||
self.tag_manager_panel.tsp.set_driver(self)
|
||||
self.tag_manager.tsp.set_driver(self)
|
||||
|
||||
self.tag_manager_panel.done.connect(
|
||||
self.tag_manager.done.connect(
|
||||
lambda checked=False: self.main_window.preview_panel.set_selection(
|
||||
self.selected, update_preview=False
|
||||
)
|
||||
@@ -382,8 +382,8 @@ class QtDriver(DriverMixin, QObject):
|
||||
self.color_manager_panel = TagColorManager(self)
|
||||
|
||||
# Initialize the Field Template Manager panel
|
||||
self.field_template_manager_panel = PanelModal(
|
||||
widget=FieldTemplateSearchPanel(
|
||||
self.field_template_manager = Modal(
|
||||
content_widget=FieldTemplateSearchPanel(
|
||||
self.lib,
|
||||
is_field_template_chooser=False,
|
||||
view=FieldTemplateSearchPanelView(is_field_template_chooser=False),
|
||||
@@ -391,7 +391,7 @@ class QtDriver(DriverMixin, QObject):
|
||||
title=Translations["field_template_manager.title"],
|
||||
is_savable=False,
|
||||
)
|
||||
self.field_template_manager_panel.done.connect(
|
||||
self.field_template_manager.done.connect(
|
||||
lambda checked=False: self.main_window.preview_panel.set_selection(
|
||||
self.selected, update_preview=False
|
||||
)
|
||||
@@ -486,14 +486,14 @@ class QtDriver(DriverMixin, QObject):
|
||||
lambda f="": self.delete_files_callback(f)
|
||||
)
|
||||
|
||||
self.main_window.menu_bar.tag_manager_action.triggered.connect(self.tag_manager_panel.show)
|
||||
self.main_window.menu_bar.tag_manager_action.triggered.connect(self.tag_manager.show)
|
||||
|
||||
self.main_window.menu_bar.color_manager_action.triggered.connect(
|
||||
self.color_manager_panel.show
|
||||
)
|
||||
|
||||
self.main_window.menu_bar.field_template_manager_action.triggered.connect(
|
||||
self.field_template_manager_panel.show
|
||||
self.field_template_manager.show
|
||||
)
|
||||
|
||||
# endregion
|
||||
@@ -733,7 +733,7 @@ class QtDriver(DriverMixin, QObject):
|
||||
self.ignore_modal = None
|
||||
|
||||
panel = IgnoreModal(self.lib)
|
||||
self.ignore_modal = PanelModal(
|
||||
self.ignore_modal = Modal(
|
||||
panel,
|
||||
Translations["menu.edit.ignore_files"],
|
||||
is_savable=True,
|
||||
@@ -871,7 +871,7 @@ class QtDriver(DriverMixin, QObject):
|
||||
|
||||
def add_tag_action_callback(self):
|
||||
panel = BuildTagPanel(self.lib)
|
||||
self.modal = PanelModal(
|
||||
self.modal = Modal(
|
||||
panel,
|
||||
Translations["tag.new"],
|
||||
Translations["tag.create"],
|
||||
|
||||
@@ -15,14 +15,14 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from tagstudio.qt.controllers.clickable_label import ClickableLabel
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import checkbox_style
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class EditFieldTemplateModalView(PanelWidget):
|
||||
class EditFieldTemplateModalView(ModalContent):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from tagstudio.qt.controllers.clickable_label import ClickableLabel
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import checkbox_style, title_line_edit_style
|
||||
|
||||
|
||||
class EditTextView(PanelWidget):
|
||||
class EditTextView(ModalContent):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setMinimumSize(480, 240)
|
||||
|
||||
@@ -14,13 +14,13 @@ from PySide6.QtWidgets import (
|
||||
from tagstudio.core.constants import IGNORE_NAME
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.core.library.alchemy.models import Tag
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class IgnoreModalView(PanelWidget):
|
||||
class IgnoreModalView(ModalContent):
|
||||
on_edit = Signal(Tag)
|
||||
|
||||
def __init__(self, library: Library) -> None:
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
|
||||
import structlog
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import QHBoxLayout, QLabel, QPushButton, QVBoxLayout, QWidget
|
||||
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import header
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class ModalView(QVBoxLayout):
|
||||
"""A generic reusable modal panel widget."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
content_widget: ModalContent,
|
||||
title: str = "",
|
||||
is_savable: bool = False,
|
||||
inline_title: bool = True,
|
||||
):
|
||||
super().__init__()
|
||||
self.content_widget = content_widget
|
||||
self.setContentsMargins(6, 6 if inline_title else 12, 6, 6)
|
||||
|
||||
self.button_container = QWidget()
|
||||
self.button_layout = QHBoxLayout(self.button_container)
|
||||
self.button_layout.setContentsMargins(6, 6, 6, 6)
|
||||
self.button_layout.addStretch(1)
|
||||
|
||||
# [Done]
|
||||
# - OR -
|
||||
# [Cancel] [Save]
|
||||
if not is_savable:
|
||||
done_button = QPushButton(Translations["generic.done"])
|
||||
done_button.setAutoDefault(True)
|
||||
self.content_widget.done_button = done_button
|
||||
self.button_layout.addWidget(done_button)
|
||||
else:
|
||||
cancel_button = QPushButton(Translations["generic.cancel"])
|
||||
self.content_widget.cancel_button = cancel_button
|
||||
self.button_layout.addWidget(cancel_button)
|
||||
|
||||
save_button = QPushButton(Translations["generic.save"])
|
||||
save_button.setAutoDefault(True)
|
||||
self.content_widget.save_button = save_button
|
||||
self.button_layout.addWidget(save_button)
|
||||
|
||||
if inline_title:
|
||||
self.title_label = QLabel()
|
||||
self.title_label.setObjectName("fieldTitle")
|
||||
self.title_label.setWordWrap(True)
|
||||
self.title_label.setText(header(title, 3))
|
||||
self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.addWidget(self.title_label)
|
||||
|
||||
self.addWidget(content_widget)
|
||||
self.setStretch(1, 2)
|
||||
self.addWidget(self.button_container)
|
||||
content_widget.parent_post_init()
|
||||
@@ -1,127 +0,0 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
|
||||
import contextlib
|
||||
from typing import Any, override
|
||||
|
||||
import structlog
|
||||
from PySide6 import QtCore, QtGui
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtWidgets import QHBoxLayout, QLabel, QPushButton, QVBoxLayout, QWidget
|
||||
|
||||
from tagstudio.qt.translations import Translations
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class PanelModal(QWidget):
|
||||
"""A generic reusable modal panel widget."""
|
||||
|
||||
done = Signal()
|
||||
saved = Signal()
|
||||
saved_data = Signal(type(Any))
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
widget: "PanelWidget",
|
||||
title: str = "",
|
||||
window_title: str | None = None,
|
||||
is_savable: bool = False,
|
||||
inline_title: bool = True,
|
||||
):
|
||||
# [Done]
|
||||
# - OR -
|
||||
# [Cancel] [Save]
|
||||
super().__init__()
|
||||
self.widget = widget
|
||||
self.setWindowTitle(title if window_title is None else window_title)
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.root_layout = QVBoxLayout(self)
|
||||
self.root_layout.setContentsMargins(6, 0 if inline_title else 12, 6, 6)
|
||||
|
||||
self.button_container = QWidget()
|
||||
self.button_layout = QHBoxLayout(self.button_container)
|
||||
self.button_layout.setContentsMargins(6, 6, 6, 6)
|
||||
self.button_layout.addStretch(1)
|
||||
|
||||
if not is_savable:
|
||||
self.done_button = QPushButton(Translations["generic.done"])
|
||||
self.done_button.setAutoDefault(True)
|
||||
self.done_button.clicked.connect(self.hide)
|
||||
self.done_button.clicked.connect(self.done.emit)
|
||||
self.widget.panel_done_button = self.done_button
|
||||
self.button_layout.addWidget(self.done_button)
|
||||
else:
|
||||
self.cancel_button = QPushButton(Translations["generic.cancel"])
|
||||
self.cancel_button.clicked.connect(self.hide)
|
||||
self.cancel_button.clicked.connect(widget.reset)
|
||||
self.widget.panel_cancel_button = self.cancel_button
|
||||
self.button_layout.addWidget(self.cancel_button)
|
||||
|
||||
self.save_button = QPushButton(Translations["generic.save"])
|
||||
self.save_button.setAutoDefault(True)
|
||||
self.save_button.clicked.connect(self.hide)
|
||||
self.save_button.clicked.connect(self.saved.emit)
|
||||
self.save_button.clicked.connect(lambda: self.saved_data.emit(widget.saved_data()))
|
||||
self.widget.panel_save_button = self.save_button
|
||||
self.button_layout.addWidget(self.save_button)
|
||||
|
||||
if inline_title:
|
||||
self.title_widget = QLabel()
|
||||
self.title_widget.setObjectName("fieldTitle")
|
||||
self.title_widget.setWordWrap(True)
|
||||
self.title_widget.setStyleSheet("font-weight:bold;font-size:14px;padding-top:6px")
|
||||
self.title_widget.setText(title)
|
||||
self.title_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.root_layout.addWidget(self.title_widget)
|
||||
|
||||
self.root_layout.addWidget(widget)
|
||||
widget.parent_modal = self
|
||||
self.root_layout.setStretch(1, 2)
|
||||
self.root_layout.addWidget(self.button_container)
|
||||
widget.parent_post_init()
|
||||
|
||||
@override
|
||||
def closeEvent(self, event: QtGui.QCloseEvent) -> None:
|
||||
with contextlib.suppress(AttributeError):
|
||||
self.cancel_button.click()
|
||||
with contextlib.suppress(AttributeError):
|
||||
self.done_button.click()
|
||||
event.accept()
|
||||
|
||||
|
||||
class PanelWidget(QWidget):
|
||||
"""Used for widgets that go in a modal panel, ex. for editing or searching."""
|
||||
|
||||
parent_modal: PanelModal | None = None
|
||||
panel_save_button: QPushButton | None = None
|
||||
panel_cancel_button: QPushButton | None = None
|
||||
panel_done_button: QPushButton | None = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def saved_data(self) -> Any: # pyright: ignore[reportExplicitAny]
|
||||
return None
|
||||
|
||||
def reset(self) -> None:
|
||||
pass
|
||||
|
||||
def parent_post_init(self) -> None:
|
||||
pass
|
||||
|
||||
@override
|
||||
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
|
||||
if event.key() == QtCore.Qt.Key.Key_Escape:
|
||||
if self.panel_cancel_button:
|
||||
self.panel_cancel_button.click()
|
||||
elif self.panel_done_button:
|
||||
self.panel_done_button.click()
|
||||
elif event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter:
|
||||
if self.panel_save_button:
|
||||
self.panel_save_button.click()
|
||||
elif self.panel_done_button:
|
||||
self.panel_done_button.click()
|
||||
else: # Other key presses
|
||||
super().keyPressEvent(event)
|
||||
@@ -17,15 +17,15 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from tagstudio.core.library.alchemy.library import Library
|
||||
from tagstudio.qt.controllers.modal_content import ModalContent
|
||||
from tagstudio.qt.translations import Translations
|
||||
from tagstudio.qt.views.panel_modal import PanelWidget
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import list_button_style
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from tagstudio.qt.controllers.search_panel_controller import SearchPanel
|
||||
|
||||
|
||||
class SearchPanelView(PanelWidget):
|
||||
class SearchPanelView(ModalContent):
|
||||
def __init__(self, is_chooser: bool) -> None:
|
||||
self.is_chooser: bool = is_chooser
|
||||
super().__init__()
|
||||
|
||||
@@ -4,14 +4,7 @@
|
||||
|
||||
import structlog
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QFrame,
|
||||
QHBoxLayout,
|
||||
QScrollArea,
|
||||
QSizePolicy,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
from PySide6.QtWidgets import QFrame, QHBoxLayout, QScrollArea, QSizePolicy, QVBoxLayout, QWidget
|
||||
|
||||
from tagstudio.qt.controllers.autofill_line_edit import AutofillLineEdit
|
||||
from tagstudio.qt.views.stylesheets.stylesheets import (
|
||||
|
||||
@@ -22,8 +22,9 @@ def test_add_tag_callback(qt_driver: QtDriver):
|
||||
qt_driver.add_tag_action_callback()
|
||||
|
||||
# When
|
||||
assert isinstance(qt_driver.modal.widget, BuildTagPanel)
|
||||
qt_driver.modal.widget.name_field.setText("xxx")
|
||||
build_tag_panel = qt_driver.modal.layout().content_widget
|
||||
assert isinstance(build_tag_panel, BuildTagPanel)
|
||||
build_tag_panel.name_field.setText("xxx")
|
||||
# qt_driver.modal.widget.color_field.setCurrentIndex(1)
|
||||
qt_driver.modal.saved.emit()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user