From 5533c9b5d509b615c8a193b37788e5446281138f Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Thu, 9 Jan 2025 16:29:06 +0000 Subject: [PATCH 1/3] All GUI options stuff: 1. Add the option to show the "GUI actions" button in the status bar. The code to add the Preferences button is commented out. 2. Add a shortcut to All GUI Actions: Ctrl+Alt+Shift+G 3. Fix some existing problems in the All GUI Actions implementation. 4. Add the infrastructure for status line buttons (all in init.py) 5. Make using the shortcut for All GUI Actions place the menu over the button in the status bar, if it exists and is the only button/menu item. (actions.__init__.py) --- src/calibre/gui2/__init__.py | 2 + src/calibre/gui2/actions/__init__.py | 7 +++ src/calibre/gui2/actions/all_actions.py | 22 ++++----- src/calibre/gui2/init.py | 32 ++++++++++++ src/calibre/gui2/preferences/look_feel.py | 4 ++ src/calibre/gui2/preferences/look_feel.ui | 60 ++++++++++++++++++++--- 6 files changed, 109 insertions(+), 18 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 9ff3d3a95f1a..c8793f9df630 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -415,6 +415,8 @@ def create_defs(): defs['tag_browser_allow_keyboard_focus'] = False defs['book_list_tooltips'] = True defs['show_layout_buttons'] = False + # defs['show_sb_preference_button'] = False + defs['show_sb_all_actions_button'] = False defs['bd_show_cover'] = True defs['bd_overlay_cover_size'] = False defs['tags_browser_category_icons'] = {} diff --git a/src/calibre/gui2/actions/__init__.py b/src/calibre/gui2/actions/__init__.py index 3c24995491cc..5282d6b91155 100644 --- a/src/calibre/gui2/actions/__init__.py +++ b/src/calibre/gui2/actions/__init__.py @@ -68,6 +68,13 @@ def show_menu_under_widget(gui, menu, action, name): return except Exception: continue + # Is it one of the status bar buttons? + for button in gui.status_bar_extra_buttons: + if name == button.action_name and button.isVisible(): + r = button.geometry() + p = gui.status_bar + menu.exec(p.mapToGlobal(QPoint(r.x()+2, r.height()-2))) + return # No visible button found. Fall back to displaying in upper left corner # of the library view. menu.exec(gui.library_view.mapToGlobal(QPoint(10, 10))) diff --git a/src/calibre/gui2/actions/all_actions.py b/src/calibre/gui2/actions/all_actions.py index 2ef657f8cdb9..58f65c642e1a 100644 --- a/src/calibre/gui2/actions/all_actions.py +++ b/src/calibre/gui2/actions/all_actions.py @@ -14,7 +14,7 @@ class AllGUIActions(InterfaceAction): name = 'All GUI actions' - action_spec = (_('All GUI actions'), 'wizard.png', + action_spec = (_('GUI actions'), 'wizard.png', _("Show a menu of all available GUI and plugin actions.\nThis menu " "is not available when looking at books on a device"), None) @@ -33,10 +33,9 @@ def genesis(self): self.shortcut_action = self.create_menu_action( menu=self.hidden_menu, unique_name='Main window layout', - shortcut=None, - text=_("Save and restore layout item sizes, and add/remove/toggle " - "layout items such as the search bar, tag browser, etc. "), - icon='layout.png', + shortcut='Ctrl+Shift+Alt+G', + text=_("Show a menu of all available GUI and plugin actions."), + icon='wizard.png', triggered=self.show_menu) # We want to show the menu when a shortcut is used. Apparently the only way @@ -59,9 +58,6 @@ def initialization_complete(self): def about_to_show_menu(self): self.populate_menu() - def location_selected(self, loc): - self.qaction.setEnabled(loc == 'library') - def populate_menu(self): # Need to do this on every invocation because shortcuts can change m = self.qaction.menu() @@ -127,13 +123,16 @@ def add_action(menu, display_name): if act.popup_type == QToolButton.ToolButtonPopupMode.MenuButtonPopup: if getattr(act, 'action_add_menu', None) or (getattr(act, 'qaction', None) and act.qaction.menu() and act.qaction.menu().children()): # The action offers both a 'click' and a menu. Use the menu. - menu.addAction(icon, menu_text, partial(self._do_menu, display_name, act)) + ma = menu.addAction(icon, menu_text, partial(self._do_menu, display_name, act)) else: # The action is a dialog. - menu.addAction(act.qaction.icon(), menu_text, partial(self._do_action, act)) + ma = menu.addAction(act.qaction.icon(), menu_text, partial(self._do_action, act)) else: # The action is a menu. - menu.addAction(icon, menu_text, partial(self._do_menu, display_name, act)) + ma = menu.addAction(icon, menu_text, partial(self._do_menu, display_name, act)) + # Disable the menu line if the underlying qaction is disabled. This + # happens when devices are connected and in some other contexts. + ma.setEnabled(act.qaction.isEnabled()) # Finally the real work, building the action menu. Partition long lists # of actions into sublists of some arbitrary length. @@ -166,6 +165,7 @@ def partition(names): partition(builtin_actions) # Add access to the toolbars and keyboard shortcuts preferences dialogs m.addSection(_('Preferences') + ' ') + m.addAction(QIcon.ic('wizard.png'), _('Main dialog'), self.gui.iactions['Preferences'].qaction.trigger) m.addAction(QIcon.ic('wizard.png'), _('Toolbars'), self._do_pref_toolbar) m.addAction(QIcon.ic('keyboard-prefs.png'), _('Keyboard shortcuts'), self._do_pref_shortcuts) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 7d824cb909d6..cb2d1eb23398 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -487,6 +487,29 @@ def restore(self, x): # }}} +class StatusBarButton(QToolButton): + + def __init__(self, parent, action_name, pref_name, on_click): + super().__init__(parent=parent) + act = parent.iactions[action_name] + self.action_name = action_name + self.setCursor(Qt.CursorShape.PointingHandCursor) + self.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) + self.setAutoRaise(True) + self.setIcon(QIcon.ic(act.action_spec[1])) + self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon) + self.setText(act.action_spec[0]) + self.setToolTip(act.action_spec[2]) + self.setVisible(gprefs[pref_name]) + parent.status_bar.addPermanentWidget(self) + if on_click == 'menu': + self.setMenu(act.qaction.menu()) + elif on_click == 'trigger': + self.clicked.connect(act.qaction.trigger) + else: + raise ValueError(f'make_status_line_action_button: invalid on_click ({on_click}') + + class LayoutMixin: # {{{ def __init__(self, *args, **kwargs): @@ -573,6 +596,15 @@ def init_layout_mixin(self): b.setToolTip(_( 'Show and hide various parts of the calibre main window')) self.status_bar.addPermanentWidget(b) + + # These must be after the layout button because it can be expanded into + # the component buttons. Order: last is right-most. + # The preferences status bar button isn't (yet) allowed on the status bar + # self.sb_preferences_button = StatusBarButton(self, 'Preferences', 'show_sb_preference_button', 'trigger') + self.sb_all_gui_actions_button = StatusBarButton(self, 'All GUI actions', + 'show_sb_all_actions_button', 'menu') + self.status_bar_extra_buttons = (self.sb_all_gui_actions_button,) + self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index aa2bac525449..6568558df371 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -632,6 +632,8 @@ def genesis(self, gui): r('dnd_merge', gprefs) r('wrap_toolbar_text', gprefs, restart_required=True) r('show_layout_buttons', gprefs) + r('show_sb_all_actions_button', gprefs) + # r('show_sb_preference_button', gprefs) r('row_numbers_in_book_list', gprefs) r('tag_browser_old_look', gprefs) r('tag_browser_hide_empty_categories', gprefs) @@ -1277,6 +1279,8 @@ def refresh_gui(self, gui): qv = get_quickview_action_plugin() if qv: qv.refill_quickview() + gui.sb_all_gui_actions_button.setVisible(gprefs['show_sb_all_actions_button']) + # gui.sb_preferences_button.setVisible(gprefs['show_sb_preference_button']) if __name__ == '__main__': diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index e105ce3d303c..c2bc90545889 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -41,6 +41,23 @@ + + + + + + Qt::Horizontal + + + + 20 + 1 + + + + + + @@ -190,13 +207,6 @@ - - - - Show &layout buttons in the status bar - - - @@ -267,6 +277,42 @@ + + + + Status bar buttons + + + + + + Show &layout buttons + + + + + + + S&how GUI actions button + + + + + + + Qt::Vertical + + + + 10 + 20 + + + + + + + From 96725ce841c1d42516d6d88e406f994d358436ff Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Thu, 9 Jan 2025 16:32:08 +0000 Subject: [PATCH 2/3] Changed shortcut as suggested. --- src/calibre/gui2/actions/all_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/actions/all_actions.py b/src/calibre/gui2/actions/all_actions.py index 58f65c642e1a..b04eed42532f 100644 --- a/src/calibre/gui2/actions/all_actions.py +++ b/src/calibre/gui2/actions/all_actions.py @@ -33,7 +33,7 @@ def genesis(self): self.shortcut_action = self.create_menu_action( menu=self.hidden_menu, unique_name='Main window layout', - shortcut='Ctrl+Shift+Alt+G', + shortcut='Ctrl+F1', text=_("Show a menu of all available GUI and plugin actions."), icon='wizard.png', triggered=self.show_menu) From 94854384ae97204eda6bb5dff92dd54a37a029fd Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Thu, 9 Jan 2025 18:01:11 +0000 Subject: [PATCH 3/3] Improved partitioning of actions in the menu. --- src/calibre/gui2/actions/all_actions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/actions/all_actions.py b/src/calibre/gui2/actions/all_actions.py index b04eed42532f..aaf2c9463ac3 100644 --- a/src/calibre/gui2/actions/all_actions.py +++ b/src/calibre/gui2/actions/all_actions.py @@ -135,11 +135,11 @@ def add_action(menu, display_name): ma.setEnabled(act.qaction.isEnabled()) # Finally the real work, building the action menu. Partition long lists - # of actions into sublists of some arbitrary length. + # of actions into mostly-equal-length sublists of some arbitrary length. def partition(names): - count_in_partition = 10 # arbitrary - if len(names) >= count_in_partition: - partition_count = len(names) // (count_in_partition - 1) + max_in_partition = 10 # arbitrary + if len(names) >= max_in_partition: + partition_count = ceil(len(names) / max_in_partition) step = int(ceil(len(names) / partition_count)) for first in range(0, len(names), step): last = min(first + step - 1, len(names) - 1)