From c8127ee60619a2cde084d91d29d27b872078b72d Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 13 Aug 2017 19:11:40 +1000 Subject: [PATCH] hideable sidebar - rely on restoreGeom() to preserve visibility - case-insensitive sort on tags --- aqt/browser.py | 115 ++++++++++++++++++++++++++++++++++++ designer/browser.ui | 9 +++ designer/icons.qrc | 4 ++ designer/icons/deck.png | Bin 0 -> 1312 bytes designer/icons/heart.png | Bin 0 -> 1788 bytes designer/icons/notetype.png | Bin 0 -> 922 bytes designer/icons/tag.png | Bin 0 -> 1050 bytes 7 files changed, 128 insertions(+) create mode 100644 designer/icons/deck.png create mode 100644 designer/icons/heart.png create mode 100644 designer/icons/notetype.png create mode 100644 designer/icons/tag.png diff --git a/aqt/browser.py b/aqt/browser.py index f8f306b50..23642f8b2 100644 --- a/aqt/browser.py +++ b/aqt/browser.py @@ -373,6 +373,7 @@ class Browser(QMainWindow): self._closeEventHasCleanedUp = False self.form = aqt.forms.browser.Ui_Dialog() self.form.setupUi(self) + self.setupSidebar() restoreGeom(self, "editor", 0) restoreState(self, "editor") restoreSplitter(self.form.splitter, "editor3") @@ -430,6 +431,7 @@ class Browser(QMainWindow): f.actionFind.triggered.connect(self.onFind) f.actionNote.triggered.connect(self.onNote) f.actionTags.triggered.connect(self.onFilterButton) + f.actionSidebar.triggered.connect(self.focusSidebar) f.actionCardList.triggered.connect(self.onCardList) # help f.actionGuide.triggered.connect(self.onHelp) @@ -493,6 +495,8 @@ class Browser(QMainWindow): "Show answer on RET or register answer." if evt.key() == Qt.Key_Escape: self.close() + else: + super().keyPressEvent(evt) def setupColumns(self): self.columns = [ @@ -743,6 +747,109 @@ by clicking on one on the left.""")) def onColumnMoved(self, a, b, c): self.setColumnSizes() + # Sidebar + ###################################################################### + + class CallbackItem(QTreeWidgetItem): + def __init__(self, root, name, onclick, oncollapse=None, expanded=False): + QTreeWidgetItem.__init__(self, root, [name]) + self.setExpanded(expanded) + self.onclick = onclick + self.oncollapse = oncollapse + + class SidebarTreeWidget(QTreeWidget): + def __init__(self): + QTreeWidget.__init__(self) + self.itemClicked.connect(self.onTreeClick) + self.itemExpanded.connect(lambda item: self.onTreeCollapse(item)) + self.itemCollapsed.connect(lambda item: self.onTreeCollapse(item)) + + def keyPressEvent(self, evt): + if evt.key() in (Qt.Key_Return, Qt.Key_Enter): + item = self.currentItem() + self.onTreeClick(item, 0) + else: + super().keyPressEvent(evt) + + def onTreeClick(self, item, col): + if getattr(item, 'onclick', None): + item.onclick() + + def onTreeCollapse(self, item): + if getattr(item, 'oncollapse', None): + item.oncollapse() + + def setupSidebar(self): + dw = self.sidebarDockWidget = QDockWidget(_("Sidebar"), self) + dw.setFeatures(QDockWidget.DockWidgetClosable) + dw.setObjectName("Sidebar") + dw.setAllowedAreas(Qt.LeftDockWidgetArea) + self.sidebarTree = self.SidebarTreeWidget() + self.sidebarTree.mw = self.mw + self.sidebarTree.setFrameShape(QFrame.NoFrame) + self.sidebarTree.header().setVisible(False) + dw.setWidget(self.sidebarTree) + p = QPalette() + p.setColor(QPalette.Base, p.window().color()) + self.sidebarTree.setPalette(p) + self.sidebarTree.setVisible(True) + self.sidebarDockWidget.visibilityChanged.connect(self.onSidebarVisChanged) + + def onSidebarVisChanged(self, visible): + if visible: + self.buildTree() + else: + pass + + def focusSidebar(self): + self.sidebarDockWidget.setVisible(True) + self.sidebarTree.setFocus() + + def maybeRefreshSidebar(self): + if self.sidebarDockWidget.isVisible(): + self.buildTree() + + def buildTree(self): + self.sidebarTree.clear() + root = self.sidebarTree + self._favTree(root) + self._decksTree(root) + self._modelTree(root) + self._userTagTree(root) + self.sidebarTree.setIndentation(15) + + def _favTree(self, root): + saved = self.col.conf.get('savedFilters', {}) + for name, filt in sorted(saved.items()): + item = self.CallbackItem(root, name, lambda s=filt: self.setFilter(s)) + item.setIcon(0, QIcon(":/icons/heart.png")) + + def _userTagTree(self, root): + for t in sorted(self.col.tags.all(), key=lambda t: t.lower()): + item = self.CallbackItem( + root, t, lambda t=t: self.setFilter("tag", t)) + item.setIcon(0, QIcon(":/icons/tag.png")) + + def _decksTree(self, root): + grps = self.col.sched.deckDueTree() + def fillGroups(root, grps, head=""): + for g in grps: + item = self.CallbackItem( + root, g[0], + lambda g=g: self.setFilter("deck", head+g[0]), + lambda g=g: self.mw.col.decks.collapseBrowser(g[1]), + not self.mw.col.decks.get(g[1]).get('browserCollapsed', False)) + item.setIcon(0, QIcon(":/icons/deck.png")) + newhead = head + g[0]+"::" + fillGroups(item, g[5], newhead) + fillGroups(root, grps) + + def _modelTree(self, root): + for m in sorted(self.col.models.all(), key=itemgetter("name")): + mitem = self.CallbackItem( + root, m['name'], lambda m=m: self.setFilter("mid", str(m['id']))) + mitem.setIcon(0, QIcon(":/icons/notetype.png")) + # Filter tree ###################################################################### @@ -758,6 +865,10 @@ by clicking on one on the left.""")) self._addNoteTypeFilters(m) self._addTagFilters(m) + m.addSeparator() + m.addAction(self.sidebarDockWidget.toggleViewAction()) + m.addSeparator() + self._addSavedSearches(m) m.exec_(self.form.filter.mapToGlobal(QPoint(0,0))) @@ -1469,12 +1580,16 @@ update cards set usn=?, mod=?, did=? where id in """ + scids, addHook("reset", self.onReset) addHook("editTimer", self.refreshCurrentCard) addHook("editFocusLost", self.refreshCurrentCardFilter) + for t in "newTag", "newModel", "newDeck": + addHook(t, self.maybeRefreshSidebar) def teardownHooks(self): remHook("reset", self.onReset) remHook("editTimer", self.refreshCurrentCard) remHook("editFocusLost", self.refreshCurrentCardFilter) remHook("undoState", self.onUndoState) + for t in "newTag", "newModel", "newDeck": + remHook(t, self.maybeRefreshSidebar) def onUndoState(self, on): self.form.actionUndo.setEnabled(on) diff --git a/designer/browser.ui b/designer/browser.ui index 8c0af36bc..54c3417c1 100644 --- a/designer/browser.ui +++ b/designer/browser.ui @@ -250,6 +250,7 @@ + @@ -553,6 +554,14 @@ Ctrl+4 + + + Sidebar + + + Ctrl+Shift+R + + diff --git a/designer/icons.qrc b/designer/icons.qrc index c48a65e67..1c30eebd2 100644 --- a/designer/icons.qrc +++ b/designer/icons.qrc @@ -1,5 +1,9 @@ icons/anki.png + icons/tag.png + icons/deck.png + icons/notetype.png + icons/heart.png diff --git a/designer/icons/deck.png b/designer/icons/deck.png new file mode 100644 index 0000000000000000000000000000000000000000..5dd6bced4444647f6e6fbf85112a3550f394895f GIT binary patch literal 1312 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 U+DvOyod5s;07*qoM6N<$g0C>}p#T5? literal 0 HcmV?d00001