diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 3e32c6b58f..9ff3d3a95f 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -411,6 +411,7 @@ def create_defs(): defs['tag_browser_old_look'] = False defs['tag_browser_hide_empty_categories'] = False defs['tag_browser_always_autocollapse'] = False + defs['tag_browser_restore_tree_expansion'] = False defs['tag_browser_allow_keyboard_focus'] = False defs['book_list_tooltips'] = True defs['show_layout_buttons'] = False diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 4e17ca4ab2..aa2bac5254 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -636,6 +636,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('tag_browser_old_look', gprefs) r('tag_browser_hide_empty_categories', gprefs) r('tag_browser_always_autocollapse', gprefs) + r('tag_browser_restore_tree_expansion', gprefs) r('tag_browser_show_tooltips', gprefs) r('tag_browser_allow_keyboard_focus', gprefs) r('bd_show_cover', gprefs) diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index d2d24ce76d..5801c552de 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -1426,6 +1426,18 @@ also checked then only categories containing a matched item will be shown.</p + + + + <p>When checked, when a library is opened the Tag browser +will expand the tree so that the last used item is visible. An item is "used" when +it is expanded, collapsed, or clicked.</p> + + + Expand tr&ee to show last used item + + + diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index 4de70a6701..2216471635 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -947,8 +947,26 @@ class TagBrowserWidget(QFrame): # {{{ self.tags_view.model().prefs['tag_browser_hide_empty_categories'] ^= True self.tags_view.recount_with_position_based_index() - def save_state(self): - gprefs.set('tag browser search box visible', self.toggle_search_button.isChecked()) + def save_state(self, gprefs_local=None): + if gprefs_local is None: + gprefs_local = gprefs + gprefs_local.set('tag browser search box visible', self.toggle_search_button.isChecked()) + + def restore_expansion_state(self, state): + ''' + Expands the tag browser tree so that the node specified in state is + visible. Use get_expansion_state() to get the state. The intent is that + a plugin could restore the state in the library_changed() method. + ''' + if state is not None: + self.tags_view.restore_expansion(state) + + def get_expansion_state(self): + ''' + Returns the currently expanded node in the tag browser as a string + suitable for restoring using restore_expansion_state. + ''' + return self.tags_view.current_expansion def toggle_item(self): self.tags_view.toggle_current_index() diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py index 5d6d421795..1271509da3 100644 --- a/src/calibre/gui2/tag_browser/view.py +++ b/src/calibre/gui2/tag_browser/view.py @@ -289,6 +289,7 @@ class TagsView(QTreeView): # {{{ self.setDropIndicatorShown(True) self.setAutoExpandDelay(500) self.pane_is_visible = False + self.current_expansion = None self.search_icon = QIcon.ic('search.png') self.search_copy_icon = QIcon.ic("search_copy_saved.png") self.user_category_icon = QIcon.ic('tb_folder.png') @@ -426,6 +427,7 @@ class TagsView(QTreeView): # {{{ self.refresh_signal_processed = True db.add_listener(self.database_changed) self.expanded.connect(self.item_expanded) + self.collapsed.connect(self.item_collapsed) self.collapsed.connect(self.collapse_node_and_children) db.data.add_marked_listener(self.marked_change_listener) @@ -1434,6 +1436,7 @@ class TagsView(QTreeView): # {{{ ci = self.currentIndex() if not ci.isValid(): ci = self.indexAt(QPoint(10, 10)) + item_is_expanded = True if ci.isValid() and self.isExpanded(ci) else False use_pos = self._model.use_position_based_index_on_next_recount self._model.use_position_based_index_on_next_recount = False if use_pos: @@ -1454,6 +1457,10 @@ class TagsView(QTreeView): # {{{ index = self._model.index_for_named_path(path) if index.isValid(): self.show_item_at_index(index) + if not item_is_expanded: + # show_item_at_index() will expand the target node. + # Collapse it if it wasn't expanded before the recount. + self.collapse(index) self.blockSignals(False) def show_item_at_path(self, path, box=False, @@ -1490,5 +1497,26 @@ class TagsView(QTreeView): # {{{ Called by the expanded signal ''' self.setCurrentIndex(idx) + self.current_expansion = (self.isExpanded(idx), self._model.named_path_for_index(idx)) + + def item_collapsed(self, idx): + ''' + Called by the collapsed signal + ''' + self.current_expansion = (self.isExpanded(idx), self._model.named_path_for_index(idx)) + + def currentChanged(self, idx, prev_idx): + self.current_expansion = (self.isExpanded(idx), self._model.named_path_for_index(idx)) + super().currentChanged(idx, prev_idx) + + def restore_expansion(self, expansion): + self.current_expansion = None + if expansion is not None and len(expansion) == 2: + idx = self._model.index_for_named_path(expansion[1]) + if idx.isValid(): + self.show_item_at_index(idx) + if not expansion[0]: + self.collapse(idx) + # }}} diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index b2b520f70e..4f3394b91f 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -983,6 +983,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ return else: return + self._save_tb_state(gprefs) for action in self.iactions.values(): try: action.library_about_to_change(olddb, db) @@ -1009,6 +1010,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ if db.new_api.pref('virtual_lib_on_startup'): self.apply_virtual_library(db.new_api.pref('virtual_lib_on_startup')) self.rebuild_vl_tabs() + self._restore_tb_expansion_state() # Do this before plugins library_changed() for action in self.iactions.values(): try: action.library_changed(db) @@ -1167,16 +1169,29 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ _('Failed')+': '+str(job.description), det_msg=job.details, retry_func=retry_func) + def _save_tb_state(self, gprefs): + self.tb_widget.save_state(gprefs) + if gprefs['tag_browser_restore_tree_expansion']: + tv_saved_expansions = gprefs.get('tags_view_saved_expansions', {}) + tv_saved_expansions.update({self.current_db.library_id: self.tb_widget.get_expansion_state()}) + gprefs['tags_view_saved_expansions'] = tv_saved_expansions + + def _restore_tb_expansion_state(self): + if gprefs['tag_browser_restore_tree_expansion']: + tv_saved_expansions = gprefs.get('tags_view_saved_expansions', {}) + self.tb_widget.restore_expansion_state(tv_saved_expansions.get(self.current_db.library_id)) + def read_settings(self): self.restore_geometry(gprefs, 'calibre_main_window_geometry', get_legacy_saved_geometry=lambda: config['main_window_geometry']) self.read_layout_settings() + self._restore_tb_expansion_state() def write_settings(self): with gprefs: # Only write to gprefs once self.save_geometry(gprefs, 'calibre_main_window_geometry') dynamic.set('sort_history', self.library_view.model().sort_history) self.save_layout_state() - self.tb_widget.save_state() + self._save_tb_state(gprefs) def restart(self): self.quit(restart=True)