From 501d1b93ffa7d064fe93e8dd033cf996f4ee3ce8 Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Sun, 18 Jan 2026 16:05:26 +0100 Subject: [PATCH] Bookshelf: allow custom background --- src/calibre/gui2/__init__.py | 8 ++++ src/calibre/gui2/library/bookshelf_view.py | 37 +++++++++++++++++-- .../look_feel_tabs/bookshelf_view.py | 3 ++ .../look_feel_tabs/bookshelf_view.ui | 20 +++++++++- 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 64a4f7faef..7eff9507d1 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -507,6 +507,10 @@ def create_defs(): defs['bookshelf_start_with_divider'] = False defs['bookshelf_divider_style'] = 'text' defs['bookshelf_theme_override'] = 'none' + defs['bookshelf_use_custom_background'] = False + defs['bookshelf_custom_background'] = { + 'light': (255, 255, 255), 'dark': (64, 64, 64), 'light_texture': None, 'dark_texture': None + } # Migrate beta bookshelf_thumbnail if isinstance(btv := gprefs.get('bookshelf_thumbnail'), bool): @@ -1796,3 +1800,7 @@ def resolve_custom_background(name: str ,which='color', for_dark: bool | None = def resolve_grid_color(which='color', for_dark: bool | None = None, use_defaults: bool = False): return resolve_custom_background('cover_grid_background', which=which, for_dark=for_dark, use_defaults=use_defaults) + + +def resolve_bookshelf_color(which='color', for_dark: bool | None = None, use_defaults: bool = False): + return resolve_custom_background('bookshelf_custom_background', which=which, for_dark=for_dark, use_defaults=use_defaults) diff --git a/src/calibre/gui2/library/bookshelf_view.py b/src/calibre/gui2/library/bookshelf_view.py index ef4829b3ca..bbc3cd981b 100644 --- a/src/calibre/gui2/library/bookshelf_view.py +++ b/src/calibre/gui2/library/bookshelf_view.py @@ -5,6 +5,7 @@ # Imports {{{ import bisect import math +import os import random import struct import weakref @@ -62,13 +63,16 @@ from qt.core import ( QWidget, pyqtProperty, pyqtSignal, + qBlue, + qGreen, + qRed, ) from xxhash import xxh3_64_intdigest from calibre import fit_image from calibre.db.cache import Cache from calibre.ebooks.metadata import authors_to_string, rating_to_stars -from calibre.gui2 import config, gprefs +from calibre.gui2 import config, gprefs, resolve_bookshelf_color from calibre.gui2.library.alternate_views import ( ClickStartData, double_click_action, @@ -1535,6 +1539,7 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea): self.text_color_for_dark_background = dark_palette().color(QPalette.ColorRole.WindowText) self.text_color_for_light_background = light_palette().color(QPalette.ColorRole.WindowText) self.setPalette(dark_palette() if is_dark_theme() else light_palette()) + self.set_custom_background() def view_is_visible(self) -> bool: '''Return if the bookshelf view is visible.''' @@ -1767,6 +1772,30 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea): draw_horizontal(top, 'top') draw_horizontal(bottom, 'bottom') + def set_custom_background(self): + if not gprefs['bookshelf_use_custom_background']: + self.setStyleSheet('') + return + r, g, b = resolve_bookshelf_color(for_dark=is_dark_theme()) + tex = resolve_bookshelf_color(for_dark=is_dark_theme(), which='texture') + pal = self.palette() + bgcol = QColor(r, g, b) + pal.setColor(QPalette.ColorRole.Base, bgcol) + self.setPalette(pal) + ss = f'background-color: {bgcol.name()}; border: 0px solid {bgcol.name()};' + if tex: + from calibre.gui2.preferences.texture_chooser import texture_path + path = texture_path(tex) + if path: + path = os.path.abspath(path).replace(os.sep, '/') + ss += f'background-image: url({path});' + ss += 'background-attachment: fixed;' + pm = QPixmap(path) + if not pm.isNull(): + val = pm.scaled(1, 1).toImage().pixel(0, 0) + r, g, b = qRed(val), qGreen(val), qBlue(val) + self.setStyleSheet(f'QAbstractScrollArea {{ {ss} }}') + def paintEvent(self, ev: QPaintEvent) -> None: '''Paint the bookshelf view.''' if not self.view_is_visible(): @@ -1797,8 +1826,10 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea): shelves.append((nshelf, shelf is not nshelf)) if not hasattr(self, 'case_renderer'): self.case_renderer = RenderCase() - painter.drawPixmap( - QPoint(0, 0), self.case_renderer.background_as_pixmap(viewport_rect.width(), viewport_rect.height())) + if not gprefs['bookshelf_use_custom_background']: + painter.drawPixmap( + QPoint(0, 0), self.case_renderer.background_as_pixmap(viewport_rect.width(), viewport_rect.height()), + ) n = self.shelves_per_screen for base in shelf_bases: self.draw_shelf_base(painter, base, scroll_y, self.width(), base.idx % n) diff --git a/src/calibre/gui2/preferences/look_feel_tabs/bookshelf_view.py b/src/calibre/gui2/preferences/look_feel_tabs/bookshelf_view.py index 23067816c7..c77c7ff0ee 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/bookshelf_view.py +++ b/src/calibre/gui2/preferences/look_feel_tabs/bookshelf_view.py @@ -118,6 +118,9 @@ class BookshelfTab(QTabWidget, LazyConfigWidgetBase, Ui_Form): (_('Dark'), 'dark'), ]) + r('bookshelf_use_custom_background', gprefs) + self.background_box.link_config('bookshelf_custom_background') + self.config_cache.link( self.gui.bookshelf_view.cover_cache, 'bookshelf_disk_cache_size', 'bookshelf_cache_size_multiple', diff --git a/src/calibre/gui2/preferences/look_feel_tabs/bookshelf_view.ui b/src/calibre/gui2/preferences/look_feel_tabs/bookshelf_view.ui index 6f79452716..686fd04281 100644 --- a/src/calibre/gui2/preferences/look_feel_tabs/bookshelf_view.ui +++ b/src/calibre/gui2/preferences/look_feel_tabs/bookshelf_view.ui @@ -340,6 +340,19 @@ + + + Use a custom background instead of the one generated by calibre + + + Use a custom background + + + + + + + @@ -373,7 +386,7 @@ - + Qt::Vertical @@ -410,6 +423,11 @@ ConfigWidgetBase
calibre/gui2/preferences/look_feel_tabs.h
+ + BackgroundConfig + ConfigWidgetBase +
calibre/gui2/preferences/look_feel_tabs.h
+
CoverCacheConfig ConfigWidgetBase