From 4c753c6f637a24b44118741dc3394f170c6bfadd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 22 Dec 2023 13:03:14 +0530 Subject: [PATCH] Work on plugging the new layout code into the actual GUI --- src/calibre/gui2/actions/layout_actions.py | 4 +- src/calibre/gui2/actions/show_quickview.py | 38 +-- src/calibre/gui2/book_details.py | 6 +- src/calibre/gui2/central.py | 120 +++++++-- src/calibre/gui2/cover_flow.py | 35 +-- src/calibre/gui2/dialogs/quickview.py | 12 +- src/calibre/gui2/init.py | 249 ++++++------------ src/calibre/gui2/ui.py | 72 ++---- src/calibre/gui2/widgets.py | 280 +-------------------- 9 files changed, 227 insertions(+), 589 deletions(-) diff --git a/src/calibre/gui2/actions/layout_actions.py b/src/calibre/gui2/actions/layout_actions.py index e005b2ed2f..36b086346a 100644 --- a/src/calibre/gui2/actions/layout_actions.py +++ b/src/calibre/gui2/actions/layout_actions.py @@ -3,7 +3,7 @@ from enum import Enum from functools import partial -from qt.core import QIcon, QToolButton +from qt.core import QToolButton from calibre.gui2.actions import InterfaceAction @@ -33,7 +33,7 @@ class LayoutActions(InterfaceAction): m.addAction(_('Hide all'), self.hide_all) for button, name in zip(self.gui.layout_buttons, self.gui.button_order): m.addSeparator() - ic = QIcon.ic(button.icname) + ic = button.icon() m.addAction(ic, _('Show {}').format(button.label), partial(self.set_visible, Panel(name), True)) m.addAction(ic, _('Hide {}').format(button.label), partial(self.set_visible, Panel(name), False)) diff --git a/src/calibre/gui2/actions/show_quickview.py b/src/calibre/gui2/actions/show_quickview.py index 8cb390b1c9..ac396c1a19 100644 --- a/src/calibre/gui2/actions/show_quickview.py +++ b/src/calibre/gui2/actions/show_quickview.py @@ -10,38 +10,7 @@ from qt.core import QAction, QTimer from calibre.gui2.actions import InterfaceAction from calibre.gui2.dialogs.quickview import Quickview -from calibre.gui2 import error_dialog, gprefs -from calibre.gui2.widgets import LayoutButton - - -class QuickviewButton(LayoutButton): # {{{ - - def __init__(self, gui, quickview_manager): - self.qv = quickview_manager - qaction = quickview_manager.qaction - LayoutButton.__init__(self, 'quickview.png', _('Quickview'), - parent=gui, shortcut=qaction.shortcut().toString()) - self.toggled.connect(self.update_state) - self.action_toggle = qaction - self.action_toggle.triggered.connect(self.toggle) - self.action_toggle.changed.connect(self.update_shortcut) - - def update_state(self, checked): - if checked: - self.set_state_to_hide() - self.qv._show_quickview() - else: - self.set_state_to_show() - self.qv._hide_quickview() - - def save_state(self): - gprefs['quickview visible'] = bool(self.isChecked()) - - def restore_state(self): - if gprefs.get('quickview visible', False): - self.toggle() - -# }}} +from calibre.gui2 import error_dialog current_qv_action_pi = None @@ -104,10 +73,13 @@ class ShowQuickviewAction(InterfaceAction): group=self.action_spec[0]) self.search_action.triggered.connect(self.search_quickview) - self.qv_button = QuickviewButton(self.gui, self) + @property + def qv_button(self): + return self.gui.layout_container.quick_view_button def initialization_complete(self): set_quickview_action_plugin(self) + self.qv_button.update_shortcut(self.qaction) def _hide_quickview(self): ''' diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index e44b865c29..307ccc7711 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -1156,9 +1156,9 @@ class DetailsLayout(QSplitter): # {{{ self.restoreState(s) self.setOrientation(Qt.Orientation.Vertical if self.vertical else Qt.Orientation.Horizontal) - def setGeometry(self, r): - super().setGeometry(r) - self.do_layout(r) + def setGeometry(self, *a): + super().setGeometry(*a) + self.do_layout(self.geometry()) def do_splitter_moved(self, *args): gprefs['book_details_widget_splitter_state'] = bytearray(self.saveState()) diff --git a/src/calibre/gui2/central.py b/src/calibre/gui2/central.py index 18087ffc7b..dbd82bc588 100644 --- a/src/calibre/gui2/central.py +++ b/src/calibre/gui2/central.py @@ -5,12 +5,12 @@ from copy import copy from dataclasses import asdict, dataclass, fields from enum import Enum, auto from qt.core import ( - QDialog, QHBoxLayout, QIcon, QKeySequence, QLabel, QPalette, QPointF, QSize, - QSizePolicy, QStyle, QStyleOption, QStylePainter, Qt, QToolButton, QVBoxLayout, - QWidget, pyqtSignal, + QAction, QDialog, QHBoxLayout, QIcon, QKeySequence, QLabel, QPalette, QPointF, + QSize, QSizePolicy, QStyle, QStyleOption, QStylePainter, Qt, QToolButton, + QVBoxLayout, QWidget, pyqtSignal, ) -from calibre.gui2 import Application, gprefs +from calibre.gui2 import Application, config, gprefs from calibre.gui2.cover_flow import MIN_SIZE HIDE_THRESHOLD = 10 @@ -30,7 +30,9 @@ class Placeholder(QLabel): class LayoutButton(QToolButton): - def __init__(self, name: str, icon: str, label: str, central: 'Central', shortcut=None): + on_action_trigger = pyqtSignal(bool) + + def __init__(self, name: str, icon: str, label: str, central: 'CentralContainer', shortcut=None): super().__init__(central) self.central = central self.label = label @@ -39,14 +41,30 @@ class LayoutButton(QToolButton): self.setIcon(QIcon.ic(icon)) self.setCheckable(True) self.setChecked(self.is_visible) - self.toggled.connect(central.layout_button_toggled) + self.setCursor(Qt.CursorShape.PointingHandCursor) + if isinstance(central, CentralContainer): + self.toggled.connect(central.layout_button_toggled) + + def initialize_with_gui(self, gui): + if self.shortcut is not None: + self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self) + self.action_toggle.changed.connect(self.update_shortcut) + self.action_toggle.triggered.connect(self.toggle_triggered) + gui.addAction(self.action_toggle) + gui.keyboard.register_shortcut( + f'toggle_central_panel_{self.name}', self.action_toggle.text(), default_keys=(self.shortcut,), action=self.action_toggle) + + def toggle_triggered(self): + self.toggle() + self.on_action_trigger.emit(self.isChecked()) @property def is_visible(self): return getattr(self.central.is_visible, self.name) def update_shortcut(self, action_toggle=None): - action_toggle = action_toggle or getattr(self, 'action_toggle', None) + if not isinstance(action_toggle, QAction): + action_toggle = getattr(self, 'action_toggle', None) if action_toggle: sc = ', '.join(sc.toString(QKeySequence.SequenceFormat.NativeText) for sc in action_toggle.shortcuts()) @@ -230,23 +248,31 @@ class Visibility: setattr(self, f.name, getattr(c, f.name)) -class Central(QWidget): +class CentralContainer(QWidget): layout: Layout = Layout.wide - def __init__(self, parent=None, prefs_name='main_window_central_widget_state'): + def __init__(self, parent=None, prefs_name='main_window_central_widget_state', separate_cover_browser=None, for_develop=False): + self.separate_cover_browser = config['separate_cover_flow'] if separate_cover_browser is None else separate_cover_browser self.prefs_name = prefs_name super().__init__(parent) self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) self.wide_desires = WideDesires() self.narrow_desires = NarrowDesires() self.is_visible = Visibility() - self.tag_browser = Placeholder('tag browser', self) - self.book_list = Placeholder('book list', self) - self.cover_browser = Placeholder('cover browser', self) + if for_develop: + self.tag_browser = Placeholder('tag browser', self) + self.book_list = Placeholder('book list', self) + self.cover_browser = Placeholder('cover browser', self) + self.book_details = Placeholder('book details', self) + self.quick_view = Placeholder('quick view', self) + else: + self.tag_browser = QWidget(self) + self.book_list = QWidget(self) + self.cover_browser = QWidget(self) + self.book_details = QWidget(self) + self.quick_view = QWidget(self) self.cover_browser.setMinimumSize(MIN_SIZE) - self.book_details = Placeholder('book details', self) - self.quick_view = Placeholder('quick view', self) self.ignore_button_toggles = False self.tag_browser_button = LayoutButton('tag_browser', 'tags.png', _('Tag browser'), self, 'Shift+Alt+T') self.book_details_button = LayoutButton('book_details', 'book.png', _('Book details'), self, 'Shift+Alt+D') @@ -265,6 +291,28 @@ class Central(QWidget): self.top_handle = h(Qt.Orientation.Horizontal) self.bottom_handle = h(Qt.Orientation.Horizontal) + def set_widget(self, which, w): + existing = getattr(self, which) + existing.setVisible(False) + existing.setParent(None) + setattr(self, which, w) + w.setParent(self) + + def initialize_with_gui(self, gui, book_list_widget): + self.tag_browser_button.initialize_with_gui(gui) + self.book_details_button.initialize_with_gui(gui) + self.cover_browser_button.initialize_with_gui(gui) + self.quick_view_button.initialize_with_gui(gui) + self.set_widget('book_details', gui.book_details) + self.set_widget('tag_browser', gui.tb_widget) + self.set_widget('book_list', book_list_widget) + # cover browser is set in CoverFlowMixin + # Quickview is set in quickview.py code + + @property + def is_wide(self): + return self.layout is Layout.wide + def serialized_settings(self): return { 'layout': self.layout.name, @@ -296,6 +344,7 @@ class Central(QWidget): before = self.serialized_settings() self.unserialize_settings(gprefs.get(self.prefs_name) or {}) if self.serialized_settings() != before: + self.update_button_states_from_visibility() self.relayout() def reset_to_defaults(self): @@ -305,6 +354,7 @@ class Central(QWidget): self.wide_desires.reset_to_defaults() self.narrow_desires.reset_to_defaults() if self.serialized_settings() != before: + self.update_button_states_from_visibility() self.relayout() def toggle_panel(self, which): @@ -322,6 +372,16 @@ class Central(QWidget): setattr(self.is_visible, which, visible) self.update_button_states_from_visibility() + def show_panel(self, which): + if not getattr(self.is_visible, which): + self.set_visibility_of(which, True) + self.relayout() + + def hide_panel(self, which): + if getattr(self.is_visible, which): + self.set_visibility_of(which, False) + self.relayout() + def panel_name_for_handle(self, handle): return self.panel_name_for_handle_wide(handle) if self.layout is Layout.wide else self.panel_name_for_handle_narrow(handle) @@ -381,7 +441,7 @@ class Central(QWidget): def relayout(self): self.tag_browser.setVisible(self.is_visible.tag_browser) self.book_details.setVisible(self.is_visible.book_details) - self.cover_browser.setVisible(self.is_visible.cover_browser) + self.cover_browser.setVisible(self.is_visible.cover_browser and not self.separate_cover_browser) self.book_list.setVisible(self.is_visible.book_list) self.quick_view.setVisible(self.is_visible.quick_view) if self.layout is Layout.wide: @@ -394,6 +454,12 @@ class Central(QWidget): self.layout = Layout.narrow if self.layout is Layout.wide else Layout.wide self.relayout() + def button_for(self, which): + return getattr(self, which + '_button') + + def sizeHint(self): + return QSize(800, 600) + # Wide {{{ def wide_handle_state(self, handle): if handle is self.left_handle: @@ -401,7 +467,7 @@ class Central(QWidget): if handle is self.right_handle: return HandleState.both_visible if self.is_visible.book_details else HandleState.only_main_visible if handle is self.top_handle: - if self.is_visible.cover_browser: + if self.is_visible.cover_browser and not self.separate_cover_browser: return HandleState.both_visible if self.is_visible.book_list else HandleState.only_side_visible return HandleState.only_main_visible if handle is self.bottom_handle: @@ -443,13 +509,13 @@ class Central(QWidget): hs = h.state if hs is HandleState.both_visible or hs is HandleState.only_side_visible: height = normal_handle_width - if h is self.bottom_handle and hs is HandleState.only_main_visible: + if hs is HandleState.only_main_visible and h is self.bottom_handle or (h is self.top_handle and self.separate_cover_browser): height = 0 h.resize(int(central_width), int(height)) available_height -= height cb = max(self.cover_browser.minimumHeight(), int(self.wide_desires.cover_browser_height * self.height())) - if not self.is_visible.cover_browser: + if not self.is_visible.cover_browser or self.separate_cover_browser: cb = 0 qv = bl = 0 if cb >= available_height: @@ -466,7 +532,7 @@ class Central(QWidget): bl = available_height - qv else: bl = available_height - if self.is_visible.cover_browser: + if self.is_visible.cover_browser and not self.separate_cover_browser: self.cover_browser.setGeometry(int(central_x), 0, int(central_width), int(cb)) self.top_handle.move(central_x, cb) if self.is_visible.book_list: @@ -532,7 +598,7 @@ class Central(QWidget): if handle is self.left_handle: return HandleState.both_visible if self.is_visible.tag_browser else HandleState.only_main_visible if handle is self.right_handle: - if self.is_visible.cover_browser: + if self.is_visible.cover_browser and not self.separate_cover_browser: return HandleState.both_visible if self.is_visible.book_list else HandleState.only_side_visible return HandleState.only_main_visible if handle is self.top_handle: @@ -569,12 +635,15 @@ class Central(QWidget): hs = h.state if hs is HandleState.both_visible or hs is HandleState.only_side_visible: width = normal_handle_width + if h is self.right_handle and self.separate_cover_browser: + width = 0 h.resize(int(width), int(central_height)) available_width -= width tb = int(self.narrow_desires.tag_browser_width * self.width()) if self.is_visible.tag_browser else 0 - cb = max(self.cover_browser.minimumWidth(), int(self.narrow_desires.cover_browser_width * self.width())) if self.is_visible.cover_browser else 0 + cb = max(self.cover_browser.minimumWidth(), + int(self.narrow_desires.cover_browser_width * self.width())) if self.is_visible.cover_browser and not self.separate_cover_browser else 0 min_central_width = self.min_central_width_narrow() - if tb + cb > available_width - min_central_width: + if tb + cb > max(0, available_width - min_central_width): width_to_share = max(0, available_width - min_central_width) cb = int(cb * width_to_share / (tb + cb)) cb = max(self.cover_browser.minimumWidth(), cb) @@ -587,7 +656,7 @@ class Central(QWidget): self.left_handle.move(tb, 0) central_x = self.left_handle.x() + self.left_handle.width() self.right_handle.move(tb + central_width + self.left_handle.width(), 0) - if self.is_visible.cover_browser: + if self.is_visible.cover_browser and not self.separate_cover_browser: self.cover_browser.setGeometry(int(self.right_handle.x() + self.right_handle.width()), 0, int(cb), int(self.height())) self.top_handle.resize(int(central_width), int(normal_handle_width if self.is_visible.quick_view else 0)) central_height -= self.top_handle.height() @@ -661,9 +730,6 @@ class Central(QWidget): return {self.left_handle: 'tag_browser', self.right_handle: 'cover_browser', self.top_handle: 'quick_view', self.bottom_handle: 'book_details'}[handle] # }}} - def sizeHint(self): - return QSize(800, 600) - # develop {{{ def develop(): @@ -675,7 +741,7 @@ def develop(): l.setContentsMargins(0, 0, 0, 0) h = QHBoxLayout() l.addLayout(h) - self.central = Central(self, prefs_name='develop_central_layout_widget_state') + self.central = CentralContainer(self, for_develop=True, prefs_name='develop_central_layout_widget_state', separate_cover_browser=False) h.addWidget(self.central.tag_browser_button) h.addWidget(self.central.book_details_button) h.addWidget(self.central.cover_browser_button) diff --git a/src/calibre/gui2/cover_flow.py b/src/calibre/gui2/cover_flow.py index 17c22905ef..bc88a89941 100644 --- a/src/calibre/gui2/cover_flow.py +++ b/src/calibre/gui2/cover_flow.py @@ -353,8 +353,12 @@ class CoverFlowMixin: disable_cover_browser_refresh = False + @property + def cb_button(self): + return self.layout_container.cover_browser_button + def one_auto_scroll(self): - cb_visible = self.cover_flow is not None and self.cb_splitter.button.isChecked() + cb_visible = self.cover_flow is not None and self.cb_button.isChecked() if cb_visible: self.cover_flow.one_auto_scroll() else: @@ -388,18 +392,17 @@ class CoverFlowMixin: self.cover_flow.setImages(self.db_images) self.cover_flow.itemActivated.connect(self.iactions['View'].view_specific_book) self.update_cover_flow_subtitle_font() + button = self.cb_button if self.separate_cover_browser: - self.separate_cover_browser = True - self.cb_splitter.button.clicked.connect(self.toggle_cover_browser) - self.cb_splitter.button.set_state_to_show() - self.cb_splitter.action_toggle.triggered.connect(self.toggle_cover_browser) + button.clicked.connect(self.toggle_cover_browser) + button.set_state_to_show() + button.on_action_trigger.connect(self.toggle_cover_browser) self.cover_flow.stop.connect(self.hide_cover_browser) self.cover_flow.setVisible(False) else: - self.separate_cover_browser = False - self.cb_splitter.insertWidget(self.cb_splitter.side_index, self.cover_flow) - self.cover_flow.stop.connect(self.cb_splitter.hide_side_pane) - self.cb_splitter.button.toggled.connect(self.cover_browser_toggled, type=Qt.ConnectionType.QueuedConnection) + self.cover_flow.stop.connect(button.set_state_to_hide) + self.layout_container.set_widget('cover_browser', self.cover_flow) + button.toggled.connect(self.cover_browser_toggled, type=Qt.ConnectionType.QueuedConnection) def update_cover_flow_subtitle_font(self): db = self.current_db.new_api @@ -419,7 +422,7 @@ class CoverFlowMixin: self.show_cover_browser() def cover_browser_toggled(self, *args): - if self.cb_splitter.button.isChecked(): + if self.cb_button.isChecked(): self.cover_browser_shown() else: self.cover_browser_hidden() @@ -447,25 +450,25 @@ class CoverFlowMixin: def show_cover_browser(self): d = CBDialog(self, self.cover_flow) - d.addAction(self.cb_splitter.action_toggle) + d.addAction(self.cb_button.action_toggle) self.cover_flow.setVisible(True) self.cover_flow.setFocus(Qt.FocusReason.OtherFocusReason) d.show_fullscreen() if gprefs['cb_fullscreen'] else d.show() - self.cb_splitter.button.set_state_to_hide() + self.cb_button.set_state_to_hide() d.closed.connect(self.cover_browser_closed) self.cb_dialog = d - self.cb_splitter.button.set_state_to_hide() + self.cb_button.set_state_to_hide() def cover_browser_closed(self, *args): self.cb_dialog = None - self.cb_splitter.button.set_state_to_show() + self.cb_button.set_state_to_show() def hide_cover_browser(self, *args): cbd = getattr(self, 'cb_dialog', None) if cbd is not None: cbd.accept() self.cb_dialog = None - self.cb_splitter.button.set_state_to_show() + self.cb_button.set_state_to_show() def is_cover_browser_visible(self): try: @@ -473,7 +476,7 @@ class CoverFlowMixin: return self.cover_flow.isVisible() except AttributeError: return False # called before init_cover_flow_mixin - return not self.cb_splitter.is_side_index_hidden + return self.cb_button.isChecked() def refresh_cover_browser(self): if self.disable_cover_browser_refresh: diff --git a/src/calibre/gui2/dialogs/quickview.py b/src/calibre/gui2/dialogs/quickview.py index f532dfe839..6b3894f2a9 100644 --- a/src/calibre/gui2/dialogs/quickview.py +++ b/src/calibre/gui2/dialogs/quickview.py @@ -269,7 +269,7 @@ class Quickview(QDialog, Ui_Quickview): # Remove the ampersands from the buttons because shortcuts exist. self.lock_qv.setText(_('Lock Quickview contents')) self.refresh_button.setText(_('Refresh')) - self.gui.quickview_splitter.add_quickview_dialog(self) + self.gui.layout_container.set_widget('quick_view', self) self.close_button.setVisible(False) else: self.dock_button.setToolTip(_('Embed the Quickview panel into the main calibre window')) @@ -324,7 +324,7 @@ class Quickview(QDialog, Ui_Quickview): t.start() def item_doubleclicked(self, item): - tb = self.gui.stack.tb_widget + tb = self.gui.tb_widget tb.set_focus_to_find_box() tb.item_search.lineEdit().setText(self.current_key + ':=' + item.text()) tb.do_find() @@ -456,7 +456,7 @@ class Quickview(QDialog, Ui_Quickview): def show(self): QDialog.show(self) if self.is_pane: - self.gui.quickview_splitter.show_quickview_widget() + self.gui.show_panel('quick_view') def show_as_pane_changed(self): gprefs['quickview_is_pane'] = not gprefs.get('quickview_is_pane', False) @@ -707,10 +707,6 @@ class Quickview(QDialog, Ui_Quickview): def resizeEvent(self, *args): QDialog.resizeEvent(self, *args) - # Do this if we are resizing for the first time to reset state. - if self.is_pane and self.height() == 0: - self.gui.quickview_splitter.set_sizes() - if self.books_table_column_widths is not None: for c,w in enumerate(self.books_table_column_widths): self.books_table.setColumnWidth(c, w) @@ -868,7 +864,7 @@ class Quickview(QDialog, Ui_Quickview): def _reject(self): if self.is_pane: - self.gui.quickview_splitter.hide_quickview_widget() + self.gui.hide_panel('quick_view') self.gui.library_view.setFocus(Qt.FocusReason.ActiveWindowFocusReason) self._close() QDialog.reject(self) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 1fc8321378..642cecf33f 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -7,23 +7,21 @@ __docformat__ = 'restructuredtext en' import functools from qt.core import ( - QAction, QApplication, QDialog, QEvent, QIcon, QLabel, QMenu, QPixmap, QSizePolicy, - QSplitter, QStackedWidget, QStatusBar, QStyle, QStyleOption, QStylePainter, Qt, - QTabBar, QTimer, QToolButton, QUrl, QVBoxLayout, QWidget, + QAction, QApplication, QDialog, QEvent, QIcon, QLabel, QMenu, QPixmap, + QStackedWidget, QStatusBar, QStyle, QStyleOption, QStylePainter, Qt, QTabBar, + QTimer, QToolButton, QUrl, ) from calibre.constants import get_appname_for_display, get_version, ismacos from calibre.customize.ui import find_plugin -from calibre.gui2 import ( - config, error_dialog, gprefs, is_widescreen, open_local_file, open_url, -) +from calibre.gui2 import config, error_dialog, gprefs, open_local_file, open_url from calibre.gui2.book_details import BookDetails +from calibre.gui2.central import CentralContainer, LayoutButton from calibre.gui2.layout_menu import LayoutMenu from calibre.gui2.library.alternate_views import GridView from calibre.gui2.library.views import BooksView, DeviceBooksView from calibre.gui2.notify import get_notifier from calibre.gui2.tag_browser.ui import TagBrowserWidget -from calibre.gui2.widgets import LayoutButton, Splitter from calibre.utils.config import prefs from calibre.utils.icu import sort_key from calibre.utils.localization import localize_website_link, ngettext @@ -111,105 +109,6 @@ class LibraryViewMixin: # {{{ # }}} - -class QuickviewSplitter(QSplitter): # {{{ - - def __init__(self, parent=None, orientation=Qt.Orientation.Vertical, qv_widget=None): - super().__init__(parent=parent, orientation=orientation) - self.splitterMoved.connect(self.splitter_moved) - self.setChildrenCollapsible(False) - self.qv_widget = qv_widget - - def splitter_moved(self): - gprefs['quickview_dialog_heights'] = self.sizes() - - def resizeEvent(self, *args): - super().resizeEvent(*args) - if self.sizes()[1] != 0: - gprefs['quickview_dialog_heights'] = self.sizes() - - def set_sizes(self): - sizes = gprefs.get('quickview_dialog_heights', []) - if len(sizes) == 2: - self.setSizes(sizes) - - def add_quickview_dialog(self, qv_dialog): - self.qv_widget.layout().addWidget(qv_dialog) - - def show_quickview_widget(self): - self.qv_widget.show() - - def hide_quickview_widget(self): - self.qv_widget.hide() -# }}} - - -class LibraryWidget(Splitter): # {{{ - - def __init__(self, parent): - orientation = Qt.Orientation.Vertical - if config['gui_layout'] == 'narrow': - orientation = Qt.Orientation.Horizontal if is_widescreen() else Qt.Orientation.Vertical - idx = 0 if orientation == Qt.Orientation.Vertical else 1 - size = 300 if orientation == Qt.Orientation.Vertical else 550 - Splitter.__init__(self, 'cover_browser_splitter', _('Cover browser'), - 'cover_flow.png', - orientation=orientation, parent=parent, - connect_button=not config['separate_cover_flow'], - side_index=idx, initial_side_size=size, initial_show=False, - shortcut='Shift+Alt+B') - - quickview_widget = QWidget() - parent.quickview_splitter = QuickviewSplitter( - parent=self, orientation=Qt.Orientation.Vertical, qv_widget=quickview_widget) - parent.library_view = BooksView(parent) - parent.library_view.setObjectName('library_view') - stack = QStackedWidget(self) - av = parent.library_view.alternate_views - parent.pin_container = av.set_stack(stack) - parent.grid_view = GridView(parent) - parent.grid_view.setObjectName('grid_view') - av.add_view('grid', parent.grid_view) - parent.quickview_splitter.addWidget(stack) - - l = QVBoxLayout() - l.setContentsMargins(4, 0, 0, 0) - quickview_widget.setLayout(l) - parent.quickview_splitter.addWidget(quickview_widget) - parent.quickview_splitter.hide_quickview_widget() - - self.addWidget(parent.quickview_splitter) -# }}} - - -class Stack(QStackedWidget): # {{{ - - def __init__(self, parent): - QStackedWidget.__init__(self, parent) - - parent.cb_splitter = LibraryWidget(parent) - self.tb_widget = TagBrowserWidget(parent) - parent.tb_splitter = Splitter('tag_browser_splitter', - _('Tag browser'), 'tags.png', - parent=parent, side_index=0, initial_side_size=200, - shortcut='Shift+Alt+T') - parent.tb_splitter.state_changed.connect( - self.tb_widget.set_pane_is_visible, Qt.ConnectionType.QueuedConnection) - parent.tb_splitter.addWidget(self.tb_widget) - parent.tb_splitter.addWidget(parent.cb_splitter) - parent.tb_splitter.setCollapsible(parent.tb_splitter.other_index, False) - - self.addWidget(parent.tb_splitter) - for x in ('memory', 'card_a', 'card_b'): - name = x+'_view' - w = DeviceBooksView(parent) - setattr(parent, name, w) - self.addWidget(w) - w.setObjectName(name) - - -# }}} - class UpdateLabel(QLabel): # {{{ def __init__(self, *args, **kwargs): @@ -220,7 +119,6 @@ class UpdateLabel(QLabel): # {{{ pass # }}} - class VersionLabel(QLabel): # {{{ def __init__(self, parent): @@ -258,7 +156,6 @@ class VersionLabel(QLabel): # {{{ return QLabel.paintEvent(self, ev) # }}} - class StatusBar(QStatusBar): # {{{ def __init__(self, parent=None): @@ -327,12 +224,11 @@ class StatusBar(QStatusBar): # {{{ # }}} - class GridViewButton(LayoutButton): # {{{ def __init__(self, gui): sc = 'Alt+Shift+G' - LayoutButton.__init__(self, 'grid.png', _('Cover grid'), parent=gui, shortcut=sc) + LayoutButton.__init__(self, 'cover_grid', 'grid.png', _('Cover grid'), gui, shortcut=sc) self.set_state_to_show() self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self) gui.addAction(self.action_toggle) @@ -342,6 +238,10 @@ class GridViewButton(LayoutButton): # {{{ self.action_toggle.changed.connect(self.update_shortcut) self.toggled.connect(self.update_state) + @property + def is_visible(self): + return self.isChecked() + def update_state(self, checked): if checked: self.set_state_to_hide() @@ -362,7 +262,7 @@ class SearchBarButton(LayoutButton): # {{{ def __init__(self, gui): sc = 'Alt+Shift+F' - LayoutButton.__init__(self, 'search.png', _('Search bar'), parent=gui, shortcut=sc) + LayoutButton.__init__(self, 'search', 'search.png', _('Search bar'), gui, shortcut=sc) self.set_state_to_hide() self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self) gui.addAction(self.action_toggle) @@ -372,6 +272,10 @@ class SearchBarButton(LayoutButton): # {{{ self.action_toggle.changed.connect(self.update_shortcut) self.toggled.connect(self.update_state) + @property + def is_visible(self): + return self.isChecked() + def update_state(self, checked): if checked: self.set_state_to_hide() @@ -560,7 +464,6 @@ class VLTabs(QTabBar): # {{{ # }}} - class LayoutMixin: # {{{ def __init__(self, *args, **kwargs): @@ -569,35 +472,34 @@ class LayoutMixin: # {{{ def init_layout_mixin(self): self.vl_tabs = VLTabs(self) self.centralwidget.layout().addWidget(self.vl_tabs) + self.layout_container = CentralContainer(self) + self.centralwidget.layout().addWidget(self.layout_container) + self.book_details = BookDetails(self.layout_container.is_wide, self) + self.stack = QStackedWidget(self) + self.library_view = BooksView(self) + self.library_view.setObjectName('library_view') + stack = QStackedWidget(self) + self.stack.addWidget(stack) + av = self.library_view.alternate_views + self.pin_container = av.set_stack(stack) + self.grid_view = GridView(self) + self.grid_view.setObjectName('grid_view') + av.add_view('grid', self.grid_view) + self.tb_widget = TagBrowserWidget(self) + self.memory_view = DeviceBooksView(self) + self.stack.addWidget(self.memory_view) + self.memory_view.setObjectName('memory_view') + self.card_a_view = DeviceBooksView(self) + self.stack.addWidget(self.card_a_view) + self.card_a_view.setObjectName('card_a_view') + self.card_b_view = DeviceBooksView(self) + self.stack.addWidget(self.card_b_view) + self.card_b_view.setObjectName('card_b_view') - if config['gui_layout'] == 'narrow': # narrow {{{ - self.book_details = BookDetails(False, self) - self.stack = Stack(self) - self.bd_splitter = Splitter('book_details_splitter', - _('Book details'), 'book.png', - orientation=Qt.Orientation.Vertical, parent=self, side_index=1, - shortcut='Shift+Alt+D') - self.bd_splitter.addWidget(self.stack) - self.bd_splitter.addWidget(self.book_details) - self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) - self.centralwidget.layout().addWidget(self.bd_splitter) - self.button_order = button_order = ('sb', 'tb', 'bd', 'gv', 'cb', 'qv') - # }}} - else: # wide {{{ - self.bd_splitter = Splitter('book_details_splitter', - _('Book details'), 'book.png', initial_side_size=200, - orientation=Qt.Orientation.Horizontal, parent=self, side_index=1, - shortcut='Shift+Alt+D') - self.stack = Stack(self) - self.bd_splitter.addWidget(self.stack) - self.book_details = BookDetails(True, self) - self.bd_splitter.addWidget(self.book_details) - self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) - self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Expanding, - QSizePolicy.Policy.Expanding)) - self.centralwidget.layout().addWidget(self.bd_splitter) - self.button_order = button_order = ('sb', 'tb', 'cb', 'gv', 'qv', 'bd') - # }}} + if self.layout_container.is_wide: + self.button_order = 'sb', 'tb', 'cb', 'gv', 'qv', 'bd' + else: + self.button_order = 'sb', 'tb', 'bd', 'gv', 'cb', 'qv' # This must use the base method to find the plugin because it hasn't # been fully initialized yet @@ -605,6 +507,9 @@ class LayoutMixin: # {{{ if self.qv and self.qv.actual_plugin_: self.qv = self.qv.actual_plugin_ + self.layout_container.initialize_with_gui(self, self.stack) + self.layout_container.tag_browser_button.toggled.connect( + self.tb_widget.set_pane_is_visible, Qt.ConnectionType.QueuedConnection) self.status_bar = StatusBar(self) stylename = str(self.style().objectName()) self.grid_view_button = GridViewButton(self) @@ -613,40 +518,33 @@ class LayoutMixin: # {{{ self.search_bar_button.toggled.connect(self.toggle_search_bar) self.layout_buttons = [] - for x in button_order: - if hasattr(self, x + '_splitter'): - button = getattr(self, x + '_splitter').button + for x in self.button_order: + if x == 'gv': + button = self.grid_view_button + elif x == 'sb': + button = self.search_bar_button else: - if x == 'gv': - button = self.grid_view_button - elif x == 'qv': - if self.qv is None: - continue - button = self.qv.qv_button - else: - button = self.search_bar_button + button = self.layout_container.button_for({ + 'tb': 'tag_browser', 'bd': 'book_details', 'cb': 'cover_browser', 'qv': 'quick_view' + }[x]) self.layout_buttons.append(button) - button.setVisible(False) + button.setVisible(gprefs['show_layout_buttons']) if ismacos and stylename != 'Calibre': button.setStyleSheet(''' QToolButton { background: none; border:none; padding: 0px; } QToolButton:checked { background: rgba(0, 0, 0, 25%); } ''') self.status_bar.addPermanentWidget(button) - if gprefs['show_layout_buttons']: - for b in self.layout_buttons: - b.setVisible(True) - self.status_bar.addPermanentWidget(b) - else: - self.layout_button = b = QToolButton(self) - b.setAutoRaise(True), b.setCursor(Qt.CursorShape.PointingHandCursor) - b.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) - b.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon) - b.setText(_('Layout')), b.setIcon(QIcon.ic('layout.png')) - b.setMenu(LayoutMenu(self)) - b.setToolTip(_( - 'Show and hide various parts of the calibre main window')) - self.status_bar.addPermanentWidget(b) + self.layout_button = b = QToolButton(self) + b.setAutoRaise(True), b.setCursor(Qt.CursorShape.PointingHandCursor) + b.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) + b.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon) + b.setText(_('Layout')), b.setIcon(QIcon.ic('layout.png')) + b.setMenu(LayoutMenu(self)) + b.setToolTip(_( + 'Show and hide various parts of the calibre main window')) + self.status_bar.addPermanentWidget(b) + b.setVisible(not gprefs['show_layout_buttons']) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) @@ -698,6 +596,12 @@ class LayoutMixin: # {{{ self.library_view.currentIndex()) self.library_view.setFocus(Qt.FocusReason.OtherFocusReason) + def show_panel(self, name): + self.layout_container.show_panel(name) + + def hide_panel(self, name): + self.layout_container.hide_panel(name) + def set_search_string_with_append(self, expression, append=''): current = self.search.text().strip() if append: @@ -727,7 +631,7 @@ class LayoutMixin: # {{{ def find_in_tag_browser_triggered(self, field, value): if field and value: - tb = self.stack.tb_widget + tb = self.tb_widget tb.set_focus_to_find_box() tb.item_search.lineEdit().setText(field + ':=' + value) tb.do_find() @@ -812,22 +716,15 @@ class LayoutMixin: # {{{ for x in ('library', 'memory', 'card_a', 'card_b'): getattr(self, x+'_view').save_state() - for x in ('cb', 'tb', 'bd'): - s = getattr(self, x+'_splitter') - s.update_desired_state() - s.save_state() + self.layout_container.write_settings() self.grid_view_button.save_state() self.search_bar_button.save_state() - if self.qv: - self.qv.qv_button.save_state() def read_layout_settings(self): # View states are restored automatically when set_database is called - for x in ('cb', 'tb', 'bd'): - getattr(self, x+'_splitter').restore_state() + self.layout_container.read_settings() self.grid_view_button.restore_state() self.search_bar_button.restore_state() - # Can't do quickview here because the gui isn't totally set up. Do it in ui def update_status_bar(self, *args): v = self.current_view() diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index e3ed3b00d5..2f6f9ed5ff 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -17,7 +17,6 @@ import sys import textwrap import time from collections import OrderedDict, deque -from functools import partial from io import BytesIO from qt.core import ( QAction, QApplication, QDialog, QFont, QIcon, QMenu, QSystemTrayIcon, Qt, QTimer, @@ -56,7 +55,7 @@ from calibre.gui2.search_box import SavedSearchBoxMixin, SearchBoxMixin from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin from calibre.gui2.tag_browser.ui import TagBrowserMixin from calibre.gui2.update import UpdateMixin -from calibre.gui2.widgets import ProgressIndicator +from calibre.gui2.widgets import ProgressIndicator, BusyCursor from calibre.library import current_library_name from calibre.srv.library_broker import GuiLibraryBroker, db_matches from calibre.utils.config import dynamic, prefs @@ -398,7 +397,13 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ do_hide_windows = True show_gui = False setattr(self, '__systray_minimized', True) - QTimer.singleShot(0, partial(self.post_initialize_actions, show_gui, do_hide_windows)) + if do_hide_windows: + self.hide_windows() + if show_gui: + timed_print('GUI main window shown') + self.show() + self.layout_container.relayout() + QTimer.singleShot(0, self.post_initialize_actions) self.read_settings() self.finalize_layout() @@ -430,43 +435,12 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ self.iactions['Connect Share'].check_smartdevice_menus() QTimer.singleShot(100, self.update_toggle_to_tray_action) - def post_initialize_actions(self, show_gui, do_hide_windows): + def post_initialize_actions(self): # Various post-initialization actions after an event loop tick - + self.listener.start_listening() + self.start_smartdevice() # Collect cycles now gc.collect() - - self.listener.start_listening() - if do_hide_windows: - self.hide_windows() - if show_gui: - timed_print('GUI main window shown') - self.show() - # Force repaint of the book details splitter because it otherwise ends - # up with the wrong size. I don't know why. - self.bd_splitter.repaint() - - # Once the gui is initialized we can restore the quickview state - # The same thing will be true for any action-based operation with a - # layout button. We need to let a book be selected in the book list - # before initializing quickview, so run it after an event loop tick - QTimer.singleShot(0, self.start_quickview) - - # Start the smartdevice later so that the network time doesn't affect - # the gui repaint debouncing. Wait 3 seconds before starting to be sure - # that all other initialization (plugins etc) has completed. Yes, 3 - # seconds is an arbitrary value and probably too long, but it will do - # until the underlying structure changes to make it unnecessary. - QTimer.singleShot(3000, self.start_smartdevice) - - def start_quickview(self): - from calibre.gui2.actions.show_quickview import get_quickview_action_plugin - qv = get_quickview_action_plugin() - if qv: - timed_print('QuickView starting') - qv.qv_button.restore_state() - timed_print('QuickView started') - self.save_layout_state() self.focus_library_view() def show_gui_debug_msg(self): @@ -498,14 +472,15 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ timed_print('Starting the smartdevice driver') message = None if self.device_manager.get_option('smartdevice', 'autostart'): - try: - message = self.device_manager.start_plugin('smartdevice') - timed_print('Finished starting smartdevice') - except Exception as e: - message = str(e) - timed_print(f'Starting smartdevice driver failed: {message}') - import traceback - traceback.print_exc() + with BusyCursor(): + try: + message = self.device_manager.start_plugin('smartdevice') + timed_print('Finished starting smartdevice') + except Exception as e: + message = str(e) + timed_print(f'Starting smartdevice driver failed: {message}') + import traceback + traceback.print_exc() if message: if not self.device_manager.is_running('Wireless Devices'): error_dialog(self, _('Problem starting the wireless device'), @@ -1043,9 +1018,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3 self.stack.setCurrentIndex(page) self.book_details.reset_info() - for x in ('tb', 'cb'): - splitter = getattr(self, x+'_splitter') - splitter.button.setEnabled(location == 'library') + self.layout_container.tag_browser_button.setEnabled(location == 'library') + self.layout_container.cover_browser_button.setEnabled(location == 'library') for action in self.iactions.values(): action.location_selected(location) if location == 'library': @@ -1168,7 +1142,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ self.save_geometry(gprefs, 'calibre_main_window_geometry') dynamic.set('sort_history', self.library_view.model().sort_history) self.save_layout_state() - self.stack.tb_widget.save_state() + self.tb_widget.save_state() def quit(self, checked=True, restart=False, debug_on_restart=False, confirm_quit=True, no_plugins_on_restart=False): diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 9cbac3049a..8211d542f9 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -6,11 +6,11 @@ Miscellaneous widgets used in the GUI import os import re from qt.core import ( - QAction, QApplication, QClipboard, QColor, QComboBox, QCompleter, QCursor, QEvent, - QFont, QGraphicsScene, QGraphicsView, QIcon, QKeySequence, QLabel, QLineEdit, - QListWidget, QListWidgetItem, QMenu, QPageSize, QPainter, QPalette, QPen, QPixmap, - QPrinter, QRect, QSize, QSplitter, QSplitterHandle, QStringListModel, - QSyntaxHighlighter, Qt, QTextCharFormat, QTimer, QToolButton, QWidget, pyqtSignal, + QApplication, QClipboard, QColor, QComboBox, QCompleter, QCursor, QEvent, QFont, + QGraphicsScene, QGraphicsView, QIcon, QLabel, QLineEdit, QListWidget, + QListWidgetItem, QMenu, QPageSize, QPainter, QPalette, QPen, QPixmap, QPrinter, + QRect, QSize, QSplitterHandle, QStringListModel, QSyntaxHighlighter, Qt, + QTextCharFormat, QWidget, pyqtSignal, ) from calibre import fit_image, force_unicode, strftime @@ -1063,276 +1063,6 @@ class SplitterHandle(QSplitterHandle): self.double_clicked.emit(self) -class LayoutButton(QToolButton): - - def __init__(self, icon, text, splitter=None, parent=None, shortcut=None): - QToolButton.__init__(self, parent) - self.label = text - self.setIcon(QIcon.ic(icon)) - self.setCheckable(True) - self.icname = os.path.basename(icon).rpartition('.')[0] - - self.splitter = splitter - if splitter is not None: - splitter.state_changed.connect(self.update_state) - self.setCursor(Qt.CursorShape.PointingHandCursor) - self.shortcut = shortcut or '' - - def update_shortcut(self, action_toggle=None): - action_toggle = action_toggle or getattr(self, 'action_toggle', None) - if action_toggle: - sc = ', '.join(sc.toString(QKeySequence.SequenceFormat.NativeText) - for sc in action_toggle.shortcuts()) - self.shortcut = sc or '' - self.update_text() - - def update_text(self): - t = _('Hide {}') if self.isChecked() else _('Show {}') - t = t.format(self.label) - if self.shortcut: - t += f' [{self.shortcut}]' - self.setText(t), self.setToolTip(t), self.setStatusTip(t) - - def set_state_to_show(self, *args): - self.setChecked(False) - self.update_text() - - def set_state_to_hide(self, *args): - self.setChecked(True) - self.update_text() - - def update_state(self, *args): - if self.splitter.is_side_index_hidden: - self.set_state_to_show() - else: - self.set_state_to_hide() - - def mouseReleaseEvent(self, ev): - if ev.button() == Qt.MouseButton.RightButton: - from calibre.gui2.ui import get_gui - gui = get_gui() - if self.icname == 'search': - gui.iactions['Preferences'].do_config(initial_plugin=('Interface', 'Search'), close_after_initial=True) - ev.accept() - return - tab_name = {'book':'book_details', 'grid':'cover_grid', 'cover_flow':'cover_browser', - 'tags':'tag_browser', 'quickview':'quickview'}.get(self.icname) - if tab_name: - if gui is not None: - gui.iactions['Preferences'].do_config(initial_plugin=('Interface', 'Look & Feel', tab_name+'_tab'), close_after_initial=True) - ev.accept() - return - return QToolButton.mouseReleaseEvent(self, ev) - - -class Splitter(QSplitter): - - state_changed = pyqtSignal(object) - reapply_sizes = pyqtSignal(object) - - def __init__(self, name, label, icon, initial_show=True, - initial_side_size=120, connect_button=True, - orientation=Qt.Orientation.Horizontal, side_index=0, parent=None, - shortcut=None, hide_handle_on_single_panel=True): - super().__init__(parent) - self.reapply_sizes.connect(self.setSizes, type=Qt.ConnectionType.QueuedConnection) - self.hide_handle_on_single_panel = hide_handle_on_single_panel - if hide_handle_on_single_panel: - self.state_changed.connect(self.update_handle_width) - self.original_handle_width = self.handleWidth() - self.resize_timer = QTimer(self) - self.resize_timer.setSingleShot(True) - self.desired_side_size = initial_side_size - self.desired_show = initial_show - self.resize_timer.setInterval(5) - self.resize_timer.timeout.connect(self.do_resize) - self.setOrientation(orientation) - self.side_index = side_index - self._name = name - self.label = label - self.initial_side_size = initial_side_size - self.initial_show = initial_show - self.splitterMoved.connect(self.splitter_moved, type=Qt.ConnectionType.QueuedConnection) - self.button = LayoutButton(icon, label, self, shortcut=shortcut) - if connect_button: - self.button.clicked.connect(self.double_clicked) - - if shortcut is not None: - self.action_toggle = QAction(QIcon.ic(icon), _('Toggle') + ' ' + label, - self) - self.action_toggle.changed.connect(self.update_shortcut) - self.action_toggle.triggered.connect(self.toggle_triggered) - if parent is not None: - parent.addAction(self.action_toggle) - if hasattr(parent, 'keyboard'): - parent.keyboard.register_shortcut('splitter %s %s'%(name, - label), str(self.action_toggle.text()), - default_keys=(shortcut,), action=self.action_toggle) - else: - self.action_toggle.setShortcut(shortcut) - else: - self.action_toggle.setShortcut(shortcut) - - def update_shortcut(self): - self.button.update_shortcut(self.action_toggle) - - def toggle_triggered(self, *args): - self.toggle_side_pane() - - def createHandle(self): - return SplitterHandle(self.orientation(), self) - - def initialize(self): - for i in range(self.count()): - h = self.handle(i) - if h is not None: - h.splitter_moved() - self.state_changed.emit(not self.is_side_index_hidden) - - def splitter_moved(self, *args): - self.desired_side_size = self.side_index_size - self.state_changed.emit(not self.is_side_index_hidden) - - def update_handle_width(self, not_one_panel): - self.setHandleWidth(self.original_handle_width if not_one_panel else 0) - - @property - def is_side_index_hidden(self): - sizes = list(self.sizes()) - try: - return sizes[self.side_index] == 0 - except IndexError: - return True - - @property - def save_name(self): - ori = 'horizontal' if self.orientation() == Qt.Orientation.Horizontal \ - else 'vertical' - return self._name + '_' + ori - - def print_sizes(self): - if self.count() > 1: - print(self.save_name, 'side:', self.side_index_size, 'other:', end=' ') - print(list(self.sizes())[self.other_index]) - - @property - def side_index_size(self): - if self.count() < 2: - return 0 - return self.sizes()[self.side_index] - - @side_index_size.setter - def side_index_size(self, val): - if self.count() < 2: - return - side_index_hidden = self.is_side_index_hidden - if val == 0 and not side_index_hidden: - self.save_state() - sizes = list(self.sizes()) - for i in range(len(sizes)): - sizes[i] = val if i == self.side_index else 10 - self.setSizes(sizes) - sizes = list(self.sizes()) - total = sum(sizes) - total_needs_adjustment = self.hide_handle_on_single_panel and side_index_hidden - if total_needs_adjustment: - total -= self.original_handle_width - for i in range(len(sizes)): - sizes[i] = val if i == self.side_index else total-val - self.setSizes(sizes) - self.initialize() - if total_needs_adjustment: - # the handle visibility and therefore size distribution will change - # when the event loop ticks - self.reapply_sizes.emit(sizes) - - def ignore_child_paints(self, ignore=True): - for widget in self: - if hasattr(widget, 'ignore_paint_events'): - widget.ignore_paint_events = ignore - - def do_resize(self, *args): - orig = self.desired_side_size - super().resizeEvent(self._resize_ev) - if orig > 20 and self.desired_show: - c = 0 - while abs(self.side_index_size - orig) > 10 and c < 5: - self.apply_state(self.get_state(), save_desired=False) - c += 1 - self.ignore_child_paints(False) - - def __iter__(self): - for i in range(self.count()): - yield self.widget(i) - - def resizeEvent(self, ev): - self.ignore_child_paints() - self._resize_ev = ev - self.resize_timer.start() - - def get_state(self): - if self.count() < 2: - return (False, 200) - return (self.desired_show, self.desired_side_size) - - def apply_state(self, state, save_desired=True): - if state[0]: - self.side_index_size = state[1] - if save_desired: - self.desired_side_size = self.side_index_size - else: - self.side_index_size = 0 - self.desired_show = state[0] - - def default_state(self): - return (self.initial_show, self.initial_side_size) - - # Public API {{{ - - def update_desired_state(self): - self.desired_show = not self.is_side_index_hidden - - def save_state(self): - if self.count() > 1: - gprefs[self.save_name+'_state'] = self.get_state() - - @property - def other_index(self): - return (self.side_index+1)%2 - - def restore_state(self): - if self.count() > 1: - state = gprefs.get(self.save_name+'_state', - self.default_state()) - self.apply_state(state, save_desired=False) - self.desired_side_size = state[1] - - def toggle_side_pane(self, hide=None): - if hide is None: - action = 'show' if self.is_side_index_hidden else 'hide' - else: - action = 'hide' if hide else 'show' - getattr(self, action+'_side_pane')() - - def show_side_pane(self): - if self.count() < 2 or not self.is_side_index_hidden: - return - if self.desired_side_size == 0: - self.desired_side_size = self.initial_side_size - self.apply_state((True, self.desired_side_size)) - - def hide_side_pane(self): - if self.count() < 2 or self.is_side_index_hidden: - return - self.apply_state((False, self.desired_side_size)) - - def double_clicked(self, *args): - self.toggle_side_pane() - - # }}} - -# }}} - class PaperSizes(QComboBox): # {{{