Add an option to expand the tag browser tree to show the item that was current when the library was closed. The implementation provides an API for plugins to get and set the expansion state.

See https://www.mobileread.com/forums/showthread.php?t=362240
This commit is contained in:
Charles Haley 2024-12-16 12:39:09 +00:00
parent 16a5a9abff
commit 2ac8fcb927
6 changed files with 78 additions and 3 deletions

View File

@ -411,6 +411,7 @@ def create_defs():
defs['tag_browser_old_look'] = False defs['tag_browser_old_look'] = False
defs['tag_browser_hide_empty_categories'] = False defs['tag_browser_hide_empty_categories'] = False
defs['tag_browser_always_autocollapse'] = False defs['tag_browser_always_autocollapse'] = False
defs['tag_browser_restore_tree_expansion'] = False
defs['tag_browser_allow_keyboard_focus'] = False defs['tag_browser_allow_keyboard_focus'] = False
defs['book_list_tooltips'] = True defs['book_list_tooltips'] = True
defs['show_layout_buttons'] = False defs['show_layout_buttons'] = False

View File

@ -636,6 +636,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('tag_browser_old_look', gprefs) r('tag_browser_old_look', gprefs)
r('tag_browser_hide_empty_categories', gprefs) r('tag_browser_hide_empty_categories', gprefs)
r('tag_browser_always_autocollapse', gprefs) r('tag_browser_always_autocollapse', gprefs)
r('tag_browser_restore_tree_expansion', gprefs)
r('tag_browser_show_tooltips', gprefs) r('tag_browser_show_tooltips', gprefs)
r('tag_browser_allow_keyboard_focus', gprefs) r('tag_browser_allow_keyboard_focus', gprefs)
r('bd_show_cover', gprefs) r('bd_show_cover', gprefs)

View File

@ -1426,6 +1426,18 @@ also checked then only categories containing a matched item will be shown.</p
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QCheckBox" name="opt_tag_browser_restore_tree_expansion">
<property name="toolTip">
<string>&lt;p&gt;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.&lt;/p&gt;</string>
</property>
<property name="text">
<string>Expand tr&amp;ee to show last used item</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item row="8" column="0" colspan="3"> <item row="8" column="0" colspan="3">

View File

@ -947,8 +947,26 @@ class TagBrowserWidget(QFrame): # {{{
self.tags_view.model().prefs['tag_browser_hide_empty_categories'] ^= True self.tags_view.model().prefs['tag_browser_hide_empty_categories'] ^= True
self.tags_view.recount_with_position_based_index() self.tags_view.recount_with_position_based_index()
def save_state(self): def save_state(self, gprefs_local=None):
gprefs.set('tag browser search box visible', self.toggle_search_button.isChecked()) 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): def toggle_item(self):
self.tags_view.toggle_current_index() self.tags_view.toggle_current_index()

View File

@ -289,6 +289,7 @@ class TagsView(QTreeView): # {{{
self.setDropIndicatorShown(True) self.setDropIndicatorShown(True)
self.setAutoExpandDelay(500) self.setAutoExpandDelay(500)
self.pane_is_visible = False self.pane_is_visible = False
self.current_expansion = None
self.search_icon = QIcon.ic('search.png') self.search_icon = QIcon.ic('search.png')
self.search_copy_icon = QIcon.ic("search_copy_saved.png") self.search_copy_icon = QIcon.ic("search_copy_saved.png")
self.user_category_icon = QIcon.ic('tb_folder.png') self.user_category_icon = QIcon.ic('tb_folder.png')
@ -426,6 +427,7 @@ class TagsView(QTreeView): # {{{
self.refresh_signal_processed = True self.refresh_signal_processed = True
db.add_listener(self.database_changed) db.add_listener(self.database_changed)
self.expanded.connect(self.item_expanded) self.expanded.connect(self.item_expanded)
self.collapsed.connect(self.item_collapsed)
self.collapsed.connect(self.collapse_node_and_children) self.collapsed.connect(self.collapse_node_and_children)
db.data.add_marked_listener(self.marked_change_listener) db.data.add_marked_listener(self.marked_change_listener)
@ -1434,6 +1436,7 @@ class TagsView(QTreeView): # {{{
ci = self.currentIndex() ci = self.currentIndex()
if not ci.isValid(): if not ci.isValid():
ci = self.indexAt(QPoint(10, 10)) 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 use_pos = self._model.use_position_based_index_on_next_recount
self._model.use_position_based_index_on_next_recount = False self._model.use_position_based_index_on_next_recount = False
if use_pos: if use_pos:
@ -1454,6 +1457,10 @@ class TagsView(QTreeView): # {{{
index = self._model.index_for_named_path(path) index = self._model.index_for_named_path(path)
if index.isValid(): if index.isValid():
self.show_item_at_index(index) 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) self.blockSignals(False)
def show_item_at_path(self, path, box=False, def show_item_at_path(self, path, box=False,
@ -1490,5 +1497,26 @@ class TagsView(QTreeView): # {{{
Called by the expanded signal Called by the expanded signal
''' '''
self.setCurrentIndex(idx) 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)
# }}} # }}}

View File

@ -983,6 +983,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
return return
else: else:
return return
self._save_tb_state(gprefs)
for action in self.iactions.values(): for action in self.iactions.values():
try: try:
action.library_about_to_change(olddb, db) 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'): if db.new_api.pref('virtual_lib_on_startup'):
self.apply_virtual_library(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.rebuild_vl_tabs()
self._restore_tb_expansion_state() # Do this before plugins library_changed()
for action in self.iactions.values(): for action in self.iactions.values():
try: try:
action.library_changed(db) action.library_changed(db)
@ -1167,16 +1169,29 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
_('<b>Failed</b>')+': '+str(job.description), _('<b>Failed</b>')+': '+str(job.description),
det_msg=job.details, retry_func=retry_func) 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): def read_settings(self):
self.restore_geometry(gprefs, 'calibre_main_window_geometry', get_legacy_saved_geometry=lambda: config['main_window_geometry']) self.restore_geometry(gprefs, 'calibre_main_window_geometry', get_legacy_saved_geometry=lambda: config['main_window_geometry'])
self.read_layout_settings() self.read_layout_settings()
self._restore_tb_expansion_state()
def write_settings(self): def write_settings(self):
with gprefs: # Only write to gprefs once with gprefs: # Only write to gprefs once
self.save_geometry(gprefs, 'calibre_main_window_geometry') self.save_geometry(gprefs, 'calibre_main_window_geometry')
dynamic.set('sort_history', self.library_view.model().sort_history) dynamic.set('sort_history', self.library_view.model().sort_history)
self.save_layout_state() self.save_layout_state()
self.tb_widget.save_state() self._save_tb_state(gprefs)
def restart(self): def restart(self):
self.quit(restart=True) self.quit(restart=True)