mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Work on plugging the new layout code into the actual GUI
This commit is contained in:
parent
3f446ae4af
commit
4c753c6f63
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from qt.core import QIcon, QToolButton
|
from qt.core import QToolButton
|
||||||
|
|
||||||
from calibre.gui2.actions import InterfaceAction
|
from calibre.gui2.actions import InterfaceAction
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ class LayoutActions(InterfaceAction):
|
|||||||
m.addAction(_('Hide all'), self.hide_all)
|
m.addAction(_('Hide all'), self.hide_all)
|
||||||
for button, name in zip(self.gui.layout_buttons, self.gui.button_order):
|
for button, name in zip(self.gui.layout_buttons, self.gui.button_order):
|
||||||
m.addSeparator()
|
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, _('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))
|
m.addAction(ic, _('Hide {}').format(button.label), partial(self.set_visible, Panel(name), False))
|
||||||
|
|
||||||
|
@ -10,38 +10,7 @@ from qt.core import QAction, QTimer
|
|||||||
|
|
||||||
from calibre.gui2.actions import InterfaceAction
|
from calibre.gui2.actions import InterfaceAction
|
||||||
from calibre.gui2.dialogs.quickview import Quickview
|
from calibre.gui2.dialogs.quickview import Quickview
|
||||||
from calibre.gui2 import error_dialog, gprefs
|
from calibre.gui2 import error_dialog
|
||||||
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()
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
|
|
||||||
current_qv_action_pi = None
|
current_qv_action_pi = None
|
||||||
@ -104,10 +73,13 @@ class ShowQuickviewAction(InterfaceAction):
|
|||||||
group=self.action_spec[0])
|
group=self.action_spec[0])
|
||||||
self.search_action.triggered.connect(self.search_quickview)
|
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):
|
def initialization_complete(self):
|
||||||
set_quickview_action_plugin(self)
|
set_quickview_action_plugin(self)
|
||||||
|
self.qv_button.update_shortcut(self.qaction)
|
||||||
|
|
||||||
def _hide_quickview(self):
|
def _hide_quickview(self):
|
||||||
'''
|
'''
|
||||||
|
@ -1156,9 +1156,9 @@ class DetailsLayout(QSplitter): # {{{
|
|||||||
self.restoreState(s)
|
self.restoreState(s)
|
||||||
self.setOrientation(Qt.Orientation.Vertical if self.vertical else Qt.Orientation.Horizontal)
|
self.setOrientation(Qt.Orientation.Vertical if self.vertical else Qt.Orientation.Horizontal)
|
||||||
|
|
||||||
def setGeometry(self, r):
|
def setGeometry(self, *a):
|
||||||
super().setGeometry(r)
|
super().setGeometry(*a)
|
||||||
self.do_layout(r)
|
self.do_layout(self.geometry())
|
||||||
|
|
||||||
def do_splitter_moved(self, *args):
|
def do_splitter_moved(self, *args):
|
||||||
gprefs['book_details_widget_splitter_state'] = bytearray(self.saveState())
|
gprefs['book_details_widget_splitter_state'] = bytearray(self.saveState())
|
||||||
|
@ -5,12 +5,12 @@ from copy import copy
|
|||||||
from dataclasses import asdict, dataclass, fields
|
from dataclasses import asdict, dataclass, fields
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
QDialog, QHBoxLayout, QIcon, QKeySequence, QLabel, QPalette, QPointF, QSize,
|
QAction, QDialog, QHBoxLayout, QIcon, QKeySequence, QLabel, QPalette, QPointF,
|
||||||
QSizePolicy, QStyle, QStyleOption, QStylePainter, Qt, QToolButton, QVBoxLayout,
|
QSize, QSizePolicy, QStyle, QStyleOption, QStylePainter, Qt, QToolButton,
|
||||||
QWidget, pyqtSignal,
|
QVBoxLayout, QWidget, pyqtSignal,
|
||||||
)
|
)
|
||||||
|
|
||||||
from calibre.gui2 import Application, gprefs
|
from calibre.gui2 import Application, config, gprefs
|
||||||
from calibre.gui2.cover_flow import MIN_SIZE
|
from calibre.gui2.cover_flow import MIN_SIZE
|
||||||
|
|
||||||
HIDE_THRESHOLD = 10
|
HIDE_THRESHOLD = 10
|
||||||
@ -30,7 +30,9 @@ class Placeholder(QLabel):
|
|||||||
|
|
||||||
class LayoutButton(QToolButton):
|
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)
|
super().__init__(central)
|
||||||
self.central = central
|
self.central = central
|
||||||
self.label = label
|
self.label = label
|
||||||
@ -39,14 +41,30 @@ class LayoutButton(QToolButton):
|
|||||||
self.setIcon(QIcon.ic(icon))
|
self.setIcon(QIcon.ic(icon))
|
||||||
self.setCheckable(True)
|
self.setCheckable(True)
|
||||||
self.setChecked(self.is_visible)
|
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
|
@property
|
||||||
def is_visible(self):
|
def is_visible(self):
|
||||||
return getattr(self.central.is_visible, self.name)
|
return getattr(self.central.is_visible, self.name)
|
||||||
|
|
||||||
def update_shortcut(self, action_toggle=None):
|
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:
|
if action_toggle:
|
||||||
sc = ', '.join(sc.toString(QKeySequence.SequenceFormat.NativeText)
|
sc = ', '.join(sc.toString(QKeySequence.SequenceFormat.NativeText)
|
||||||
for sc in action_toggle.shortcuts())
|
for sc in action_toggle.shortcuts())
|
||||||
@ -230,23 +248,31 @@ class Visibility:
|
|||||||
setattr(self, f.name, getattr(c, f.name))
|
setattr(self, f.name, getattr(c, f.name))
|
||||||
|
|
||||||
|
|
||||||
class Central(QWidget):
|
class CentralContainer(QWidget):
|
||||||
|
|
||||||
layout: Layout = Layout.wide
|
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
|
self.prefs_name = prefs_name
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||||
self.wide_desires = WideDesires()
|
self.wide_desires = WideDesires()
|
||||||
self.narrow_desires = NarrowDesires()
|
self.narrow_desires = NarrowDesires()
|
||||||
self.is_visible = Visibility()
|
self.is_visible = Visibility()
|
||||||
self.tag_browser = Placeholder('tag browser', self)
|
if for_develop:
|
||||||
self.book_list = Placeholder('book list', self)
|
self.tag_browser = Placeholder('tag browser', self)
|
||||||
self.cover_browser = Placeholder('cover 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.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.ignore_button_toggles = False
|
||||||
self.tag_browser_button = LayoutButton('tag_browser', 'tags.png', _('Tag browser'), self, 'Shift+Alt+T')
|
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')
|
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.top_handle = h(Qt.Orientation.Horizontal)
|
||||||
self.bottom_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):
|
def serialized_settings(self):
|
||||||
return {
|
return {
|
||||||
'layout': self.layout.name,
|
'layout': self.layout.name,
|
||||||
@ -296,6 +344,7 @@ class Central(QWidget):
|
|||||||
before = self.serialized_settings()
|
before = self.serialized_settings()
|
||||||
self.unserialize_settings(gprefs.get(self.prefs_name) or {})
|
self.unserialize_settings(gprefs.get(self.prefs_name) or {})
|
||||||
if self.serialized_settings() != before:
|
if self.serialized_settings() != before:
|
||||||
|
self.update_button_states_from_visibility()
|
||||||
self.relayout()
|
self.relayout()
|
||||||
|
|
||||||
def reset_to_defaults(self):
|
def reset_to_defaults(self):
|
||||||
@ -305,6 +354,7 @@ class Central(QWidget):
|
|||||||
self.wide_desires.reset_to_defaults()
|
self.wide_desires.reset_to_defaults()
|
||||||
self.narrow_desires.reset_to_defaults()
|
self.narrow_desires.reset_to_defaults()
|
||||||
if self.serialized_settings() != before:
|
if self.serialized_settings() != before:
|
||||||
|
self.update_button_states_from_visibility()
|
||||||
self.relayout()
|
self.relayout()
|
||||||
|
|
||||||
def toggle_panel(self, which):
|
def toggle_panel(self, which):
|
||||||
@ -322,6 +372,16 @@ class Central(QWidget):
|
|||||||
setattr(self.is_visible, which, visible)
|
setattr(self.is_visible, which, visible)
|
||||||
self.update_button_states_from_visibility()
|
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):
|
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)
|
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):
|
def relayout(self):
|
||||||
self.tag_browser.setVisible(self.is_visible.tag_browser)
|
self.tag_browser.setVisible(self.is_visible.tag_browser)
|
||||||
self.book_details.setVisible(self.is_visible.book_details)
|
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.book_list.setVisible(self.is_visible.book_list)
|
||||||
self.quick_view.setVisible(self.is_visible.quick_view)
|
self.quick_view.setVisible(self.is_visible.quick_view)
|
||||||
if self.layout is Layout.wide:
|
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.layout = Layout.narrow if self.layout is Layout.wide else Layout.wide
|
||||||
self.relayout()
|
self.relayout()
|
||||||
|
|
||||||
|
def button_for(self, which):
|
||||||
|
return getattr(self, which + '_button')
|
||||||
|
|
||||||
|
def sizeHint(self):
|
||||||
|
return QSize(800, 600)
|
||||||
|
|
||||||
# Wide {{{
|
# Wide {{{
|
||||||
def wide_handle_state(self, handle):
|
def wide_handle_state(self, handle):
|
||||||
if handle is self.left_handle:
|
if handle is self.left_handle:
|
||||||
@ -401,7 +467,7 @@ class Central(QWidget):
|
|||||||
if handle is self.right_handle:
|
if handle is self.right_handle:
|
||||||
return HandleState.both_visible if self.is_visible.book_details else HandleState.only_main_visible
|
return HandleState.both_visible if self.is_visible.book_details else HandleState.only_main_visible
|
||||||
if handle is self.top_handle:
|
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.both_visible if self.is_visible.book_list else HandleState.only_side_visible
|
||||||
return HandleState.only_main_visible
|
return HandleState.only_main_visible
|
||||||
if handle is self.bottom_handle:
|
if handle is self.bottom_handle:
|
||||||
@ -443,13 +509,13 @@ class Central(QWidget):
|
|||||||
hs = h.state
|
hs = h.state
|
||||||
if hs is HandleState.both_visible or hs is HandleState.only_side_visible:
|
if hs is HandleState.both_visible or hs is HandleState.only_side_visible:
|
||||||
height = normal_handle_width
|
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
|
height = 0
|
||||||
h.resize(int(central_width), int(height))
|
h.resize(int(central_width), int(height))
|
||||||
available_height -= height
|
available_height -= height
|
||||||
|
|
||||||
cb = max(self.cover_browser.minimumHeight(), int(self.wide_desires.cover_browser_height * self.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
|
cb = 0
|
||||||
qv = bl = 0
|
qv = bl = 0
|
||||||
if cb >= available_height:
|
if cb >= available_height:
|
||||||
@ -466,7 +532,7 @@ class Central(QWidget):
|
|||||||
bl = available_height - qv
|
bl = available_height - qv
|
||||||
else:
|
else:
|
||||||
bl = available_height
|
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.cover_browser.setGeometry(int(central_x), 0, int(central_width), int(cb))
|
||||||
self.top_handle.move(central_x, cb)
|
self.top_handle.move(central_x, cb)
|
||||||
if self.is_visible.book_list:
|
if self.is_visible.book_list:
|
||||||
@ -532,7 +598,7 @@ class Central(QWidget):
|
|||||||
if handle is self.left_handle:
|
if handle is self.left_handle:
|
||||||
return HandleState.both_visible if self.is_visible.tag_browser else HandleState.only_main_visible
|
return HandleState.both_visible if self.is_visible.tag_browser else HandleState.only_main_visible
|
||||||
if handle is self.right_handle:
|
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.both_visible if self.is_visible.book_list else HandleState.only_side_visible
|
||||||
return HandleState.only_main_visible
|
return HandleState.only_main_visible
|
||||||
if handle is self.top_handle:
|
if handle is self.top_handle:
|
||||||
@ -569,12 +635,15 @@ class Central(QWidget):
|
|||||||
hs = h.state
|
hs = h.state
|
||||||
if hs is HandleState.both_visible or hs is HandleState.only_side_visible:
|
if hs is HandleState.both_visible or hs is HandleState.only_side_visible:
|
||||||
width = normal_handle_width
|
width = normal_handle_width
|
||||||
|
if h is self.right_handle and self.separate_cover_browser:
|
||||||
|
width = 0
|
||||||
h.resize(int(width), int(central_height))
|
h.resize(int(width), int(central_height))
|
||||||
available_width -= width
|
available_width -= width
|
||||||
tb = int(self.narrow_desires.tag_browser_width * self.width()) if self.is_visible.tag_browser else 0
|
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()
|
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)
|
width_to_share = max(0, available_width - min_central_width)
|
||||||
cb = int(cb * width_to_share / (tb + cb))
|
cb = int(cb * width_to_share / (tb + cb))
|
||||||
cb = max(self.cover_browser.minimumWidth(), cb)
|
cb = max(self.cover_browser.minimumWidth(), cb)
|
||||||
@ -587,7 +656,7 @@ class Central(QWidget):
|
|||||||
self.left_handle.move(tb, 0)
|
self.left_handle.move(tb, 0)
|
||||||
central_x = self.left_handle.x() + self.left_handle.width()
|
central_x = self.left_handle.x() + self.left_handle.width()
|
||||||
self.right_handle.move(tb + central_width + self.left_handle.width(), 0)
|
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.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))
|
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()
|
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]
|
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 {{{
|
# develop {{{
|
||||||
def develop():
|
def develop():
|
||||||
@ -675,7 +741,7 @@ def develop():
|
|||||||
l.setContentsMargins(0, 0, 0, 0)
|
l.setContentsMargins(0, 0, 0, 0)
|
||||||
h = QHBoxLayout()
|
h = QHBoxLayout()
|
||||||
l.addLayout(h)
|
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.tag_browser_button)
|
||||||
h.addWidget(self.central.book_details_button)
|
h.addWidget(self.central.book_details_button)
|
||||||
h.addWidget(self.central.cover_browser_button)
|
h.addWidget(self.central.cover_browser_button)
|
||||||
|
@ -353,8 +353,12 @@ class CoverFlowMixin:
|
|||||||
|
|
||||||
disable_cover_browser_refresh = False
|
disable_cover_browser_refresh = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cb_button(self):
|
||||||
|
return self.layout_container.cover_browser_button
|
||||||
|
|
||||||
def one_auto_scroll(self):
|
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:
|
if cb_visible:
|
||||||
self.cover_flow.one_auto_scroll()
|
self.cover_flow.one_auto_scroll()
|
||||||
else:
|
else:
|
||||||
@ -388,18 +392,17 @@ class CoverFlowMixin:
|
|||||||
self.cover_flow.setImages(self.db_images)
|
self.cover_flow.setImages(self.db_images)
|
||||||
self.cover_flow.itemActivated.connect(self.iactions['View'].view_specific_book)
|
self.cover_flow.itemActivated.connect(self.iactions['View'].view_specific_book)
|
||||||
self.update_cover_flow_subtitle_font()
|
self.update_cover_flow_subtitle_font()
|
||||||
|
button = self.cb_button
|
||||||
if self.separate_cover_browser:
|
if self.separate_cover_browser:
|
||||||
self.separate_cover_browser = True
|
button.clicked.connect(self.toggle_cover_browser)
|
||||||
self.cb_splitter.button.clicked.connect(self.toggle_cover_browser)
|
button.set_state_to_show()
|
||||||
self.cb_splitter.button.set_state_to_show()
|
button.on_action_trigger.connect(self.toggle_cover_browser)
|
||||||
self.cb_splitter.action_toggle.triggered.connect(self.toggle_cover_browser)
|
|
||||||
self.cover_flow.stop.connect(self.hide_cover_browser)
|
self.cover_flow.stop.connect(self.hide_cover_browser)
|
||||||
self.cover_flow.setVisible(False)
|
self.cover_flow.setVisible(False)
|
||||||
else:
|
else:
|
||||||
self.separate_cover_browser = False
|
self.cover_flow.stop.connect(button.set_state_to_hide)
|
||||||
self.cb_splitter.insertWidget(self.cb_splitter.side_index, self.cover_flow)
|
self.layout_container.set_widget('cover_browser', self.cover_flow)
|
||||||
self.cover_flow.stop.connect(self.cb_splitter.hide_side_pane)
|
button.toggled.connect(self.cover_browser_toggled, type=Qt.ConnectionType.QueuedConnection)
|
||||||
self.cb_splitter.button.toggled.connect(self.cover_browser_toggled, type=Qt.ConnectionType.QueuedConnection)
|
|
||||||
|
|
||||||
def update_cover_flow_subtitle_font(self):
|
def update_cover_flow_subtitle_font(self):
|
||||||
db = self.current_db.new_api
|
db = self.current_db.new_api
|
||||||
@ -419,7 +422,7 @@ class CoverFlowMixin:
|
|||||||
self.show_cover_browser()
|
self.show_cover_browser()
|
||||||
|
|
||||||
def cover_browser_toggled(self, *args):
|
def cover_browser_toggled(self, *args):
|
||||||
if self.cb_splitter.button.isChecked():
|
if self.cb_button.isChecked():
|
||||||
self.cover_browser_shown()
|
self.cover_browser_shown()
|
||||||
else:
|
else:
|
||||||
self.cover_browser_hidden()
|
self.cover_browser_hidden()
|
||||||
@ -447,25 +450,25 @@ class CoverFlowMixin:
|
|||||||
|
|
||||||
def show_cover_browser(self):
|
def show_cover_browser(self):
|
||||||
d = CBDialog(self, self.cover_flow)
|
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.setVisible(True)
|
||||||
self.cover_flow.setFocus(Qt.FocusReason.OtherFocusReason)
|
self.cover_flow.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||||
d.show_fullscreen() if gprefs['cb_fullscreen'] else d.show()
|
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)
|
d.closed.connect(self.cover_browser_closed)
|
||||||
self.cb_dialog = d
|
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):
|
def cover_browser_closed(self, *args):
|
||||||
self.cb_dialog = None
|
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):
|
def hide_cover_browser(self, *args):
|
||||||
cbd = getattr(self, 'cb_dialog', None)
|
cbd = getattr(self, 'cb_dialog', None)
|
||||||
if cbd is not None:
|
if cbd is not None:
|
||||||
cbd.accept()
|
cbd.accept()
|
||||||
self.cb_dialog = None
|
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):
|
def is_cover_browser_visible(self):
|
||||||
try:
|
try:
|
||||||
@ -473,7 +476,7 @@ class CoverFlowMixin:
|
|||||||
return self.cover_flow.isVisible()
|
return self.cover_flow.isVisible()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return False # called before init_cover_flow_mixin
|
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):
|
def refresh_cover_browser(self):
|
||||||
if self.disable_cover_browser_refresh:
|
if self.disable_cover_browser_refresh:
|
||||||
|
@ -269,7 +269,7 @@ class Quickview(QDialog, Ui_Quickview):
|
|||||||
# Remove the ampersands from the buttons because shortcuts exist.
|
# Remove the ampersands from the buttons because shortcuts exist.
|
||||||
self.lock_qv.setText(_('Lock Quickview contents'))
|
self.lock_qv.setText(_('Lock Quickview contents'))
|
||||||
self.refresh_button.setText(_('Refresh'))
|
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)
|
self.close_button.setVisible(False)
|
||||||
else:
|
else:
|
||||||
self.dock_button.setToolTip(_('Embed the Quickview panel into the main calibre window'))
|
self.dock_button.setToolTip(_('Embed the Quickview panel into the main calibre window'))
|
||||||
@ -324,7 +324,7 @@ class Quickview(QDialog, Ui_Quickview):
|
|||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
def item_doubleclicked(self, item):
|
def item_doubleclicked(self, item):
|
||||||
tb = self.gui.stack.tb_widget
|
tb = self.gui.tb_widget
|
||||||
tb.set_focus_to_find_box()
|
tb.set_focus_to_find_box()
|
||||||
tb.item_search.lineEdit().setText(self.current_key + ':=' + item.text())
|
tb.item_search.lineEdit().setText(self.current_key + ':=' + item.text())
|
||||||
tb.do_find()
|
tb.do_find()
|
||||||
@ -456,7 +456,7 @@ class Quickview(QDialog, Ui_Quickview):
|
|||||||
def show(self):
|
def show(self):
|
||||||
QDialog.show(self)
|
QDialog.show(self)
|
||||||
if self.is_pane:
|
if self.is_pane:
|
||||||
self.gui.quickview_splitter.show_quickview_widget()
|
self.gui.show_panel('quick_view')
|
||||||
|
|
||||||
def show_as_pane_changed(self):
|
def show_as_pane_changed(self):
|
||||||
gprefs['quickview_is_pane'] = not gprefs.get('quickview_is_pane', False)
|
gprefs['quickview_is_pane'] = not gprefs.get('quickview_is_pane', False)
|
||||||
@ -707,10 +707,6 @@ class Quickview(QDialog, Ui_Quickview):
|
|||||||
def resizeEvent(self, *args):
|
def resizeEvent(self, *args):
|
||||||
QDialog.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:
|
if self.books_table_column_widths is not None:
|
||||||
for c,w in enumerate(self.books_table_column_widths):
|
for c,w in enumerate(self.books_table_column_widths):
|
||||||
self.books_table.setColumnWidth(c, w)
|
self.books_table.setColumnWidth(c, w)
|
||||||
@ -868,7 +864,7 @@ class Quickview(QDialog, Ui_Quickview):
|
|||||||
|
|
||||||
def _reject(self):
|
def _reject(self):
|
||||||
if self.is_pane:
|
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.gui.library_view.setFocus(Qt.FocusReason.ActiveWindowFocusReason)
|
||||||
self._close()
|
self._close()
|
||||||
QDialog.reject(self)
|
QDialog.reject(self)
|
||||||
|
@ -7,23 +7,21 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import functools
|
import functools
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
QAction, QApplication, QDialog, QEvent, QIcon, QLabel, QMenu, QPixmap, QSizePolicy,
|
QAction, QApplication, QDialog, QEvent, QIcon, QLabel, QMenu, QPixmap,
|
||||||
QSplitter, QStackedWidget, QStatusBar, QStyle, QStyleOption, QStylePainter, Qt,
|
QStackedWidget, QStatusBar, QStyle, QStyleOption, QStylePainter, Qt, QTabBar,
|
||||||
QTabBar, QTimer, QToolButton, QUrl, QVBoxLayout, QWidget,
|
QTimer, QToolButton, QUrl,
|
||||||
)
|
)
|
||||||
|
|
||||||
from calibre.constants import get_appname_for_display, get_version, ismacos
|
from calibre.constants import get_appname_for_display, get_version, ismacos
|
||||||
from calibre.customize.ui import find_plugin
|
from calibre.customize.ui import find_plugin
|
||||||
from calibre.gui2 import (
|
from calibre.gui2 import config, error_dialog, gprefs, open_local_file, open_url
|
||||||
config, error_dialog, gprefs, is_widescreen, open_local_file, open_url,
|
|
||||||
)
|
|
||||||
from calibre.gui2.book_details import BookDetails
|
from calibre.gui2.book_details import BookDetails
|
||||||
|
from calibre.gui2.central import CentralContainer, LayoutButton
|
||||||
from calibre.gui2.layout_menu import LayoutMenu
|
from calibre.gui2.layout_menu import LayoutMenu
|
||||||
from calibre.gui2.library.alternate_views import GridView
|
from calibre.gui2.library.alternate_views import GridView
|
||||||
from calibre.gui2.library.views import BooksView, DeviceBooksView
|
from calibre.gui2.library.views import BooksView, DeviceBooksView
|
||||||
from calibre.gui2.notify import get_notifier
|
from calibre.gui2.notify import get_notifier
|
||||||
from calibre.gui2.tag_browser.ui import TagBrowserWidget
|
from calibre.gui2.tag_browser.ui import TagBrowserWidget
|
||||||
from calibre.gui2.widgets import LayoutButton, Splitter
|
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.localization import localize_website_link, ngettext
|
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): # {{{
|
class UpdateLabel(QLabel): # {{{
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -220,7 +119,6 @@ class UpdateLabel(QLabel): # {{{
|
|||||||
pass
|
pass
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class VersionLabel(QLabel): # {{{
|
class VersionLabel(QLabel): # {{{
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
@ -258,7 +156,6 @@ class VersionLabel(QLabel): # {{{
|
|||||||
return QLabel.paintEvent(self, ev)
|
return QLabel.paintEvent(self, ev)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class StatusBar(QStatusBar): # {{{
|
class StatusBar(QStatusBar): # {{{
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
@ -327,12 +224,11 @@ class StatusBar(QStatusBar): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class GridViewButton(LayoutButton): # {{{
|
class GridViewButton(LayoutButton): # {{{
|
||||||
|
|
||||||
def __init__(self, gui):
|
def __init__(self, gui):
|
||||||
sc = 'Alt+Shift+G'
|
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.set_state_to_show()
|
||||||
self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self)
|
self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self)
|
||||||
gui.addAction(self.action_toggle)
|
gui.addAction(self.action_toggle)
|
||||||
@ -342,6 +238,10 @@ class GridViewButton(LayoutButton): # {{{
|
|||||||
self.action_toggle.changed.connect(self.update_shortcut)
|
self.action_toggle.changed.connect(self.update_shortcut)
|
||||||
self.toggled.connect(self.update_state)
|
self.toggled.connect(self.update_state)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_visible(self):
|
||||||
|
return self.isChecked()
|
||||||
|
|
||||||
def update_state(self, checked):
|
def update_state(self, checked):
|
||||||
if checked:
|
if checked:
|
||||||
self.set_state_to_hide()
|
self.set_state_to_hide()
|
||||||
@ -362,7 +262,7 @@ class SearchBarButton(LayoutButton): # {{{
|
|||||||
|
|
||||||
def __init__(self, gui):
|
def __init__(self, gui):
|
||||||
sc = 'Alt+Shift+F'
|
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.set_state_to_hide()
|
||||||
self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self)
|
self.action_toggle = QAction(self.icon(), _('Toggle') + ' ' + self.label, self)
|
||||||
gui.addAction(self.action_toggle)
|
gui.addAction(self.action_toggle)
|
||||||
@ -372,6 +272,10 @@ class SearchBarButton(LayoutButton): # {{{
|
|||||||
self.action_toggle.changed.connect(self.update_shortcut)
|
self.action_toggle.changed.connect(self.update_shortcut)
|
||||||
self.toggled.connect(self.update_state)
|
self.toggled.connect(self.update_state)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_visible(self):
|
||||||
|
return self.isChecked()
|
||||||
|
|
||||||
def update_state(self, checked):
|
def update_state(self, checked):
|
||||||
if checked:
|
if checked:
|
||||||
self.set_state_to_hide()
|
self.set_state_to_hide()
|
||||||
@ -560,7 +464,6 @@ class VLTabs(QTabBar): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class LayoutMixin: # {{{
|
class LayoutMixin: # {{{
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -569,35 +472,34 @@ class LayoutMixin: # {{{
|
|||||||
def init_layout_mixin(self):
|
def init_layout_mixin(self):
|
||||||
self.vl_tabs = VLTabs(self)
|
self.vl_tabs = VLTabs(self)
|
||||||
self.centralwidget.layout().addWidget(self.vl_tabs)
|
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 {{{
|
if self.layout_container.is_wide:
|
||||||
self.book_details = BookDetails(False, self)
|
self.button_order = 'sb', 'tb', 'cb', 'gv', 'qv', 'bd'
|
||||||
self.stack = Stack(self)
|
else:
|
||||||
self.bd_splitter = Splitter('book_details_splitter',
|
self.button_order = 'sb', 'tb', 'bd', 'gv', 'cb', 'qv'
|
||||||
_('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')
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# This must use the base method to find the plugin because it hasn't
|
# This must use the base method to find the plugin because it hasn't
|
||||||
# been fully initialized yet
|
# been fully initialized yet
|
||||||
@ -605,6 +507,9 @@ class LayoutMixin: # {{{
|
|||||||
if self.qv and self.qv.actual_plugin_:
|
if self.qv and self.qv.actual_plugin_:
|
||||||
self.qv = 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)
|
self.status_bar = StatusBar(self)
|
||||||
stylename = str(self.style().objectName())
|
stylename = str(self.style().objectName())
|
||||||
self.grid_view_button = GridViewButton(self)
|
self.grid_view_button = GridViewButton(self)
|
||||||
@ -613,40 +518,33 @@ class LayoutMixin: # {{{
|
|||||||
self.search_bar_button.toggled.connect(self.toggle_search_bar)
|
self.search_bar_button.toggled.connect(self.toggle_search_bar)
|
||||||
|
|
||||||
self.layout_buttons = []
|
self.layout_buttons = []
|
||||||
for x in button_order:
|
for x in self.button_order:
|
||||||
if hasattr(self, x + '_splitter'):
|
if x == 'gv':
|
||||||
button = getattr(self, x + '_splitter').button
|
button = self.grid_view_button
|
||||||
|
elif x == 'sb':
|
||||||
|
button = self.search_bar_button
|
||||||
else:
|
else:
|
||||||
if x == 'gv':
|
button = self.layout_container.button_for({
|
||||||
button = self.grid_view_button
|
'tb': 'tag_browser', 'bd': 'book_details', 'cb': 'cover_browser', 'qv': 'quick_view'
|
||||||
elif x == 'qv':
|
}[x])
|
||||||
if self.qv is None:
|
|
||||||
continue
|
|
||||||
button = self.qv.qv_button
|
|
||||||
else:
|
|
||||||
button = self.search_bar_button
|
|
||||||
self.layout_buttons.append(button)
|
self.layout_buttons.append(button)
|
||||||
button.setVisible(False)
|
button.setVisible(gprefs['show_layout_buttons'])
|
||||||
if ismacos and stylename != 'Calibre':
|
if ismacos and stylename != 'Calibre':
|
||||||
button.setStyleSheet('''
|
button.setStyleSheet('''
|
||||||
QToolButton { background: none; border:none; padding: 0px; }
|
QToolButton { background: none; border:none; padding: 0px; }
|
||||||
QToolButton:checked { background: rgba(0, 0, 0, 25%); }
|
QToolButton:checked { background: rgba(0, 0, 0, 25%); }
|
||||||
''')
|
''')
|
||||||
self.status_bar.addPermanentWidget(button)
|
self.status_bar.addPermanentWidget(button)
|
||||||
if gprefs['show_layout_buttons']:
|
self.layout_button = b = QToolButton(self)
|
||||||
for b in self.layout_buttons:
|
b.setAutoRaise(True), b.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||||
b.setVisible(True)
|
b.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
||||||
self.status_bar.addPermanentWidget(b)
|
b.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
|
||||||
else:
|
b.setText(_('Layout')), b.setIcon(QIcon.ic('layout.png'))
|
||||||
self.layout_button = b = QToolButton(self)
|
b.setMenu(LayoutMenu(self))
|
||||||
b.setAutoRaise(True), b.setCursor(Qt.CursorShape.PointingHandCursor)
|
b.setToolTip(_(
|
||||||
b.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
'Show and hide various parts of the calibre main window'))
|
||||||
b.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
|
self.status_bar.addPermanentWidget(b)
|
||||||
b.setText(_('Layout')), b.setIcon(QIcon.ic('layout.png'))
|
b.setVisible(not gprefs['show_layout_buttons'])
|
||||||
b.setMenu(LayoutMenu(self))
|
|
||||||
b.setToolTip(_(
|
|
||||||
'Show and hide various parts of the calibre main window'))
|
|
||||||
self.status_bar.addPermanentWidget(b)
|
|
||||||
self.status_bar.addPermanentWidget(self.jobs_button)
|
self.status_bar.addPermanentWidget(self.jobs_button)
|
||||||
self.setStatusBar(self.status_bar)
|
self.setStatusBar(self.status_bar)
|
||||||
self.status_bar.update_label.linkActivated.connect(self.update_link_clicked)
|
self.status_bar.update_label.linkActivated.connect(self.update_link_clicked)
|
||||||
@ -698,6 +596,12 @@ class LayoutMixin: # {{{
|
|||||||
self.library_view.currentIndex())
|
self.library_view.currentIndex())
|
||||||
self.library_view.setFocus(Qt.FocusReason.OtherFocusReason)
|
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=''):
|
def set_search_string_with_append(self, expression, append=''):
|
||||||
current = self.search.text().strip()
|
current = self.search.text().strip()
|
||||||
if append:
|
if append:
|
||||||
@ -727,7 +631,7 @@ class LayoutMixin: # {{{
|
|||||||
|
|
||||||
def find_in_tag_browser_triggered(self, field, value):
|
def find_in_tag_browser_triggered(self, field, value):
|
||||||
if field and value:
|
if field and value:
|
||||||
tb = self.stack.tb_widget
|
tb = self.tb_widget
|
||||||
tb.set_focus_to_find_box()
|
tb.set_focus_to_find_box()
|
||||||
tb.item_search.lineEdit().setText(field + ':=' + value)
|
tb.item_search.lineEdit().setText(field + ':=' + value)
|
||||||
tb.do_find()
|
tb.do_find()
|
||||||
@ -812,22 +716,15 @@ class LayoutMixin: # {{{
|
|||||||
for x in ('library', 'memory', 'card_a', 'card_b'):
|
for x in ('library', 'memory', 'card_a', 'card_b'):
|
||||||
getattr(self, x+'_view').save_state()
|
getattr(self, x+'_view').save_state()
|
||||||
|
|
||||||
for x in ('cb', 'tb', 'bd'):
|
self.layout_container.write_settings()
|
||||||
s = getattr(self, x+'_splitter')
|
|
||||||
s.update_desired_state()
|
|
||||||
s.save_state()
|
|
||||||
self.grid_view_button.save_state()
|
self.grid_view_button.save_state()
|
||||||
self.search_bar_button.save_state()
|
self.search_bar_button.save_state()
|
||||||
if self.qv:
|
|
||||||
self.qv.qv_button.save_state()
|
|
||||||
|
|
||||||
def read_layout_settings(self):
|
def read_layout_settings(self):
|
||||||
# View states are restored automatically when set_database is called
|
# View states are restored automatically when set_database is called
|
||||||
for x in ('cb', 'tb', 'bd'):
|
self.layout_container.read_settings()
|
||||||
getattr(self, x+'_splitter').restore_state()
|
|
||||||
self.grid_view_button.restore_state()
|
self.grid_view_button.restore_state()
|
||||||
self.search_bar_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):
|
def update_status_bar(self, *args):
|
||||||
v = self.current_view()
|
v = self.current_view()
|
||||||
|
@ -17,7 +17,6 @@ import sys
|
|||||||
import textwrap
|
import textwrap
|
||||||
import time
|
import time
|
||||||
from collections import OrderedDict, deque
|
from collections import OrderedDict, deque
|
||||||
from functools import partial
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
QAction, QApplication, QDialog, QFont, QIcon, QMenu, QSystemTrayIcon, Qt, QTimer,
|
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.search_restriction_mixin import SearchRestrictionMixin
|
||||||
from calibre.gui2.tag_browser.ui import TagBrowserMixin
|
from calibre.gui2.tag_browser.ui import TagBrowserMixin
|
||||||
from calibre.gui2.update import UpdateMixin
|
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.library import current_library_name
|
||||||
from calibre.srv.library_broker import GuiLibraryBroker, db_matches
|
from calibre.srv.library_broker import GuiLibraryBroker, db_matches
|
||||||
from calibre.utils.config import dynamic, prefs
|
from calibre.utils.config import dynamic, prefs
|
||||||
@ -398,7 +397,13 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
do_hide_windows = True
|
do_hide_windows = True
|
||||||
show_gui = False
|
show_gui = False
|
||||||
setattr(self, '__systray_minimized', True)
|
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.read_settings()
|
||||||
|
|
||||||
self.finalize_layout()
|
self.finalize_layout()
|
||||||
@ -430,43 +435,12 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
self.iactions['Connect Share'].check_smartdevice_menus()
|
self.iactions['Connect Share'].check_smartdevice_menus()
|
||||||
QTimer.singleShot(100, self.update_toggle_to_tray_action)
|
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
|
# Various post-initialization actions after an event loop tick
|
||||||
|
self.listener.start_listening()
|
||||||
|
self.start_smartdevice()
|
||||||
# Collect cycles now
|
# Collect cycles now
|
||||||
gc.collect()
|
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()
|
self.focus_library_view()
|
||||||
|
|
||||||
def show_gui_debug_msg(self):
|
def show_gui_debug_msg(self):
|
||||||
@ -498,14 +472,15 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
timed_print('Starting the smartdevice driver')
|
timed_print('Starting the smartdevice driver')
|
||||||
message = None
|
message = None
|
||||||
if self.device_manager.get_option('smartdevice', 'autostart'):
|
if self.device_manager.get_option('smartdevice', 'autostart'):
|
||||||
try:
|
with BusyCursor():
|
||||||
message = self.device_manager.start_plugin('smartdevice')
|
try:
|
||||||
timed_print('Finished starting smartdevice')
|
message = self.device_manager.start_plugin('smartdevice')
|
||||||
except Exception as e:
|
timed_print('Finished starting smartdevice')
|
||||||
message = str(e)
|
except Exception as e:
|
||||||
timed_print(f'Starting smartdevice driver failed: {message}')
|
message = str(e)
|
||||||
import traceback
|
timed_print(f'Starting smartdevice driver failed: {message}')
|
||||||
traceback.print_exc()
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
if message:
|
if message:
|
||||||
if not self.device_manager.is_running('Wireless Devices'):
|
if not self.device_manager.is_running('Wireless Devices'):
|
||||||
error_dialog(self, _('Problem starting the wireless device'),
|
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
|
page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3
|
||||||
self.stack.setCurrentIndex(page)
|
self.stack.setCurrentIndex(page)
|
||||||
self.book_details.reset_info()
|
self.book_details.reset_info()
|
||||||
for x in ('tb', 'cb'):
|
self.layout_container.tag_browser_button.setEnabled(location == 'library')
|
||||||
splitter = getattr(self, x+'_splitter')
|
self.layout_container.cover_browser_button.setEnabled(location == 'library')
|
||||||
splitter.button.setEnabled(location == 'library')
|
|
||||||
for action in self.iactions.values():
|
for action in self.iactions.values():
|
||||||
action.location_selected(location)
|
action.location_selected(location)
|
||||||
if location == 'library':
|
if location == 'library':
|
||||||
@ -1168,7 +1142,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
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.stack.tb_widget.save_state()
|
self.tb_widget.save_state()
|
||||||
|
|
||||||
def quit(self, checked=True, restart=False, debug_on_restart=False,
|
def quit(self, checked=True, restart=False, debug_on_restart=False,
|
||||||
confirm_quit=True, no_plugins_on_restart=False):
|
confirm_quit=True, no_plugins_on_restart=False):
|
||||||
|
@ -6,11 +6,11 @@ Miscellaneous widgets used in the GUI
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
QAction, QApplication, QClipboard, QColor, QComboBox, QCompleter, QCursor, QEvent,
|
QApplication, QClipboard, QColor, QComboBox, QCompleter, QCursor, QEvent, QFont,
|
||||||
QFont, QGraphicsScene, QGraphicsView, QIcon, QKeySequence, QLabel, QLineEdit,
|
QGraphicsScene, QGraphicsView, QIcon, QLabel, QLineEdit, QListWidget,
|
||||||
QListWidget, QListWidgetItem, QMenu, QPageSize, QPainter, QPalette, QPen, QPixmap,
|
QListWidgetItem, QMenu, QPageSize, QPainter, QPalette, QPen, QPixmap, QPrinter,
|
||||||
QPrinter, QRect, QSize, QSplitter, QSplitterHandle, QStringListModel,
|
QRect, QSize, QSplitterHandle, QStringListModel, QSyntaxHighlighter, Qt,
|
||||||
QSyntaxHighlighter, Qt, QTextCharFormat, QTimer, QToolButton, QWidget, pyqtSignal,
|
QTextCharFormat, QWidget, pyqtSignal,
|
||||||
)
|
)
|
||||||
|
|
||||||
from calibre import fit_image, force_unicode, strftime
|
from calibre import fit_image, force_unicode, strftime
|
||||||
@ -1063,276 +1063,6 @@ class SplitterHandle(QSplitterHandle):
|
|||||||
self.double_clicked.emit(self)
|
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): # {{{
|
class PaperSizes(QComboBox): # {{{
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user