From 7ee40e3dce3aa832a7c19723aefcbe4804ce9de6 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Tue, 13 Apr 2021 11:05:49 +0200 Subject: [PATCH] Refactor sidebar.py into browser folder --- qt/aqt/browser/__init__.py | 10 + qt/aqt/browser/browser.py | 2 +- qt/aqt/browser/sidebar/__init__.py | 22 ++ qt/aqt/browser/sidebar/item.py | 145 ++++++++ qt/aqt/browser/sidebar/model.py | 110 ++++++ qt/aqt/browser/sidebar/searchbar.py | 50 +++ qt/aqt/browser/sidebar/toolbar.py | 51 +++ .../{sidebar.py => browser/sidebar/tree.py} | 324 +----------------- qt/tools/genhooks_gui.py | 6 +- 9 files changed, 397 insertions(+), 323 deletions(-) create mode 100644 qt/aqt/browser/sidebar/__init__.py create mode 100644 qt/aqt/browser/sidebar/item.py create mode 100644 qt/aqt/browser/sidebar/model.py create mode 100644 qt/aqt/browser/sidebar/searchbar.py create mode 100644 qt/aqt/browser/sidebar/toolbar.py rename qt/aqt/{sidebar.py => browser/sidebar/tree.py} (77%) diff --git a/qt/aqt/browser/__init__.py b/qt/aqt/browser/__init__.py index 56319c6b9..af3113e13 100644 --- a/qt/aqt/browser/__init__.py +++ b/qt/aqt/browser/__init__.py @@ -5,6 +5,16 @@ from __future__ import annotations from .browser import Browser from .dialogs import CardInfoDialog, ChangeModel, FindDupesDialog +from .sidebar import ( + SidebarItem, + SidebarItemType, + SidebarModel, + SidebarSearchBar, + SidebarStage, + SidebarTool, + SidebarToolbar, + SidebarTreeView, +) from .table import ( CardState, Cell, diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index 7dc2db756..d489345d0 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -19,6 +19,7 @@ from anki.tags import MARKED_TAG from anki.utils import ids2str, isMac from aqt import AnkiQt, gui_hooks from aqt.browser.dialogs import CardInfoDialog, ChangeModel, FindDupesDialog +from aqt.browser.sidebar import SidebarTreeView from aqt.browser.table import Table from aqt.editor import Editor from aqt.exporting import ExportDialog @@ -42,7 +43,6 @@ from aqt.operations.tag import ( from aqt.previewer import BrowserPreviewer as PreviewDialog from aqt.previewer import Previewer from aqt.qt import * -from aqt.sidebar import SidebarTreeView from aqt.switch import Switch from aqt.utils import ( HelpPage, diff --git a/qt/aqt/browser/sidebar/__init__.py b/qt/aqt/browser/sidebar/__init__.py new file mode 100644 index 000000000..5eaef6809 --- /dev/null +++ b/qt/aqt/browser/sidebar/__init__.py @@ -0,0 +1,22 @@ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +import sys + +import aqt +from anki.utils import isMac +from aqt.theme import theme_manager + + +def _want_right_border() -> bool: + return not isMac or theme_manager.night_mode + + +from .item import SidebarItem, SidebarItemType +from .model import SidebarModel +from .searchbar import SidebarSearchBar +from .toolbar import SidebarTool, SidebarToolbar +from .tree import SidebarStage, SidebarTreeView + +# alias for the legacy pathname +sys.modules["aqt.sidebar"] = sys.modules["aqt.browser.sidebar"] +aqt.sidebar = sys.modules["aqt.browser.sidebar"] # type: ignore diff --git a/qt/aqt/browser/sidebar/item.py b/qt/aqt/browser/sidebar/item.py new file mode 100644 index 000000000..4c38eece2 --- /dev/null +++ b/qt/aqt/browser/sidebar/item.py @@ -0,0 +1,145 @@ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +from __future__ import annotations + +from enum import Enum, auto +from typing import Callable, Iterable, List, Optional, Union + +from anki.collection import SearchNode +from aqt.theme import ColoredIcon + + +class SidebarItemType(Enum): + ROOT = auto() + SAVED_SEARCH_ROOT = auto() + SAVED_SEARCH = auto() + TODAY_ROOT = auto() + TODAY = auto() + FLAG_ROOT = auto() + FLAG = auto() + CARD_STATE_ROOT = auto() + CARD_STATE = auto() + DECK_ROOT = auto() + DECK_CURRENT = auto() + DECK = auto() + NOTETYPE_ROOT = auto() + NOTETYPE = auto() + NOTETYPE_TEMPLATE = auto() + TAG_ROOT = auto() + TAG_NONE = auto() + TAG = auto() + + CUSTOM = auto() + + @staticmethod + def section_roots() -> Iterable[SidebarItemType]: + return (type for type in SidebarItemType if type.name.endswith("_ROOT")) + + def is_section_root(self) -> bool: + return self in self.section_roots() + + def is_editable(self) -> bool: + return self in ( + SidebarItemType.SAVED_SEARCH, + SidebarItemType.DECK, + SidebarItemType.TAG, + ) + + def is_deletable(self) -> bool: + return self in ( + SidebarItemType.SAVED_SEARCH, + SidebarItemType.DECK, + SidebarItemType.TAG, + ) + + +class SidebarItem: + def __init__( + self, + name: str, + icon: Union[str, ColoredIcon], + search_node: Optional[SearchNode] = None, + on_expanded: Callable[[bool], None] = None, + expanded: bool = False, + item_type: SidebarItemType = SidebarItemType.CUSTOM, + id: int = 0, + name_prefix: str = "", + ) -> None: + self.name = name + self.name_prefix = name_prefix + self.full_name = name_prefix + name + self.icon = icon + self.item_type = item_type + self.id = id + self.search_node = search_node + self.on_expanded = on_expanded + self.children: List["SidebarItem"] = [] + self.tooltip: Optional[str] = None + self._parent_item: Optional["SidebarItem"] = None + self._expanded = expanded + self._row_in_parent: Optional[int] = None + self._search_matches_self = False + self._search_matches_child = False + + def add_child(self, cb: "SidebarItem") -> None: + self.children.append(cb) + cb._parent_item = self + + def add_simple( + self, + name: str, + icon: Union[str, ColoredIcon], + type: SidebarItemType, + search_node: Optional[SearchNode], + ) -> SidebarItem: + "Add child sidebar item, and return it." + item = SidebarItem( + name=name, + icon=icon, + search_node=search_node, + item_type=type, + ) + self.add_child(item) + return item + + @property + def expanded(self) -> bool: + return self._expanded + + @expanded.setter + def expanded(self, expanded: bool) -> None: + if self.expanded != expanded: + self._expanded = expanded + if self.on_expanded: + self.on_expanded(expanded) + + def show_expanded(self, searching: bool) -> bool: + if not searching: + return self.expanded + if self._search_matches_child: + return True + # if search matches top level, expand children one level + return self._search_matches_self and self.item_type.is_section_root() + + def is_highlighted(self) -> bool: + return self._search_matches_self + + def search(self, lowered_text: str) -> bool: + "True if we or child matched." + self._search_matches_self = lowered_text in self.name.lower() + self._search_matches_child = any( + [child.search(lowered_text) for child in self.children] + ) + return self._search_matches_self or self._search_matches_child + + def has_same_id(self, other: SidebarItem) -> bool: + "True if `other` is same type, with same id/name." + if other.item_type == self.item_type: + if self.item_type == SidebarItemType.TAG: + return self.full_name == other.full_name + elif self.item_type == SidebarItemType.SAVED_SEARCH: + return self.name == other.name + else: + return other.id == self.id + + return False diff --git a/qt/aqt/browser/sidebar/model.py b/qt/aqt/browser/sidebar/model.py new file mode 100644 index 000000000..6376f176f --- /dev/null +++ b/qt/aqt/browser/sidebar/model.py @@ -0,0 +1,110 @@ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +from __future__ import annotations + +from typing import cast + +import aqt +from aqt.browser.sidebar.item import SidebarItem +from aqt.qt import * +from aqt.theme import theme_manager + + +class SidebarModel(QAbstractItemModel): + def __init__( + self, sidebar: aqt.browser.sidebar.SidebarTreeView, root: SidebarItem + ) -> None: + super().__init__() + self.sidebar = sidebar + self.root = root + self._cache_rows(root) + + def _cache_rows(self, node: SidebarItem) -> None: + "Cache index of children in parent." + for row, item in enumerate(node.children): + item._row_in_parent = row + self._cache_rows(item) + + def item_for_index(self, idx: QModelIndex) -> SidebarItem: + return idx.internalPointer() + + def index_for_item(self, item: SidebarItem) -> QModelIndex: + return self.createIndex(item._row_in_parent, 0, item) + + def search(self, text: str) -> bool: + return self.root.search(text.lower()) + + # Qt API + ###################################################################### + + def rowCount(self, parent: QModelIndex = QModelIndex()) -> int: + if not parent.isValid(): + return len(self.root.children) + else: + item: SidebarItem = parent.internalPointer() + return len(item.children) + + def columnCount(self, parent: QModelIndex = QModelIndex()) -> int: + return 1 + + def index( + self, row: int, column: int, parent: QModelIndex = QModelIndex() + ) -> QModelIndex: + if not self.hasIndex(row, column, parent): + return QModelIndex() + + parentItem: SidebarItem + if not parent.isValid(): + parentItem = self.root + else: + parentItem = parent.internalPointer() + + item = parentItem.children[row] + return self.createIndex(row, column, item) + + def parent(self, child: QModelIndex) -> QModelIndex: # type: ignore + if not child.isValid(): + return QModelIndex() + + childItem: SidebarItem = child.internalPointer() + parentItem = childItem._parent_item + + if parentItem is None or parentItem == self.root: + return QModelIndex() + + row = parentItem._row_in_parent + + return self.createIndex(row, 0, parentItem) + + def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> QVariant: + if not index.isValid(): + return QVariant() + + if role not in (Qt.DisplayRole, Qt.DecorationRole, Qt.ToolTipRole, Qt.EditRole): + return QVariant() + + item: SidebarItem = index.internalPointer() + + if role in (Qt.DisplayRole, Qt.EditRole): + return QVariant(item.name) + if role == Qt.ToolTipRole: + return QVariant(item.tooltip) + return QVariant(theme_manager.icon_from_resources(item.icon)) + + def setData(self, index: QModelIndex, text: str, _role: int = Qt.EditRole) -> bool: + return self.sidebar._on_rename(index.internalPointer(), text) + + def supportedDropActions(self) -> Qt.DropActions: + return cast(Qt.DropActions, Qt.MoveAction) + + def flags(self, index: QModelIndex) -> Qt.ItemFlags: + if not index.isValid(): + return cast(Qt.ItemFlags, Qt.ItemIsEnabled) + flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled + item: SidebarItem = index.internalPointer() + if item.item_type in self.sidebar.valid_drop_types: + flags |= Qt.ItemIsDropEnabled + if item.item_type.is_editable(): + flags |= Qt.ItemIsEditable + + return cast(Qt.ItemFlags, flags) diff --git a/qt/aqt/browser/sidebar/searchbar.py b/qt/aqt/browser/sidebar/searchbar.py new file mode 100644 index 000000000..56eaebb8d --- /dev/null +++ b/qt/aqt/browser/sidebar/searchbar.py @@ -0,0 +1,50 @@ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +from __future__ import annotations + +import aqt +from aqt import colors +from aqt.browser.sidebar import _want_right_border +from aqt.qt import * +from aqt.theme import theme_manager + + +class SidebarSearchBar(QLineEdit): + def __init__(self, sidebar: aqt.browser.sidebar.SidebarTreeView) -> None: + QLineEdit.__init__(self, sidebar) + self.setPlaceholderText(sidebar.col.tr.browsing_sidebar_filter()) + self.sidebar = sidebar + self.timer = QTimer(self) + self.timer.setInterval(600) + self.timer.setSingleShot(True) + self.setFrame(False) + border = theme_manager.color(colors.MEDIUM_BORDER) + styles = [ + "padding: 1px", + "padding-left: 3px", + f"border-bottom: 1px solid {border}", + ] + if _want_right_border(): + styles.append( + f"border-right: 1px solid {border}", + ) + + self.setStyleSheet("QLineEdit { %s }" % ";".join(styles)) + + qconnect(self.timer.timeout, self.onSearch) + qconnect(self.textChanged, self.onTextChanged) + + def onTextChanged(self, text: str) -> None: + if not self.timer.isActive(): + self.timer.start() + + def onSearch(self) -> None: + self.sidebar.search_for(self.text()) + + def keyPressEvent(self, evt: QKeyEvent) -> None: + if evt.key() in (Qt.Key_Up, Qt.Key_Down): + self.sidebar.setFocus() + elif evt.key() in (Qt.Key_Enter, Qt.Key_Return): + self.onSearch() + else: + QLineEdit.keyPressEvent(self, evt) diff --git a/qt/aqt/browser/sidebar/toolbar.py b/qt/aqt/browser/sidebar/toolbar.py new file mode 100644 index 000000000..ee2d26618 --- /dev/null +++ b/qt/aqt/browser/sidebar/toolbar.py @@ -0,0 +1,51 @@ +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +from __future__ import annotations + +from enum import Enum, auto +from typing import Callable, Tuple + +import aqt +from aqt.qt import * +from aqt.theme import theme_manager +from aqt.utils import tr + + +class SidebarTool(Enum): + SELECT = auto() + SEARCH = auto() + + +class SidebarToolbar(QToolBar): + _tools: Tuple[Tuple[SidebarTool, str, Callable[[], str]], ...] = ( + (SidebarTool.SEARCH, ":/icons/magnifying_glass.svg", tr.actions_search), + (SidebarTool.SELECT, ":/icons/select.svg", tr.actions_select), + ) + + def __init__(self, sidebar: aqt.browser.sidebar.SidebarTreeView) -> None: + super().__init__() + self.sidebar = sidebar + self._action_group = QActionGroup(self) + qconnect(self._action_group.triggered, self._on_action_group_triggered) + self._setup_tools() + self.setIconSize(QSize(16, 16)) + self.setStyle(QStyleFactory.create("fusion")) + + def _setup_tools(self) -> None: + for row, tool in enumerate(self._tools): + action = self.addAction( + theme_manager.icon_from_resources(tool[1]), tool[2]() + ) + action.setCheckable(True) + action.setShortcut(f"Alt+{row + 1}") + self._action_group.addAction(action) + saved = self.sidebar.col.get_config("sidebarTool", 0) + active = saved if saved < len(self._tools) else 0 + self._action_group.actions()[active].setChecked(True) + self.sidebar.tool = self._tools[active][0] + + def _on_action_group_triggered(self, action: QAction) -> None: + index = self._action_group.actions().index(action) + self.sidebar.col.set_config("sidebarTool", index) + self.sidebar.tool = self._tools[index][0] diff --git a/qt/aqt/sidebar.py b/qt/aqt/browser/sidebar/tree.py similarity index 77% rename from qt/aqt/sidebar.py rename to qt/aqt/browser/sidebar/tree.py index 2a9c78bea..15f6b5af7 100644 --- a/qt/aqt/sidebar.py +++ b/qt/aqt/browser/sidebar/tree.py @@ -1,6 +1,5 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - from __future__ import annotations from enum import Enum, auto @@ -14,6 +13,11 @@ from anki.notes import Note from anki.tags import TagTreeNode from anki.types import assert_exhaustive from aqt import colors, gui_hooks +from aqt.browser.sidebar import _want_right_border +from aqt.browser.sidebar.item import SidebarItem, SidebarItemType +from aqt.browser.sidebar.model import SidebarModel +from aqt.browser.sidebar.searchbar import SidebarSearchBar +from aqt.browser.sidebar.toolbar import SidebarTool, SidebarToolbar from aqt.clayout import CardLayout from aqt.models import Models from aqt.operations.deck import ( @@ -33,55 +37,6 @@ from aqt.theme import ColoredIcon, theme_manager from aqt.utils import KeyboardModifiersPressed, askUser, getOnlyText, showWarning, tr -class SidebarTool(Enum): - SELECT = auto() - SEARCH = auto() - - -class SidebarItemType(Enum): - ROOT = auto() - SAVED_SEARCH_ROOT = auto() - SAVED_SEARCH = auto() - TODAY_ROOT = auto() - TODAY = auto() - FLAG_ROOT = auto() - FLAG = auto() - CARD_STATE_ROOT = auto() - CARD_STATE = auto() - DECK_ROOT = auto() - DECK_CURRENT = auto() - DECK = auto() - NOTETYPE_ROOT = auto() - NOTETYPE = auto() - NOTETYPE_TEMPLATE = auto() - TAG_ROOT = auto() - TAG_NONE = auto() - TAG = auto() - - CUSTOM = auto() - - @staticmethod - def section_roots() -> Iterable[SidebarItemType]: - return (type for type in SidebarItemType if type.name.endswith("_ROOT")) - - def is_section_root(self) -> bool: - return self in self.section_roots() - - def is_editable(self) -> bool: - return self in ( - SidebarItemType.SAVED_SEARCH, - SidebarItemType.DECK, - SidebarItemType.TAG, - ) - - def is_deletable(self) -> bool: - return self in ( - SidebarItemType.SAVED_SEARCH, - SidebarItemType.DECK, - SidebarItemType.TAG, - ) - - class SidebarStage(Enum): ROOT = auto() SAVED_SEARCHES = auto() @@ -93,275 +48,6 @@ class SidebarStage(Enum): TAGS = auto() -class SidebarItem: - def __init__( - self, - name: str, - icon: Union[str, ColoredIcon], - search_node: Optional[SearchNode] = None, - on_expanded: Callable[[bool], None] = None, - expanded: bool = False, - item_type: SidebarItemType = SidebarItemType.CUSTOM, - id: int = 0, - name_prefix: str = "", - ) -> None: - self.name = name - self.name_prefix = name_prefix - self.full_name = name_prefix + name - self.icon = icon - self.item_type = item_type - self.id = id - self.search_node = search_node - self.on_expanded = on_expanded - self.children: List["SidebarItem"] = [] - self.tooltip: Optional[str] = None - self._parent_item: Optional["SidebarItem"] = None - self._expanded = expanded - self._row_in_parent: Optional[int] = None - self._search_matches_self = False - self._search_matches_child = False - - def add_child(self, cb: "SidebarItem") -> None: - self.children.append(cb) - cb._parent_item = self - - def add_simple( - self, - name: str, - icon: Union[str, ColoredIcon], - type: SidebarItemType, - search_node: Optional[SearchNode], - ) -> SidebarItem: - "Add child sidebar item, and return it." - item = SidebarItem( - name=name, - icon=icon, - search_node=search_node, - item_type=type, - ) - self.add_child(item) - return item - - @property - def expanded(self) -> bool: - return self._expanded - - @expanded.setter - def expanded(self, expanded: bool) -> None: - if self.expanded != expanded: - self._expanded = expanded - if self.on_expanded: - self.on_expanded(expanded) - - def show_expanded(self, searching: bool) -> bool: - if not searching: - return self.expanded - if self._search_matches_child: - return True - # if search matches top level, expand children one level - return self._search_matches_self and self.item_type.is_section_root() - - def is_highlighted(self) -> bool: - return self._search_matches_self - - def search(self, lowered_text: str) -> bool: - "True if we or child matched." - self._search_matches_self = lowered_text in self.name.lower() - self._search_matches_child = any( - [child.search(lowered_text) for child in self.children] - ) - return self._search_matches_self or self._search_matches_child - - def has_same_id(self, other: SidebarItem) -> bool: - "True if `other` is same type, with same id/name." - if other.item_type == self.item_type: - if self.item_type == SidebarItemType.TAG: - return self.full_name == other.full_name - elif self.item_type == SidebarItemType.SAVED_SEARCH: - return self.name == other.name - else: - return other.id == self.id - - return False - - -class SidebarModel(QAbstractItemModel): - def __init__(self, sidebar: SidebarTreeView, root: SidebarItem) -> None: - super().__init__() - self.sidebar = sidebar - self.root = root - self._cache_rows(root) - - def _cache_rows(self, node: SidebarItem) -> None: - "Cache index of children in parent." - for row, item in enumerate(node.children): - item._row_in_parent = row - self._cache_rows(item) - - def item_for_index(self, idx: QModelIndex) -> SidebarItem: - return idx.internalPointer() - - def index_for_item(self, item: SidebarItem) -> QModelIndex: - return self.createIndex(item._row_in_parent, 0, item) - - def search(self, text: str) -> bool: - return self.root.search(text.lower()) - - # Qt API - ###################################################################### - - def rowCount(self, parent: QModelIndex = QModelIndex()) -> int: - if not parent.isValid(): - return len(self.root.children) - else: - item: SidebarItem = parent.internalPointer() - return len(item.children) - - def columnCount(self, parent: QModelIndex = QModelIndex()) -> int: - return 1 - - def index( - self, row: int, column: int, parent: QModelIndex = QModelIndex() - ) -> QModelIndex: - if not self.hasIndex(row, column, parent): - return QModelIndex() - - parentItem: SidebarItem - if not parent.isValid(): - parentItem = self.root - else: - parentItem = parent.internalPointer() - - item = parentItem.children[row] - return self.createIndex(row, column, item) - - def parent(self, child: QModelIndex) -> QModelIndex: # type: ignore - if not child.isValid(): - return QModelIndex() - - childItem: SidebarItem = child.internalPointer() - parentItem = childItem._parent_item - - if parentItem is None or parentItem == self.root: - return QModelIndex() - - row = parentItem._row_in_parent - - return self.createIndex(row, 0, parentItem) - - def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> QVariant: - if not index.isValid(): - return QVariant() - - if role not in (Qt.DisplayRole, Qt.DecorationRole, Qt.ToolTipRole, Qt.EditRole): - return QVariant() - - item: SidebarItem = index.internalPointer() - - if role in (Qt.DisplayRole, Qt.EditRole): - return QVariant(item.name) - if role == Qt.ToolTipRole: - return QVariant(item.tooltip) - return QVariant(theme_manager.icon_from_resources(item.icon)) - - def setData(self, index: QModelIndex, text: str, _role: int = Qt.EditRole) -> bool: - return self.sidebar._on_rename(index.internalPointer(), text) - - def supportedDropActions(self) -> Qt.DropActions: - return cast(Qt.DropActions, Qt.MoveAction) - - def flags(self, index: QModelIndex) -> Qt.ItemFlags: - if not index.isValid(): - return cast(Qt.ItemFlags, Qt.ItemIsEnabled) - flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled - item: SidebarItem = index.internalPointer() - if item.item_type in self.sidebar.valid_drop_types: - flags |= Qt.ItemIsDropEnabled - if item.item_type.is_editable(): - flags |= Qt.ItemIsEditable - - return cast(Qt.ItemFlags, flags) - - -class SidebarToolbar(QToolBar): - _tools: Tuple[Tuple[SidebarTool, str, Callable[[], str]], ...] = ( - (SidebarTool.SEARCH, ":/icons/magnifying_glass.svg", tr.actions_search), - (SidebarTool.SELECT, ":/icons/select.svg", tr.actions_select), - ) - - def __init__(self, sidebar: SidebarTreeView) -> None: - super().__init__() - self.sidebar = sidebar - self._action_group = QActionGroup(self) - qconnect(self._action_group.triggered, self._on_action_group_triggered) - self._setup_tools() - self.setIconSize(QSize(16, 16)) - self.setStyle(QStyleFactory.create("fusion")) - - def _setup_tools(self) -> None: - for row, tool in enumerate(self._tools): - action = self.addAction( - theme_manager.icon_from_resources(tool[1]), tool[2]() - ) - action.setCheckable(True) - action.setShortcut(f"Alt+{row + 1}") - self._action_group.addAction(action) - saved = self.sidebar.col.get_config("sidebarTool", 0) - active = saved if saved < len(self._tools) else 0 - self._action_group.actions()[active].setChecked(True) - self.sidebar.tool = self._tools[active][0] - - def _on_action_group_triggered(self, action: QAction) -> None: - index = self._action_group.actions().index(action) - self.sidebar.col.set_config("sidebarTool", index) - self.sidebar.tool = self._tools[index][0] - - -class SidebarSearchBar(QLineEdit): - def __init__(self, sidebar: SidebarTreeView) -> None: - QLineEdit.__init__(self, sidebar) - self.setPlaceholderText(sidebar.col.tr.browsing_sidebar_filter()) - self.sidebar = sidebar - self.timer = QTimer(self) - self.timer.setInterval(600) - self.timer.setSingleShot(True) - self.setFrame(False) - border = theme_manager.color(colors.MEDIUM_BORDER) - styles = [ - "padding: 1px", - "padding-left: 3px", - f"border-bottom: 1px solid {border}", - ] - if _want_right_border(): - styles.append( - f"border-right: 1px solid {border}", - ) - - self.setStyleSheet("QLineEdit { %s }" % ";".join(styles)) - - qconnect(self.timer.timeout, self.onSearch) - qconnect(self.textChanged, self.onTextChanged) - - def onTextChanged(self, text: str) -> None: - if not self.timer.isActive(): - self.timer.start() - - def onSearch(self) -> None: - self.sidebar.search_for(self.text()) - - def keyPressEvent(self, evt: QKeyEvent) -> None: - if evt.key() in (Qt.Key_Up, Qt.Key_Down): - self.sidebar.setFocus() - elif evt.key() in (Qt.Key_Enter, Qt.Key_Return): - self.onSearch() - else: - QLineEdit.keyPressEvent(self, evt) - - -def _want_right_border() -> bool: - return not isMac or theme_manager.night_mode - - # fixme: we should have a top-level Sidebar class inheriting from QWidget that # handles the treeview, search bar and so on. Currently the treeview embeds the # search bar which is wrong, and the layout code is handled in browser.py instead diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index 19d7a62f0..5e3247975 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -343,8 +343,8 @@ hooks = [ name="browser_will_build_tree", args=[ "handled: bool", - "tree: aqt.sidebar.SidebarItem", - "stage: aqt.sidebar.SidebarStage", + "tree: aqt.browser.SidebarItem", + "stage: aqt.browser.SidebarStage", "browser: aqt.browser.Browser", ], return_type="bool", @@ -355,7 +355,7 @@ hooks = [ 'stage' is an enum describing the different construction stages of the sidebar tree at which you can interject your changes. The different values can be inspected by looking at - aqt.sidebar.SidebarStage. + aqt.browser.SidebarStage. If you want Anki to proceed with the construction of the tree stage in question after your have performed your changes or additions,