From dcd10d35913d51fd89c181050ba53901f48dbc39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sim=C3=B3=20Albert=20i=20Beltran?= Date: Thu, 23 May 2024 23:37:35 +0200 Subject: [PATCH] Tag Window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use reusable Tag Window instead of a Popover for each task Signed-off-by: Simó Albert i Beltran --- errands/state.py | 4 + .../widgets/shared/task_toolbar/__init__.py | 1 + .../widgets/shared/task_toolbar/tag_window.py | 139 ++++++++++++++++++ .../widgets/shared/task_toolbar/toolbar.py | 109 +------------- 4 files changed, 148 insertions(+), 105 deletions(-) create mode 100644 errands/widgets/shared/task_toolbar/tag_window.py diff --git a/errands/state.py b/errands/state.py index ee0aaee3..cd9bdc0b 100644 --- a/errands/state.py +++ b/errands/state.py @@ -16,6 +16,7 @@ ErrandsAttachmentsWindow, ErrandsNotesWindow, PriorityWindow, + TagWindow, ) from errands.widgets.sidebar import Sidebar from errands.widgets.tags.tags import Tags @@ -72,6 +73,7 @@ class State: attachments_window: ErrandsAttachmentsWindow | None = None priority_window: PriorityWindow | None = None + tag_window: TagWindow | None = None @classmethod def init(cls) -> None: @@ -80,12 +82,14 @@ def init(cls) -> None: ErrandsDateTimeWindow, ErrandsAttachmentsWindow, PriorityWindow, + TagWindow, ) cls.notes_window = ErrandsNotesWindow() cls.datetime_window = ErrandsDateTimeWindow() cls.attachments_window = ErrandsAttachmentsWindow() cls.priority_window = PriorityWindow() + cls.tag_window = TagWindow() @classmethod def get_task_list(cls, uid: str) -> TaskList: diff --git a/errands/widgets/shared/task_toolbar/__init__.py b/errands/widgets/shared/task_toolbar/__init__.py index 949bc9be..14639e89 100644 --- a/errands/widgets/shared/task_toolbar/__init__.py +++ b/errands/widgets/shared/task_toolbar/__init__.py @@ -5,3 +5,4 @@ from errands.widgets.shared.task_toolbar.datetime_window import ErrandsDateTimeWindow from errands.widgets.shared.task_toolbar.notes_window import ErrandsNotesWindow from errands.widgets.shared.task_toolbar.priority_window import PriorityWindow +from errands.widgets.shared.task_toolbar.tag_window import TagWindow diff --git a/errands/widgets/shared/task_toolbar/tag_window.py b/errands/widgets/shared/task_toolbar/tag_window.py new file mode 100644 index 00000000..2f52e673 --- /dev/null +++ b/errands/widgets/shared/task_toolbar/tag_window.py @@ -0,0 +1,139 @@ +# Copyright 2024 Vlad Krupinskii +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from gi.repository import Adw, Gtk, GObject # type:ignore + +from errands.lib.data import UserData +from errands.lib.logging import Log +from errands.lib.utils import get_children +from errands.lib.sync.sync import Sync +from errands.state import State +from errands.widgets.shared.components.boxes import ErrandsBox +from errands.widgets.shared.components.buttons import ErrandsCheckButton +from errands.widgets.shared.components.toolbar_view import ErrandsToolbarView + +if TYPE_CHECKING: + from errands.widgets.task import Task + from errands.widgets.today.today_task import TodayTask + + +class TagWindow(Adw.Dialog): + def __init__(self): + super().__init__() + self.__build_ui() + + # ------ PRIVATE METHODS ------ # + + def __build_ui(self) -> None: + self.set_follows_content_size(True) + self.set_title(_("Tags")) + + tags_status_page: Adw.StatusPage = Adw.StatusPage( + title=_("No Tags"), + icon_name="errands-info-symbolic", + css_classes=["compact"], + ) + self.tags_list: Gtk.Box = Gtk.Box( + orientation=Gtk.Orientation.VERTICAL, + spacing=6, + margin_bottom=6, + margin_end=6, + margin_start=6, + margin_top=6, + ) + self.tags_list.bind_property( + "visible", + tags_status_page, + "visible", + GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN, + ) + + self.set_child( + ErrandsToolbarView( + top_bars=[Adw.HeaderBar()], + top_bar_style=Adw.ToolbarStyle.RAISED, + content=Gtk.ScrolledWindow( + width_request=200, + propagate_natural_height=True, + propagate_natural_width=True, + vexpand=True, + child=ErrandsBox( + orientation=Gtk.Orientation.VERTICAL, + vexpand=True, + valign=Gtk.Align.CENTER, + children=[self.tags_list, tags_status_page], + ), + ), + ) + ) + + # ------ PUBLIC METHODS ------ # + + def show(self, task: Task | TodayTask): + self.task = task + tags: list[str] = [t.text for t in UserData.tags] + tags_list_items: list[ErrandsToolbarTagsListItem] = get_children(self.tags_list) + tags_list_items_text = [t.title for t in tags_list_items] + + # Remove tags + for item in tags_list_items: + if item.title not in tags: + self.tags_list.remove(item) + + # Add tags + for tag in tags: + if tag not in tags_list_items_text: + self.tags_list.append(ErrandsToolbarTagsListItem(tag, self)) + + # Toggle tags + task_tags: list[str] = [t.title for t in self.task.tags] + tags_items: list[ErrandsToolbarTagsListItem] = get_children(self.tags_list) + for t in tags_items: + t.block_signals = True + t.toggle_btn.set_active(t.title in task_tags) + t.block_signals = False + + self.tags_list.set_visible(len(get_children(self.tags_list)) > 0) + self.present(State.main_window) + + +class ErrandsToolbarTagsListItem(Gtk.Box): + block_signals = False + + def __init__(self, title: str, tag_window: TagWindow) -> None: + super().__init__() + self.set_spacing(6) + self.title = title + self.tag_window = tag_window + self.toggle_btn = ErrandsCheckButton(on_toggle=self.__on_toggle) + self.append(self.toggle_btn) + self.append( + Gtk.Label( + label=title, hexpand=True, halign=Gtk.Align.START, max_width_chars=20 + ) + ) + self.append( + Gtk.Image(icon_name="errands-tag-symbolic", css_classes=["dim-label"]) + ) + + def __on_toggle(self, btn: Gtk.CheckButton) -> None: + if self.block_signals: + return + task = self.tag_window.task + + tags: list[str] = task.task_data.tags + + if btn.get_active(): + if self.title not in tags: + tags.append(self.title) + else: + if self.title in tags: + tags.remove(self.title) + + task.update_props(["tags", "synced"], [tags, False]) + task.update_tags_bar() + Sync.sync() diff --git a/errands/widgets/shared/task_toolbar/toolbar.py b/errands/widgets/shared/task_toolbar/toolbar.py index e77f8d0c..038e8eb3 100644 --- a/errands/widgets/shared/task_toolbar/toolbar.py +++ b/errands/widgets/shared/task_toolbar/toolbar.py @@ -8,10 +8,9 @@ from gi.repository import Adw, Gio, GLib, GObject, Gtk # type:ignore -from errands.lib.data import UserData from errands.lib.logging import Log from errands.lib.sync.sync import Sync -from errands.lib.utils import get_children, get_human_datetime +from errands.lib.utils import get_human_datetime from errands.state import State from errands.widgets.shared.color_selector import ErrandsColorSelector from errands.widgets.shared.components.boxes import ErrandsBox @@ -70,49 +69,13 @@ def __build_ui(self) -> None: ) # Tags button - tags_status_page: Adw.StatusPage = Adw.StatusPage( - title=_("No Tags"), - icon_name="errands-info-symbolic", - css_classes=["compact"], - ) - self.tags_list: Gtk.Box = Gtk.Box( - orientation=Gtk.Orientation.VERTICAL, - spacing=6, - margin_bottom=6, - margin_end=6, - margin_start=6, - margin_top=6, - ) - self.tags_list.bind_property( - "visible", - tags_status_page, - "visible", - GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN, - ) - self.tags_btn: Gtk.MenuButton = Gtk.MenuButton( + tag_btn: ErrandsButton = ErrandsButton( valign=Gtk.Align.CENTER, icon_name="errands-tag-add-symbolic", tooltip_text=_("Tags"), css_classes=["flat"], - popover=Gtk.Popover( - css_classes=["tags-menu"], - child=Gtk.ScrolledWindow( - max_content_width=200, - max_content_height=200, - width_request=200, - propagate_natural_height=True, - propagate_natural_width=True, - vexpand=True, - child=ErrandsBox( - orientation=Gtk.Orientation.VERTICAL, - vexpand=True, - valign=Gtk.Align.CENTER, - children=[self.tags_list, tags_status_page], - ), - ), - ), + on_click=lambda *_: State.tag_window.show(self.task), ) - self.tags_btn.connect("notify::active", self._on_tags_btn_toggled) self.attachments_btn: ErrandsButton = ErrandsButton( tooltip_text=_("Attachments"), @@ -184,7 +147,7 @@ def __build_ui(self) -> None: children=[ self.notes_btn, self.priority_btn, - self.tags_btn, + tag_btn, self.attachments_btn, menu_btn, ], @@ -259,67 +222,3 @@ def _on_menu_toggled(self, btn: Gtk.MenuButton, active: bool) -> None: self.task.block_signals = True self.color_selector.select_color(self.task.task_data.color) self.task.block_signals = False - - def _on_tags_btn_toggled(self, btn: Gtk.MenuButton, *_) -> None: - if not btn.get_active(): - return - tags: list[str] = [t.text for t in UserData.tags] - tags_list_items: list[ErrandsToolbarTagsListItem] = get_children(self.tags_list) - tags_list_items_text = [t.title for t in tags_list_items] - - # Remove tags - for item in tags_list_items: - if item.title not in tags: - self.tags_list.remove(item) - - # Add tags - for tag in tags: - if tag not in tags_list_items_text: - self.tags_list.append(ErrandsToolbarTagsListItem(tag, self.task)) - - # Toggle tags - task_tags: list[str] = [t.title for t in self.task.tags] - tags_items: list[ErrandsToolbarTagsListItem] = get_children(self.tags_list) - for t in tags_items: - t.block_signals = True - t.toggle_btn.set_active(t.title in task_tags) - t.block_signals = False - - self.tags_list.set_visible(len(get_children(self.tags_list)) > 0) - - -class ErrandsToolbarTagsListItem(Gtk.Box): - block_signals = False - - def __init__(self, title: str, task: Task) -> None: - super().__init__() - self.set_spacing(6) - self.title = title - self.task = task - self.toggle_btn = ErrandsCheckButton(on_toggle=self.__on_toggle) - self.append(self.toggle_btn) - self.append( - Gtk.Label( - label=title, hexpand=True, halign=Gtk.Align.START, max_width_chars=20 - ) - ) - self.append( - Gtk.Image(icon_name="errands-tag-symbolic", css_classes=["dim-label"]) - ) - - def __on_toggle(self, btn: Gtk.CheckButton) -> None: - if self.block_signals: - return - - tags: list[str] = self.task.task_data.tags - - if btn.get_active(): - if self.title not in tags: - tags.append(self.title) - else: - if self.title in tags: - tags.remove(self.title) - - self.task.update_props(["tags", "synced"], [tags, False]) - self.task.update_tags_bar() - Sync.sync()