This commit is contained in:
Kovid Goyal 2022-10-26 19:48:12 +05:30
commit 2fb78716f9
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
10 changed files with 112 additions and 60 deletions

View File

@ -8,9 +8,9 @@ from collections import namedtuple
from functools import partial
from qt.core import (
QAction, QApplication, QClipboard, QColor, QDialog, QEasingCurve, QIcon,
QKeySequence, QLayout, QMenu, QMimeData, QPainter, QPen, QPixmap,
QKeySequence, QMenu, QMimeData, QPainter, QPen, QPixmap,
QPropertyAnimation, QRect, QSize, QSizePolicy, Qt, QUrl, QWidget, pyqtProperty,
pyqtSignal
QTimer, pyqtSignal
)
from calibre import fit_image, sanitize_file_name
@ -30,6 +30,7 @@ from calibre.gui2.dialogs.confirm_delete import confirm, confirm as confirm_dele
from calibre.gui2.dnd import (
dnd_get_files, dnd_get_image, dnd_has_extension, dnd_has_image, image_extensions
)
from calibre.gui2.widgets import BasicSplitter
from calibre.gui2.widgets2 import HTMLDisplay
from calibre.utils.config import tweaks
from calibre.utils.img import blend_image, image_from_x
@ -548,6 +549,9 @@ class CoverView(QWidget): # {{{
def setCurrentPixmapSize(self, val):
self._current_pixmap_size = val
def minimumSizeHint(self):
return QSize(100, 100)
def do_layout(self):
if self.rect().width() == 0 or self.rect().height() == 0:
return
@ -652,7 +656,7 @@ class CoverView(QWidget): # {{{
book_id = self.data.get('id')
if not book_id:
return
from calibre.utils.img import image_from_x, remove_borders_from_image
from calibre.utils.img import remove_borders_from_image
img = image_from_x(self.pixmap)
nimg = remove_borders_from_image(img)
if nimg is not img:
@ -910,54 +914,56 @@ class BookInfo(HTMLDisplay):
# }}}
class DetailsLayout(QLayout): # {{{
class DetailsLayout(BasicSplitter): # {{{
def __init__(self, vertical, parent):
QLayout.__init__(self, parent)
orientation = Qt.Orientation.Vertical if vertical else Qt.Orientation.Horizontal
BasicSplitter.__init__(self, orientation, parent)
self.vertical = vertical
self.setCollapsible(0, True)
self._children = []
self.min_size = QSize(190, 200) if vertical else QSize(120, 120)
self.setContentsMargins(0, 0, 0, 0)
self.restore_geometry(gprefs, 'book_details_splitter')
self.splitterMoved.connect(self.do_splitter_moved)
def minimumSize(self):
return QSize(self.min_size)
def addItem(self, child):
def addWidget(self, child):
if len(self._children) > 2:
raise ValueError('This layout can only manage two children')
self._children.append(child)
def itemAt(self, i):
try:
return self._children[i]
except:
pass
return None
def takeAt(self, i):
try:
self._children.pop(i)
except:
pass
return None
def count(self):
return len(self._children)
def sizeHint(self):
return QSize(self.min_size)
def restore_splitter_state(self):
s = gprefs.get('book_details_widget_splitter_state')
if s is not None:
self.restoreState(s)
self.setOrientation(Qt.Orientation.Vertical if self.vertical else Qt.Orientation.Horizontal)
def setGeometry(self, r):
QLayout.setGeometry(self, r)
BasicSplitter.setGeometry(self, r)
self.do_layout(r)
self.restore_splitter_state()
def do_splitter_moved(self, *args):
gprefs['book_details_widget_splitter_state'] = bytearray(self.saveState())
self._children[0].do_layout()
def cover_height(self, r):
if not self._children[0].widget().isVisible():
if not self._children[0].isVisible():
return 0
mh = min(int(r.height()//2), int(4/3 * r.width())+1)
try:
ph = self._children[0].widget().pixmap.height()
ph = self._children[0].pixmap.height()
except:
ph = 0
if ph > 0:
@ -965,11 +971,11 @@ class DetailsLayout(QLayout): # {{{
return mh
def cover_width(self, r):
if not self._children[0].widget().isVisible():
if not self._children[0].isVisible():
return 0
mw = 1 + int(3/4 * r.height())
try:
pw = self._children[0].widget().pixmap.width()
pw = self._children[0].pixmap.width()
except:
pw = 0
if pw > 0:
@ -979,7 +985,11 @@ class DetailsLayout(QLayout): # {{{
def do_layout(self, rect):
if len(self._children) != 2:
return
left, top, right, bottom = self.getContentsMargins()
cm = self.contentsMargins()
left = cm.left()
top = cm.top()
right = cm.right()
bottom = cm.top()
r = rect.adjusted(+left, +top, -right, -bottom)
x = r.x()
y = r.y()
@ -987,20 +997,20 @@ class DetailsLayout(QLayout): # {{{
if self.vertical:
ch = self.cover_height(r)
cover.setGeometry(QRect(x, y, r.width(), ch))
cover.widget().do_layout()
y += ch + 5
details.setGeometry(QRect(x, y, r.width(), r.height()-ch-5))
else:
cw = self.cover_width(r)
cover.setGeometry(QRect(x, y, cw, r.height()))
cover.widget().do_layout()
x += cw + 5
details.setGeometry(QRect(x, y, r.width() - cw - 5, r.height()))
self.restore_splitter_state() # only required on first call to do_layout, but ...
self.save_geometry(gprefs, 'book_details_splitter')
cover.do_layout()
# }}}
class BookDetails(QWidget): # {{{
class BookDetails(DetailsLayout): # {{{
show_book_info = pyqtSignal()
open_containing_folder = pyqtSignal(int)
@ -1072,11 +1082,10 @@ class BookDetails(QWidget): # {{{
# }}}
def __init__(self, vertical, parent=None):
QWidget.__init__(self, parent)
DetailsLayout.__init__(self, vertical, parent)
self.last_data = {}
self.setAcceptDrops(True)
self._layout = DetailsLayout(vertical, self)
self.setLayout(self._layout)
self._layout = self
self.current_path = ''
self.cover_view = CoverView(vertical, self)

View File

@ -6,7 +6,7 @@ import textwrap
from qt.core import (
QAction, QApplication, QBrush, QCheckBox, QDialog, QGridLayout,
QHBoxLayout, QIcon, QKeySequence, QLabel, QListView, QModelIndex, QPalette,
QPixmap, QPushButton, QShortcut, QSize, QSplitter, Qt, QTimer, QToolButton,
QPixmap, QPushButton, QShortcut, QSize, Qt, QTimer, QToolButton,
QVBoxLayout, QWidget, pyqtSignal, QDialogButtonBox
)
@ -17,7 +17,7 @@ from calibre.gui2.book_details import (
set_html
)
from calibre.gui2.ui import get_gui
from calibre.gui2.widgets import CoverView
from calibre.gui2.widgets import CoverView, BasicSplitter
from calibre.gui2.widgets2 import Dialog, HTMLDisplay
@ -136,7 +136,7 @@ class BookInfo(QDialog):
QDialog.__init__(self, parent)
self.marked = None
self.gui = parent
self.splitter = QSplitter(self)
self.splitter = BasicSplitter(self)
self._l = l = QVBoxLayout(self)
self.setLayout(l)
l.addWidget(self.splitter)

View File

@ -9,7 +9,7 @@ import os
import weakref
from qt.core import (
QApplication, QCheckBox, QCursor, QDialog, QDialogButtonBox, QGridLayout,
QHBoxLayout, QIcon, QLabel, QLineEdit, QProgressBar, QPushButton, QSplitter,
QHBoxLayout, QIcon, QLabel, QLineEdit, QProgressBar, QPushButton,
QStackedLayout, Qt, QTextEdit, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
QWidget, pyqtSignal
)
@ -17,6 +17,7 @@ from threading import Thread
from calibre import as_unicode, prints
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.widgets import BasicSplitter
from calibre.library.check_library import CHECKS, CheckLibrary
from calibre.utils.recycle_bin import delete_file, delete_tree
@ -125,13 +126,14 @@ class CheckLibraryDialog(QDialog):
self._tl = QHBoxLayout()
self.setLayout(self._tl)
self.splitter = QSplitter(self)
self.splitter = BasicSplitter(self)
self.left = QWidget(self)
self.splitter.addWidget(self.left)
self.helpw = QTextEdit(self)
self.splitter.addWidget(self.helpw)
self._tl.addWidget(self.splitter)
self._layout = QVBoxLayout()
self._layout.setContentsMargins(0, 0, 0, 0)
self.left.setLayout(self._layout)
self.helpw.setReadOnly(True)
self.helpw.setText(_('''\

View File

@ -23,7 +23,7 @@ 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.gui2.widgets import LayoutButton, Splitter, BasicSplitter
from calibre.utils.config import prefs
from calibre.utils.icu import sort_key
from calibre.utils.localization import localize_website_link
@ -112,10 +112,10 @@ class LibraryViewMixin: # {{{
# }}}
class QuickviewSplitter(QSplitter): # {{{
class QuickviewSplitter(BasicSplitter): # {{{
def __init__(self, parent=None, orientation=Qt.Orientation.Vertical, qv_widget=None):
QSplitter.__init__(self, parent=parent, orientation=orientation)
BasicSplitter.__init__(self, parent=parent, orientation=orientation)
self.splitterMoved.connect(self.splitter_moved)
self.setChildrenCollapsible(False)
self.qv_widget = qv_widget
@ -124,7 +124,7 @@ class QuickviewSplitter(QSplitter): # {{{
gprefs['quickview_dialog_heights'] = self.sizes()
def resizeEvent(self, *args):
QSplitter.resizeEvent(self, *args)
BasicSplitter.resizeEvent(self, *args)
if self.sizes()[1] != 0:
gprefs['quickview_dialog_heights'] = self.sizes()

View File

@ -12,7 +12,7 @@ from functools import partial
from qt.core import (
QDialog, QDialogButtonBox, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QIcon,
QInputDialog, QKeySequence, QMenu, QPushButton, QScrollArea, QShortcut, QSize,
QSizePolicy, QSpacerItem, QSplitter, Qt, QTabWidget, QToolButton, QVBoxLayout,
QSizePolicy, QSpacerItem, Qt, QTabWidget, QToolButton, QVBoxLayout,
QWidget, pyqtSignal
)
@ -29,6 +29,7 @@ from calibre.gui2.metadata.basic_widgets import (
TitleSortEdit, show_locked_file_error
)
from calibre.gui2.metadata.single_download import FullFetch
from calibre.gui2.widgets import BasicSplitter
from calibre.gui2.widgets2 import CenteredToolButton
from calibre.library.comments import merge_comments as merge_two_comments
from calibre.utils.date import local_tz
@ -731,13 +732,13 @@ class MetadataSingleDialogBase(QDialog):
# }}}
class Splitter(QSplitter):
class Splitter(BasicSplitter):
frame_resized = pyqtSignal(object)
def resizeEvent(self, ev):
self.frame_resized.emit(ev)
return QSplitter.resizeEvent(self, ev)
return BasicSplitter.resizeEvent(self, ev)
class MetadataSingleDialog(MetadataSingleDialogBase): # {{{
@ -1039,7 +1040,7 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
QSizePolicy.Policy.Expanding))
wgl.addWidget(self.formats_manager)
self.splitter = QSplitter(Qt.Orientation.Horizontal, tab1)
self.splitter = BasicSplitter(Qt.Orientation.Horizontal, tab1)
tab1.l.addWidget(self.splitter)
self.splitter.addWidget(self.cover)
self.splitter.addWidget(wsp)
@ -1207,7 +1208,7 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
cover_layout.addLayout(hl)
sto(self.cover.buttons[-2], self.cover.buttons[-1])
# Splitter for both cover & formats boxes
self.cover_and_formats = cover_and_formats = QSplitter(Qt.Orientation.Vertical)
self.cover_and_formats = cover_and_formats = BasicSplitter(Qt.Orientation.Vertical)
# Put a very small margin on the left so that the word "Cover" doesn't
# touch the splitter
cover_and_formats.setContentsMargins(1, 0, 0, 0)

View File

@ -19,7 +19,7 @@ from qt.core import (
QWidget, QTableView, QGridLayout, QPalette, QTimer, pyqtSignal,
QAbstractTableModel, QSize, QListView, QPixmap, QModelIndex,
QAbstractListModel, QRect, QTextBrowser, QStringListModel, QMenu, QItemSelectionModel,
QCursor, QHBoxLayout, QPushButton, QSizePolicy, QSplitter, QAbstractItemView)
QCursor, QHBoxLayout, QPushButton, QSizePolicy, QAbstractItemView)
from calibre.customize.ui import metadata_plugins
from calibre.ebooks.metadata import authors_to_string, rating_to_stars
@ -29,6 +29,7 @@ from calibre.ebooks.metadata.book.base import Metadata
from calibre.ebooks.metadata.opf2 import OPF
from calibre.gui2 import error_dialog, rating_font, gprefs
from calibre.gui2.progress_indicator import SpinAnimator
from calibre.gui2.widgets import BasicSplitter
from calibre.gui2.widgets2 import HTMLDisplay
from calibre.utils.date import (utcnow, fromordinal, format_date,
UNDEFINED_DATE, as_utc)
@ -445,7 +446,7 @@ class IdentifyWidget(QWidget): # {{{
self.top.setWordWrap(True)
l.addWidget(self.top)
self.splitter = s = QSplitter(self)
self.splitter = s = BasicSplitter(self)
s.setChildrenCollapsible(False)
l.addWidget(s, 100)
self.results_view = ResultsView(self)

View File

@ -2,10 +2,11 @@
# License: GPLv3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from qt.core import QSplitter, QTableView
from qt.core import QTableView
from calibre.gui2.library import DEFAULT_SORT
from calibre.gui2 import gprefs
from calibre.gui2.widgets import BasicSplitter
class PinTableView(QTableView):
@ -124,10 +125,10 @@ class PinTableView(QTableView):
self.apply_state(state)
class PinContainer(QSplitter):
class PinContainer(BasicSplitter):
def __init__(self, books_view, parent=None):
QSplitter.__init__(self, parent)
BasicSplitter.__init__(self, parent)
self.setChildrenCollapsible(False)
self.books_view = books_view
self.addWidget(books_view)

View File

@ -11,7 +11,7 @@ from calibre import isbytestring, prepare_string_for_xml
from calibre.gui2 import error_dialog, info_dialog
from calibre.gui2.preferences import AbortCommit, ConfigWidgetBase, test_widget
from calibre.gui2.search_box import SearchBox2
from calibre.gui2.widgets import PythonHighlighter
from calibre.gui2.widgets import PythonHighlighter, BasicSplitter
from calibre.utils.config_base import (default_tweaks_raw, exec_tweaks,
normalize_tweak, read_custom_tweaks,
write_custom_tweaks)
@ -22,7 +22,7 @@ from qt.core import (QAbstractItemView, QAbstractListModel, QApplication,
QComboBox, QDialog, QDialogButtonBox, QFont, QGridLayout,
QGroupBox, QIcon, QItemSelectionModel, QLabel, QListView,
QMenu, QModelIndex, QPlainTextEdit, QPushButton,
QSizePolicy, QSplitter, Qt, QVBoxLayout, QWidget,
QSizePolicy, Qt, QVBoxLayout, QWidget,
pyqtSignal)
ROOT = QModelIndex()
@ -371,7 +371,7 @@ class ConfigWidget(ConfigWidgetBase):
_("Values for the tweaks are shown below. Edit them to change the behavior of calibre."
" Your changes will only take effect <b>after a restart</b> of calibre."))
l.addWidget(la), la.setWordWrap(True)
self.splitter = s = QSplitter(self)
self.splitter = s = BasicSplitter(self)
s.setChildrenCollapsible(False)
l.addWidget(s, 10)

View File

@ -434,6 +434,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
# 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)
# Force repaint of the book details splitter because it otherwise ends
# up with the wrong size. I don't know why.
QTimer.singleShot(0, self.bd_splitter.repaint)
def start_quickview(self):
from calibre.gui2.actions.show_quickview import get_quickview_action_plugin

View File

@ -980,13 +980,48 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{
# Splitter {{{
class BasicSplitterHandle(QSplitterHandle):
class SplitterHandle(QSplitterHandle):
def __init__(self, orientation, splitter):
QSplitterHandle.__init__(self, orientation, splitter)
self.handle_width = splitter.handleWidth()
def paintEvent(self, event):
rect = event.rect()
painter = QPainter(self)
# draw the separator bar.
painter.setPen(Qt.NoPen)
palette = QApplication.palette()
painter.setBrush(palette.color(QPalette.ColorGroup.Normal, QPalette.ColorRole.AlternateBase))
painter.drawRect(rect)
# draw the dots
painter.setBrush(palette.color(QPalette.ColorGroup.Normal, QPalette.ColorRole.Shadow))
horizontal = self.orientation() == Qt.Orientation.Horizontal
dot_count = 6
dot_size = int(max(1, self.handle_width/2))
start_point = max(0, int((rect.height()/2 if horizontal else rect.width()/2) - (dot_count*dot_size/2)))
for i in range(dot_count):
# Move the rect to leave 2 dot spaces between the dots
if horizontal:
dot_rect = QRect(1, start_point + i*dot_size*3, dot_size, dot_size)
else:
dot_rect = QRect(start_point + i*dot_size*3, 1, dot_size, dot_size)
painter.drawRect(dot_rect)
painter.end()
class BasicSplitter(QSplitter):
def createHandle(self):
return BasicSplitterHandle(self.orientation(), self)
class SplitterHandle(BasicSplitterHandle):
double_clicked = pyqtSignal(object)
def __init__(self, orientation, splitter):
QSplitterHandle.__init__(self, orientation, splitter)
BasicSplitterHandle.__init__(self, orientation, splitter)
splitter.splitterMoved.connect(self.splitter_moved,
type=Qt.ConnectionType.QueuedConnection)
self.double_clicked.connect(splitter.double_clicked,
@ -1066,7 +1101,7 @@ class LayoutButton(QToolButton):
return QToolButton.mouseReleaseEvent(self, ev)
class Splitter(QSplitter):
class Splitter(BasicSplitter):
state_changed = pyqtSignal(object)
reapply_sizes = pyqtSignal(object)
@ -1075,7 +1110,7 @@ class Splitter(QSplitter):
initial_side_size=120, connect_button=True,
orientation=Qt.Orientation.Horizontal, side_index=0, parent=None,
shortcut=None, hide_handle_on_single_panel=True):
QSplitter.__init__(self, parent)
BasicSplitter.__init__(self, 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:
@ -1189,7 +1224,7 @@ class Splitter(QSplitter):
def do_resize(self, *args):
orig = self.desired_side_size
QSplitter.resizeEvent(self, self._resize_ev)
BasicSplitter.resizeEvent(self, self._resize_ev)
if orig > 20 and self.desired_show:
c = 0
while abs(self.side_index_size - orig) > 10 and c < 5: