From 0f94cb092fa287ab5e538198e99f0f9d65c9d51d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Nov 2019 18:50:50 +0530 Subject: [PATCH] UI for customizing toolbar --- src/calibre/gui2/viewer/toolbars.py | 152 +++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/viewer/toolbars.py b/src/calibre/gui2/viewer/toolbars.py index 601ba602d0..5e6f3a595a 100644 --- a/src/calibre/gui2/viewer/toolbars.py +++ b/src/calibre/gui2/viewer/toolbars.py @@ -7,12 +7,17 @@ from __future__ import absolute_import, division, print_function, unicode_litera import os from functools import partial -from PyQt5.Qt import QAction, QIcon, QKeySequence, QMenu, Qt, QToolBar, pyqtSignal +from PyQt5.Qt import ( + QAction, QGroupBox, QHBoxLayout, QIcon, QKeySequence, QLabel, QListWidget, + QListWidgetItem, QMenu, Qt, QToolBar, QToolButton, QVBoxLayout, pyqtSignal +) from PyQt5.QtWebEngineWidgets import QWebEnginePage from calibre.gui2 import elided_text from calibre.gui2.viewer.shortcuts import index_to_key_sequence -from calibre.gui2.viewer.web_view import get_session_pref, set_book_path +from calibre.gui2.viewer.web_view import get_session_pref, set_book_path, vprefs +from calibre.gui2.widgets2 import Dialog +from calibre.utils.icu import primary_sort_key from polyglot.builtins import iteritems @@ -28,13 +33,14 @@ class Actions(object): def __init__(self, a): self.__dict__.update(a) + self.all_action_names = frozenset(a) def all_actions(): if not hasattr(all_actions, 'ans'): all_actions.ans = Actions({ 'back': Action('back.png', _('Back')), - 'forward': Action('back.png', _('Forward')), + 'forward': Action('forward.png', _('Forward')), 'open': Action('document_open.png', _('Open e-book')), 'copy': Action('edit-copy.png', _('Copy to clipboard')), 'increase_font_size': Action('font_size_larger.png', _('Increase font size'), 'increase_font_size'), @@ -61,6 +67,13 @@ DEFAULT_ACTIONS = ( ) +def current_actions(): + ans = vprefs.get('actions-toolbar-actions') + if not ans: + ans = DEFAULT_ACTIONS + return tuple(ans) + + class ToolBar(QToolBar): def __init__(self, parent=None): @@ -132,7 +145,7 @@ class ActionsToolBar(ToolBar): def add_actions(self): self.clear() - actions = DEFAULT_ACTIONS + actions = current_actions() for x in actions: if x is None: self.addSeparator() @@ -185,3 +198,134 @@ class ActionsToolBar(ToolBar): def update_visibility(self): self.setVisible(bool(get_session_pref('show_actions_toolbar', default=False))) + + +class ActionsList(QListWidget): + + def __init__(self, actions, parent=None, is_source=True): + QListWidget.__init__(self, parent) + self.setSelectionMode(self.ExtendedSelection) + self.setDragEnabled(True) + self.viewport().setAcceptDrops(True) + self.setDropIndicatorShown(True) + self.setDragDropMode(self.InternalMove) + self.setDefaultDropAction(Qt.MoveAction) + self.setMinimumHeight(400) + self.is_source = is_source + if is_source: + actions = self.sort_actions_alphabetically(actions) + actions = [None] + actions + self.set_names(actions) + + def sort_actions_alphabetically(self, actions): + aa = all_actions() + return sorted(actions, key=lambda name: primary_sort_key(getattr(aa, name).text) if name else primary_sort_key('')) + + def add_item_from_name(self, action): + aa = all_actions() + if action is None: + i = QListWidgetItem('-- {} --'.format(_('Separator')), self) + else: + try: + a = getattr(aa, action) + except AttributeError: + return + i = QListWidgetItem(a.icon, a.text, self) + i.setData(Qt.UserRole, action) + return i + + def set_names(self, names): + self.clear() + for name in names: + self.add_item_from_name(name) + + def remove_selected(self): + ans = [] + for item in tuple(self.selectedItems()): + action = item.data(Qt.UserRole) + if action is not None or not self.is_source: + self.takeItem(self.row(item)) + ans.append(action) + return ans + + def add_names(self, names): + for action in names: + if action or not self.is_source: + self.add_item_from_name(action) + if self.is_source: + actions = self.sort_actions_alphabetically(self.names) + self.set_names(actions) + + @property + def names(self): + for i in range(self.count()): + item = self.item(i) + yield item.data(Qt.UserRole) + + +class ConfigureToolBar(Dialog): + + def __init__(self, parent=None): + Dialog.__init__(self, _('Configure the toolbar'), 'configure-viewer-toolbar', parent=parent, prefs=vprefs) + + def setup_ui(self): + acnames = all_actions().all_action_names + self.available_actions = ActionsList(acnames - frozenset(current_actions()), parent=self) + self.current_actions = ActionsList(current_actions(), parent=self, is_source=False) + self.l = l = QVBoxLayout(self) + self.la = la = QLabel(_('Choose the actions you want on the toolbar.' + ' Drag and drop items in the right hand list to re-arrange the toolbar.')) + la.setWordWrap(True) + l.addWidget(la) + self.bv = bv = QVBoxLayout() + bv.addStretch(10) + self.add_button = b = QToolButton(self) + b.setIcon(QIcon(I('forward.png'))), b.setToolTip(_('Add selected actions to the toolbar')) + bv.addWidget(b), bv.addStretch(10) + b.clicked.connect(self.add_actions) + self.remove_button = b = QToolButton(self) + b.setIcon(QIcon(I('back.png'))), b.setToolTip(_('Remove selected actions from the toolbar')) + b.clicked.connect(self.remove_actions) + bv.addWidget(b), bv.addStretch(10) + + self.h = h = QHBoxLayout() + l.addLayout(h) + self.lg = lg = QGroupBox(_('A&vailable actions'), self) + lg.v = v = QVBoxLayout(lg) + v.addWidget(self.available_actions) + h.addWidget(lg) + self.rg = rg = QGroupBox(_('&Current actions'), self) + rg.v = v = QVBoxLayout(rg) + v.addWidget(self.current_actions) + h.addLayout(bv), h.addWidget(rg) + l.addWidget(self.bb) + self.rdb = b = self.bb.addButton(_('Restore defaults'), self.bb.ActionRole) + b.clicked.connect(self.restore_defaults) + + def remove_actions(self): + names = self.current_actions.remove_selected() + self.available_actions.add_names(names) + + def add_actions(self): + names = self.available_actions.remove_selected() + self.current_actions.add_names(names) + + def restore_defaults(self): + self.current_actions.set_names(DEFAULT_ACTIONS) + acnames = all_actions().all_action_names + rest = acnames - frozenset(DEFAULT_ACTIONS) + rest = [None] + list(rest) + self.available_actions.set_names(rest) + + def accept(self): + ans = tuple(self.current_actions.names) + if ans == DEFAULT_ACTIONS: + vprefs.__delitem__('actions-toolbar-actions') + else: + vprefs.set('actions-toolbar-actions', ans) + + +if __name__ == '__main__': + from calibre.gui2 import Application + app = Application([]) + ConfigureToolBar().exec_()