From 957953a212ebae0b180986dff5204392190c0159 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 26 Oct 2022 20:53:45 +0530 Subject: [PATCH] Get rid of BasicSplitter Move custom drawing of splitter handle into the style where it belongs. This means all splitters now use the custom handle without needing to sprinkle a special class everywhere. Note that this only works when using the calibre style not the system one. This is by design, since we do not want to override drawing of "system" components. --- src/calibre/gui2/book_details.py | 7 ++- src/calibre/gui2/dialogs/book_info.py | 6 +-- src/calibre/gui2/dialogs/check_library.py | 5 +-- src/calibre/gui2/init.py | 10 ++--- src/calibre/gui2/metadata/single.py | 15 +++---- src/calibre/gui2/metadata/single_download.py | 5 +-- src/calibre/gui2/pin_columns.py | 7 ++- src/calibre/gui2/preferences/tweaks.py | 6 +-- .../progress_indicator/QProgressIndicator.cpp | 23 ++++++++++ src/calibre/gui2/widgets.py | 44 +++---------------- 10 files changed, 56 insertions(+), 72 deletions(-) diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index fbbb1bd4b6..a8f025736e 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -8,7 +8,7 @@ from collections import namedtuple from functools import partial from qt.core import ( QAction, QApplication, QClipboard, QColor, QDialog, QEasingCurve, QIcon, - QKeySequence, QMenu, QMimeData, QPainter, QPen, QPixmap, + QKeySequence, QMenu, QMimeData, QPainter, QPen, QPixmap, QSplitter, QPropertyAnimation, QRect, QSize, QSizePolicy, Qt, QUrl, QWidget, pyqtProperty, pyqtSignal ) @@ -30,7 +30,6 @@ 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 @@ -914,11 +913,11 @@ class BookInfo(HTMLDisplay): # }}} -class DetailsLayout(BasicSplitter): # {{{ +class DetailsLayout(QSplitter): # {{{ def __init__(self, vertical, parent): orientation = Qt.Orientation.Vertical if vertical else Qt.Orientation.Horizontal - BasicSplitter.__init__(self, orientation, parent) + super().__init__(orientation, parent) self.vertical = vertical self._children = [] self.min_size = QSize(190, 200) if vertical else QSize(120, 120) diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index 6d2d1954b1..696651a308 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -7,7 +7,7 @@ from qt.core import ( QAction, QApplication, QBrush, QCheckBox, QDialog, QGridLayout, QHBoxLayout, QIcon, QKeySequence, QLabel, QListView, QModelIndex, QPalette, QPixmap, QPushButton, QShortcut, QSize, Qt, QTimer, QToolButton, - QVBoxLayout, QWidget, pyqtSignal, QDialogButtonBox + QVBoxLayout, QWidget, pyqtSignal, QDialogButtonBox, QSplitter ) from calibre import fit_image @@ -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, BasicSplitter +from calibre.gui2.widgets import CoverView 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 = BasicSplitter(self) + self.splitter = QSplitter(self) self._l = l = QVBoxLayout(self) self.setLayout(l) l.addWidget(self.splitter) diff --git a/src/calibre/gui2/dialogs/check_library.py b/src/calibre/gui2/dialogs/check_library.py index a5d0607ec9..a407eb5dcc 100644 --- a/src/calibre/gui2/dialogs/check_library.py +++ b/src/calibre/gui2/dialogs/check_library.py @@ -11,13 +11,12 @@ from qt.core import ( QApplication, QCheckBox, QCursor, QDialog, QDialogButtonBox, QGridLayout, QHBoxLayout, QIcon, QLabel, QLineEdit, QProgressBar, QPushButton, QStackedLayout, Qt, QTextEdit, QTreeWidget, QTreeWidgetItem, QVBoxLayout, - QWidget, pyqtSignal + QWidget, pyqtSignal, QSplitter ) 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 @@ -126,7 +125,7 @@ class CheckLibraryDialog(QDialog): self._tl = QHBoxLayout() self.setLayout(self._tl) - self.splitter = BasicSplitter(self) + self.splitter = QSplitter(self) self.left = QWidget(self) self.splitter.addWidget(self.left) self.helpw = QTextEdit(self) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 5e916c1240..c1b8484f80 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en' import functools from qt.core import ( QAction, QApplication, QDialog, QEvent, QIcon, QLabel, QMenu, QPixmap, QUrl, - QSizePolicy, QStackedWidget, QStatusBar, QStyle, QStyleOption, + QSizePolicy, QStackedWidget, QStatusBar, QStyle, QStyleOption, QSplitter, QStylePainter, Qt, QTabBar, QTimer, QToolButton, QVBoxLayout, QWidget ) @@ -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, BasicSplitter +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 @@ -112,10 +112,10 @@ class LibraryViewMixin: # {{{ # }}} -class QuickviewSplitter(BasicSplitter): # {{{ +class QuickviewSplitter(QSplitter): # {{{ def __init__(self, parent=None, orientation=Qt.Orientation.Vertical, qv_widget=None): - BasicSplitter.__init__(self, parent=parent, orientation=orientation) + super().__init__(parent=parent, orientation=orientation) self.splitterMoved.connect(self.splitter_moved) self.setChildrenCollapsible(False) self.qv_widget = qv_widget @@ -124,7 +124,7 @@ class QuickviewSplitter(BasicSplitter): # {{{ gprefs['quickview_dialog_heights'] = self.sizes() def resizeEvent(self, *args): - BasicSplitter.resizeEvent(self, *args) + super().resizeEvent(*args) if self.sizes()[1] != 0: gprefs['quickview_dialog_heights'] = self.sizes() diff --git a/src/calibre/gui2/metadata/single.py b/src/calibre/gui2/metadata/single.py index 0525eff188..2dffa514f0 100644 --- a/src/calibre/gui2/metadata/single.py +++ b/src/calibre/gui2/metadata/single.py @@ -12,8 +12,8 @@ from functools import partial from qt.core import ( QDialog, QDialogButtonBox, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QIcon, QInputDialog, QKeySequence, QMenu, QPushButton, QScrollArea, QShortcut, QSize, - QSizePolicy, QSpacerItem, Qt, QTabWidget, QToolButton, QVBoxLayout, - QWidget, pyqtSignal + QSizePolicy, QSpacerItem, QSplitter, Qt, QTabWidget, QToolButton, QVBoxLayout, + QWidget, pyqtSignal, ) from calibre.constants import ismacos @@ -26,10 +26,9 @@ from calibre.gui2.metadata.basic_widgets import ( AuthorsEdit, AuthorSortEdit, BuddyLabel, CommentsEdit, Cover, DateEdit, FormatsManager, IdentifiersEdit, LanguagesEdit, PubdateEdit, PublisherEdit, RatingEdit, RightClickButton, SeriesEdit, SeriesIndexEdit, TagsEdit, TitleEdit, - TitleSortEdit, show_locked_file_error + 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 @@ -732,13 +731,13 @@ class MetadataSingleDialogBase(QDialog): # }}} -class Splitter(BasicSplitter): +class Splitter(QSplitter): frame_resized = pyqtSignal(object) def resizeEvent(self, ev): self.frame_resized.emit(ev) - return BasicSplitter.resizeEvent(self, ev) + return super().resizeEvent(ev) class MetadataSingleDialog(MetadataSingleDialogBase): # {{{ @@ -1040,7 +1039,7 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ QSizePolicy.Policy.Expanding)) wgl.addWidget(self.formats_manager) - self.splitter = BasicSplitter(Qt.Orientation.Horizontal, tab1) + self.splitter = Splitter(Qt.Orientation.Horizontal, tab1) tab1.l.addWidget(self.splitter) self.splitter.addWidget(self.cover) self.splitter.addWidget(wsp) @@ -1208,7 +1207,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 = BasicSplitter(Qt.Orientation.Vertical) + self.cover_and_formats = cover_and_formats = Splitter(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) diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 29abe1d871..40928ecc0e 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -17,7 +17,7 @@ from qt.core import ( QStyledItemDelegate, QTextDocument, QRectF, QIcon, Qt, QApplication, QDialog, QVBoxLayout, QLabel, QDialogButtonBox, QStyle, QStackedWidget, QWidget, QTableView, QGridLayout, QPalette, QTimer, pyqtSignal, - QAbstractTableModel, QSize, QListView, QPixmap, QModelIndex, + QAbstractTableModel, QSize, QListView, QPixmap, QModelIndex, QSplitter, QAbstractListModel, QRect, QTextBrowser, QStringListModel, QMenu, QItemSelectionModel, QCursor, QHBoxLayout, QPushButton, QSizePolicy, QAbstractItemView) @@ -29,7 +29,6 @@ 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) @@ -446,7 +445,7 @@ class IdentifyWidget(QWidget): # {{{ self.top.setWordWrap(True) l.addWidget(self.top) - self.splitter = s = BasicSplitter(self) + self.splitter = s = QSplitter(self) s.setChildrenCollapsible(False) l.addWidget(s, 100) self.results_view = ResultsView(self) diff --git a/src/calibre/gui2/pin_columns.py b/src/calibre/gui2/pin_columns.py index d2f2a11bc8..f7da595886 100644 --- a/src/calibre/gui2/pin_columns.py +++ b/src/calibre/gui2/pin_columns.py @@ -2,11 +2,10 @@ # License: GPLv3 Copyright: 2018, Kovid Goyal -from qt.core import QTableView +from qt.core import QTableView, QSplitter from calibre.gui2.library import DEFAULT_SORT from calibre.gui2 import gprefs -from calibre.gui2.widgets import BasicSplitter class PinTableView(QTableView): @@ -125,10 +124,10 @@ class PinTableView(QTableView): self.apply_state(state) -class PinContainer(BasicSplitter): +class PinContainer(QSplitter): def __init__(self, books_view, parent=None): - BasicSplitter.__init__(self, parent) + super().__init__(parent) self.setChildrenCollapsible(False) self.books_view = books_view self.addWidget(books_view) diff --git a/src/calibre/gui2/preferences/tweaks.py b/src/calibre/gui2/preferences/tweaks.py index 8770e334b6..f260400c8d 100644 --- a/src/calibre/gui2/preferences/tweaks.py +++ b/src/calibre/gui2/preferences/tweaks.py @@ -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, BasicSplitter +from calibre.gui2.widgets import PythonHighlighter 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, Qt, QVBoxLayout, QWidget, + QSizePolicy, Qt, QVBoxLayout, QWidget, QSplitter, 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 after a restart of calibre.")) l.addWidget(la), la.setWordWrap(True) - self.splitter = s = BasicSplitter(self) + self.splitter = s = QSplitter(self) s.setChildrenCollapsible(False) l.addWidget(s, 10) diff --git a/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp b/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp index c5735f38a5..c9077bbc8b 100644 --- a/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp +++ b/src/calibre/gui2/progress_indicator/QProgressIndicator.cpp @@ -336,6 +336,29 @@ void CalibreStyle::drawPrimitive(PrimitiveElement element, const QStyleOption * void CalibreStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { const QStyleOptionViewItem *vopt = NULL; switch(element) { + case CE_Splitter: { + painter->save(); + // draw the separator bar. + painter->setPen(Qt::PenStyle::NoPen); + painter->setBrush(option->palette.color(QPalette::ColorGroup::Normal, QPalette::ColorRole::AlternateBase)); + painter->drawRect(option->rect); + // draw the dots + painter->setBrush(option->palette.color(QPalette::ColorGroup::Normal, QPalette::ColorRole::Shadow)); + bool horizontal = option->state & QStyle::State_Horizontal ? true : false; + static const int dot_count = 4; + int handle_width = horizontal ? option->rect.width() : option->rect.height(); + int dot_size = std::max(1, handle_width); + int start_point = (horizontal ? option->rect.height()/2 : option->rect.width()/2) - (dot_count*dot_size/2); + QRect dot_rect; + for (int i = 0; i < dot_count; i++) { + // Move the rect to leave spaces between the dots + if (horizontal) dot_rect = QRect(0, start_point + i*dot_size*2, dot_size, dot_size); + else dot_rect = QRect(start_point + i*dot_size*2, 0, dot_size, dot_size); + painter->drawEllipse(dot_rect); + } + painter->restore(); + return; + } break; case CE_ItemViewItem: { if (option->state & QStyle::State_HasFocus && (vopt = qstyleoption_cast(option)) && widget && widget->property("highlight_current_item").toBool()) { if (is_color_dark(option->palette.color(QPalette::Window))) { diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index f434131aa8..2daed5bc0e 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -980,46 +980,12 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{ # Splitter {{{ -class BasicSplitterHandle(QSplitterHandle): - - 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 = 4 - cr = self.contentsRect() - handle_width = cr.width() if horizontal else cr.height() - dot_size = int(max(1, handle_width)) - 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 spaces between the dots - if horizontal: - dot_rect = QRect(0, start_point + i*dot_size*2, dot_size, dot_size) - else: - dot_rect = QRect(start_point + i*dot_size*2, 0, dot_size, dot_size) - painter.drawEllipse(dot_rect) - painter.end() - - -class BasicSplitter(QSplitter): - - def createHandle(self): - return BasicSplitterHandle(self.orientation(), self) - - -class SplitterHandle(BasicSplitterHandle): +class SplitterHandle(QSplitterHandle): double_clicked = pyqtSignal(object) def __init__(self, orientation, splitter): - BasicSplitterHandle.__init__(self, orientation, splitter) + super().__init__(orientation, splitter) splitter.splitterMoved.connect(self.splitter_moved, type=Qt.ConnectionType.QueuedConnection) self.double_clicked.connect(splitter.double_clicked, @@ -1099,7 +1065,7 @@ class LayoutButton(QToolButton): return QToolButton.mouseReleaseEvent(self, ev) -class Splitter(BasicSplitter): +class Splitter(QSplitter): state_changed = pyqtSignal(object) reapply_sizes = pyqtSignal(object) @@ -1108,7 +1074,7 @@ class Splitter(BasicSplitter): initial_side_size=120, connect_button=True, orientation=Qt.Orientation.Horizontal, side_index=0, parent=None, shortcut=None, hide_handle_on_single_panel=True): - BasicSplitter.__init__(self, parent) + 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: @@ -1222,7 +1188,7 @@ class Splitter(BasicSplitter): def do_resize(self, *args): orig = self.desired_side_size - BasicSplitter.resizeEvent(self, self._resize_ev) + 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: