Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/tagstudio/qt/controllers/edit_field_template_modal.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ def __on_name_changed(self):

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
Expand Down
14 changes: 6 additions & 8 deletions src/tagstudio/qt/controllers/field_suggest_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
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

if typing.TYPE_CHECKING:
from tagstudio.qt.ts_qt import QtDriver
Expand Down Expand Up @@ -58,7 +59,7 @@ def _on_item_create(self) -> None:
# 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"],
Expand All @@ -73,9 +74,7 @@ def _on_item_create(self) -> None:
@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()
Expand Down Expand Up @@ -107,7 +106,6 @@ def _set_item_widget(self, item: BaseFieldTemplate | None, index: int) -> None:

# TODO: Add tabbing to different items, and use underline to indicate which will be added
underlined_widget.toggle_underline(index != 0)

# Disconnect previous callbacks
with catch_warnings(record=True):
field_template_widget.on_edit.disconnect()
Expand All @@ -121,7 +119,7 @@ def _set_item_widget(self, item: BaseFieldTemplate | None, index: int) -> None:
)

@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)
Expand All @@ -132,7 +130,7 @@ def _create_item_from_modal(self, edit_item_panel: PanelWidget) -> None:
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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@
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,
Expand All @@ -33,11 +34,7 @@ def __init__(
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]):
Expand Down Expand Up @@ -76,7 +73,7 @@ def on_item_create(self, add_to_entry: bool = False) -> None:
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"],
Expand All @@ -93,7 +90,7 @@ def on_item_create(self, add_to_entry: bool = False) -> None:
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"],
Expand Down Expand Up @@ -157,7 +154,7 @@ def set_item_widget(self, item: BaseFieldTemplate | None, index: int) -> None:
)

@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()
Expand All @@ -171,7 +168,7 @@ def create_item(self, edit_item_panel: PanelWidget, choose_item: bool = False) -
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

Expand Down
85 changes: 85 additions & 0 deletions src/tagstudio/qt/controllers/modal.py
Original file line number Diff line number Diff line change
@@ -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]
47 changes: 47 additions & 0 deletions src/tagstudio/qt/controllers/modal_content.py
Original file line number Diff line number Diff line change
@@ -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)
6 changes: 3 additions & 3 deletions src/tagstudio/qt/controllers/preview_panel_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
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:
Expand Down Expand Up @@ -183,7 +183,7 @@ def _edit_field(self, entry_id: int, field: BaseField) -> None:
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,
Expand All @@ -194,7 +194,7 @@ def _edit_field(self, entry_id: int, field: BaseField) -> None:
)
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,
Expand Down
8 changes: 4 additions & 4 deletions src/tagstudio/qt/controllers/search_panel_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
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__)
Expand Down Expand Up @@ -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__(
Expand Down Expand Up @@ -234,8 +234,8 @@ def keyPressEvent(self, event: QtGui.QKeyEvent) -> None: # noqa N802
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()
6 changes: 3 additions & 3 deletions src/tagstudio/qt/controllers/suggest_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

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,
Expand Down Expand Up @@ -200,10 +200,10 @@ def _editing_finished_callback(self) -> None:
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
Expand Down
4 changes: 2 additions & 2 deletions src/tagstudio/qt/controllers/tag_box_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
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:
Expand Down Expand Up @@ -74,7 +74,7 @@ def _on_remove(self, tag: Tag) -> None:
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",
Expand Down
Loading
Loading