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 9ff3d3a95f..c8793f9df6 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 3c24995491..5282d6b911 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 2ef657f8cd..58f65c642e 100644 --- a/src/calibre/gui2/actions/all_actions.py +++ b/src/calibre/gui2/actions/all_actions.py @@ -14,7 +14,7 @@ from calibre.utils.icu import sort_key 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 @@ class AllGUIActions(InterfaceAction): 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 @@ class AllGUIActions(InterfaceAction): 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 @@ class AllGUIActions(InterfaceAction): 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 @@ class AllGUIActions(InterfaceAction): 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 7d824cb909..cb2d1eb233 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -487,6 +487,29 @@ class VLTabs(QTabBar): # {{{ # }}} +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 @@ class LayoutMixin: # {{{ 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 aa2bac5254..6568558df3 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -632,6 +632,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): 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 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): 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 e105ce3d30..c2bc905458 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 58f65c642e..b04eed4253 100644 --- a/src/calibre/gui2/actions/all_actions.py +++ b/src/calibre/gui2/actions/all_actions.py @@ -33,7 +33,7 @@ class AllGUIActions(InterfaceAction): 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 b04eed4253..aaf2c9463a 100644 --- a/src/calibre/gui2/actions/all_actions.py +++ b/src/calibre/gui2/actions/all_actions.py @@ -135,11 +135,11 @@ class AllGUIActions(InterfaceAction): 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)