diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 463cdf3316..e28ed316cf 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -38,7 +38,9 @@ from qt.core import ( QLocale, QNetworkProxyFactory, QObject, + QPainterPath, QPalette, + QRectF, QResource, QSettings, QSocketNotifier, @@ -426,7 +428,7 @@ def create_defs(): defs['cover_grid_disk_cache_size'] = 2500 defs['cover_grid_show_title'] = False defs['cover_grid_texture'] = None - defs['cover_grid_corner_radius'] = 0 + defs['cover_corner_radius'] = 0 defs['show_vl_tabs'] = False defs['vl_tabs_closable'] = True defs['show_highlight_toggle_button'] = False @@ -1756,3 +1758,17 @@ def raise_without_focus(self: QWidget) -> None: QWidget.raise_and_focus = raise_and_focus QWidget.raise_without_focus = raise_without_focus + + +@contextmanager +def clip_border_radius(painter, rect): + painter.save() + r = gprefs['cover_corner_radius'] + if r > 0: + pp = QPainterPath() + pp.addRoundedRect(QRectF(rect), r, r) + painter.setClipPath(pp) + try: + yield + finally: + painter.restore() diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index 0127098b0c..939e5480b1 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -43,7 +43,18 @@ from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks.metadata.book.base import Metadata, field_metadata from calibre.ebooks.metadata.book.render import mi_to_html from calibre.ebooks.metadata.search_internet import all_author_searches, all_book_searches, name_for, url_for_author_search, url_for_book_search -from calibre.gui2 import NO_URL_FORMATTING, choose_save_file, config, default_author_link, gprefs, pixmap_to_data, question_dialog, rating_font, safe_open_url +from calibre.gui2 import ( + NO_URL_FORMATTING, + choose_save_file, + clip_border_radius, + config, + default_author_link, + gprefs, + pixmap_to_data, + question_dialog, + rating_font, + safe_open_url, +) from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.dialogs.confirm_delete import confirm as confirm_delete from calibre.gui2.dnd import dnd_get_files, dnd_get_image, dnd_has_extension, dnd_has_image, image_extensions @@ -825,7 +836,8 @@ class CoverView(QWidget): # {{{ dpr = self.devicePixelRatio() spmap = self.pixmap.scaled(target.size() * dpr, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation) spmap.setDevicePixelRatio(dpr) - p.drawPixmap(target, spmap) + with clip_border_radius(p, target): + p.drawPixmap(target, spmap) if gprefs['bd_overlay_cover_size']: sztgt = target.adjusted(0, 0, 0, -4) f = p.font() diff --git a/src/calibre/gui2/library/alternate_views.py b/src/calibre/gui2/library/alternate_views.py index e56b7fe9c0..6d0f24fdc5 100644 --- a/src/calibre/gui2/library/alternate_views.py +++ b/src/calibre/gui2/library/alternate_views.py @@ -10,7 +10,6 @@ import operator import os import weakref from collections import namedtuple -from contextlib import contextmanager from functools import wraps from io import BytesIO from textwrap import wrap @@ -37,13 +36,11 @@ from qt.core import ( QMimeData, QModelIndex, QPainter, - QPainterPath, QPalette, QPixmap, QPoint, QPropertyAnimation, QRect, - QRectF, QSize, QStyledItemDelegate, QStyleOptionViewItem, @@ -64,7 +61,7 @@ from qt.core import ( from calibre import fit_image, human_readable, prepare_string_for_xml from calibre.constants import DEBUG, config_dir, islinux from calibre.ebooks.metadata import fmt_sidx, rating_to_stars -from calibre.gui2 import config, empty_index, gprefs, rating_font +from calibre.gui2 import clip_border_radius, config, empty_index, gprefs, rating_font from calibre.gui2.dnd import path_from_qurl from calibre.gui2.gestures import GestureManager from calibre.gui2.library.caches import CoverCache, ThumbnailCache @@ -565,7 +562,7 @@ class CoverDelegate(QStyledItemDelegate): return ans def paint(self, painter, option, index): - with self.clip_border_radius(painter, option.rect): + with clip_border_radius(painter, option.rect): QStyledItemDelegate.paint(self, painter, option, empty_index) # draw the hover and selection highlights m = index.model() db = m.db @@ -662,21 +659,8 @@ class CoverDelegate(QStyledItemDelegate): finally: painter.restore() - @contextmanager - def clip_border_radius(self, painter, rect): - painter.save() - r = gprefs['cover_grid_corner_radius'] - if r > 0: - pp = QPainterPath() - pp.addRoundedRect(QRectF(rect), r, r) - painter.setClipPath(pp) - try: - yield - finally: - painter.restore() - def paint_cover(self, painter: QPainter, rect: QRect, pixmap: QPixmap): - with self.clip_border_radius(painter, rect): + with clip_border_radius(painter, rect): painter.drawPixmap(rect, pixmap) def paint_title(self, painter, rect, db, book_id): diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index b9fd91a7da..1ef80a7e49 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -625,8 +625,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('tag_browser_allow_keyboard_focus', gprefs) r('bd_show_cover', gprefs) r('bd_overlay_cover_size', gprefs) + r('cover_corner_radius', gprefs) r('cover_grid_width', gprefs) - r('cover_grid_corner_radius', gprefs) r('cover_grid_height', gprefs) r('cover_grid_cache_size_multiple', gprefs) r('cover_grid_disk_cache_size', gprefs) diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index a2965fe220..ad6f28bbe5 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -28,26 +28,6 @@ &Main interface - - - - Show &row numbers in the book list - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -58,6 +38,78 @@ + + + + Show the &splash screen at startup + + + + + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + 20 + + + + + + + Enable s&ystem tray icon (needs restart) + + + + + + + Show &row numbers in the book list + + + + + + + Choose &language (needs restart): + + + opt_language + + + + + + + Adjust &colors + + + + + + + Disable all animations. Useful if you have a slow/old computer. + + + Disable &animations + + + + + + + Draw a &grid in the book list + + + + + + + + + + @@ -68,47 +120,37 @@ - - - - User interface style (&needs restart): + + + + + 250 + 16777215 + - - opt_ui_style + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + 20 - - + + - E&xtra spacing to add between rows in the book list (can be negative): - - - opt_book_list_extra_row_spacing + Show &layout buttons in the status bar - - + + - Enable s&ystem tray icon (needs restart) + Change &icon theme - - - - Disable all animations. Useful if you have a slow/old computer. - - - Disable &animations - - - - - - - + @@ -129,28 +171,17 @@ - + Change &font (needs restart) - - - - Show &tooltips in the book list - - + + - - - - - - - - + Toolbar @@ -192,40 +223,30 @@ - - - + + + + Qt::Vertical + + - 250 - 16777215 + 20 + 40 - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - 20 - - + - - + + - Choose &language (needs restart): + User interface style (&needs restart): - opt_language + opt_ui_style - - - - Change &icon theme - - - - + px @@ -235,28 +256,14 @@ - + Allow using &drag and drop to merge books - - - - Show &layout buttons in the status bar - - - - - - - Show the &splash screen at startup - - - - + Disable popup notifications when calibre completes jobs such a conversion, sending to device etc. The notifications are sent via the operating system notification facility, if available. Note that on Windows, you have to enable the system tray icon for notifications to work. @@ -266,27 +273,43 @@ - - - - QComboBox::AdjustToMinimumContentsLengthWithIcon - - - 20 + + + + Show &tooltips in the book list - - + + - Draw a &grid in the book list + E&xtra spacing to add between rows in the book list (can be negative): + + + opt_book_list_extra_row_spacing - - + + - Adjust &colors + &Round the corners of covers: + + + opt_cover_corner_radius + + + + + + + Round the corners of covers in many places they are displayed such as the Cover grid, Book details panel, etc. Adjust the amount of rounding with this setting. A value of between 5 and 10 looks good with most covers sizes. Zero disables rounding and is the default. + + + no rounding + + + px @@ -393,6 +416,20 @@ Cover size + + + + Make the covers larger, maintaining current aspect ratio. + + + &Larger covers + + + + :/images/plus.png:/images/plus.png + + + @@ -417,10 +454,20 @@ - - + + + + Cover &width: + + + opt_cover_grid_width + + + + + - The width of displayed covers. + The height of displayed covers. A value of zero means calculate automatically. @@ -434,10 +481,40 @@ A value of zero means calculate automatically. - - + + - The height of displayed covers. + Reset size to automatic + + + &Reset size + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + The width of displayed covers. A value of zero means calculate automatically. @@ -467,83 +544,6 @@ A value of zero means calculate automatically. - - - - Cover &width: - - - opt_cover_grid_width - - - - - - - - - - - - - - Make the covers larger, maintaining current aspect ratio. - - - &Larger covers - - - - :/images/plus.png:/images/plus.png - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Reset size to automatic - - - &Reset size - - - - - - - &Round the corners: - - - opt_cover_grid_corner_radius - - - - - - - The amount by which to round the corners of the covers when they are displayed in the Cover grid. A value of 10 looks good with typical cover sizes. Adjust to your preference. A value of zero disables rounding. - - - no rounding - - - px - - - diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 833c586570..f041e372a0 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -15,6 +15,7 @@ from qt.core import ( QCursor, QEvent, QFont, + QGraphicsPixmapItem, QGraphicsScene, QGraphicsView, QIcon, @@ -43,7 +44,7 @@ from qt.core import ( from calibre import fit_image, force_unicode, strftime from calibre.constants import ismacos, iswindows from calibre.ebooks import BOOK_EXTENSIONS -from calibre.gui2 import error_dialog, gprefs, pixmap_to_data, warning_dialog +from calibre.gui2 import clip_border_radius, error_dialog, gprefs, pixmap_to_data, warning_dialog from calibre.gui2.dnd import DownloadDialog, dnd_get_files, dnd_get_image, dnd_get_local_image_and_pixmap, dnd_has_extension, dnd_has_image, image_extensions from calibre.gui2.filename_pattern_ui import Ui_Form from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator @@ -414,7 +415,8 @@ class ImageView(QWidget, ImageDropMixin): x = int(abs(cw - w)/2) y = int(abs(ch - h)/2) target = QRect(x, y, w, h) - p.drawPixmap(target, pmap) + with clip_border_radius(p, target): + p.drawPixmap(target, pmap) if self.draw_border: pen = QPen() pen.setWidth(self.BORDER_WIDTH) @@ -424,8 +426,18 @@ class ImageView(QWidget, ImageDropMixin): draw_size(p, target, ow, oh) # }}} +# CoverView {{{ -class CoverView(QGraphicsView, ImageDropMixin): # {{{ +class RoundedPixmap(QGraphicsPixmapItem): + + def paint(self, painter, option, widget): + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + target = self.boundingRect().toAlignedRect() + with clip_border_radius(painter, target): + painter.drawPixmap(target, self.pixmap()) + + +class CoverView(QGraphicsView, ImageDropMixin): cover_changed = pyqtSignal(object) @@ -445,7 +457,7 @@ class CoverView(QGraphicsView, ImageDropMixin): # {{{ def set_pixmap(self, pmap): self.scene = QGraphicsScene() - self.scene.addPixmap(pmap) + self.scene.addItem(RoundedPixmap(pmap)) self.setScene(self.scene) def set_background(self, brush=None):