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 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))
|
||||
|
||||
|
@ -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):
|
||||
'''
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
|
@ -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): # {{{
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user