Merge branch 'options-groups-divider' of https://github.com/un-pogaz/calibre

This commit is contained in:
Kovid Goyal 2026-01-18 19:08:02 +05:30
commit 628fecbeeb
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 120 additions and 9 deletions

View File

@ -503,6 +503,9 @@ def create_defs():
defs['bookshelf_height'] = 119
defs['bookshelf_make_space_for_second_line'] = False
defs['bookshelf_emblem_position'] = 'auto'
defs['bookshelf_divider_text_right'] = False
defs['bookshelf_start_with_divider'] = False
defs['bookshelf_divider_style'] = 'text'
# Migrate beta bookshelf_thumbnail
if isinstance(btv := gprefs.get('bookshelf_thumbnail'), bool):

View File

@ -42,6 +42,7 @@ from qt.core import (
QMouseEvent,
QObject,
QPainter,
QPainterPath,
QPaintEvent,
QPalette,
QParallelAnimationGroup,
@ -150,6 +151,10 @@ class WoodTheme(NamedTuple):
inner_shadow_color: QColor
cavity_color: QColor
# Divider colors
divider_color: QColor
divider_line_color: QColor
@classmethod
def light_theme(cls) -> WoodTheme:
# Light oak/pine colors for light mode
@ -182,6 +187,9 @@ class WoodTheme(NamedTuple):
side_panel_dark=QColor(145, 105, 70),
inner_shadow_color=QColor(60, 40, 25, 20),
cavity_color=QColor(90, 60, 40),
divider_color=QColor(250, 250, 250),
divider_line_color=QColor(74, 74, 106),
)
@classmethod
@ -216,6 +224,9 @@ class WoodTheme(NamedTuple):
side_panel_dark=QColor(38, 25, 18),
inner_shadow_color=QColor(0, 0, 0, 30),
cavity_color=QColor(20, 14, 10),
divider_color=QColor(100, 100, 100),
divider_line_color=QColor(180, 180, 182),
)
@ -234,7 +245,9 @@ class RenderCase:
def __init__(self):
self.last_rendered_shelf_at = QRect(0, 0, 0, 0), False
self.last_rendered_background_at = QRect(0, 0, 0, 0), False
self.last_rendered_divider_at = QRect(0, 0, 0, 0), False, 0
self.last_rendered_background = QPixmap()
self.last_rendered_divider = QPixmap()
self.shelf_cache: dict[int, QPixmap] = {}
self.back_panel_grain = tuple(self.generate_grain_lines(count=80, seed=42))
@ -455,6 +468,26 @@ class RenderCase:
end_gradient.setColorAt(1.0, self.theme.end_grain_dark)
painter.fillRect(right_end, end_gradient)
def divider_as_pixmap(self, width: int, height: int, corner_radius: int = 0, offset: int = 0) -> QPixmap:
rect = QRect(0, 0, width, height + offset)
is_dark = is_dark_theme()
q = rect, is_dark, corner_radius
if self.last_rendered_divider_at == q:
return self.last_rendered_divider
self.last_rendered_divider_at = q
self.ensure_theme(is_dark)
ans = QImage(width, height + offset, QImage.Format_ARGB32_Premultiplied)
ans.fill(Qt.GlobalColor.transparent)
with QPainter(ans) as painter:
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
path = QPainterPath()
path.addRoundedRect(
QRectF(0, 0, width, height + offset + corner_radius), corner_radius, corner_radius,
)
painter.fillPath(path, self.theme.divider_color)
self.last_rendered_divider = p = QPixmap.fromImage(ans)
return p
class ImageWithDominantColor(QImage):
@ -1059,6 +1092,7 @@ class BookCase(QObject):
if mdb is None or invalidate.is_set():
return
db = mdb.new_api
start_with_divider = gprefs['bookshelf_start_with_divider']
spine_size_template = db.pref('bookshelf_spine_size_template', get_default_from_defaults=True) or ''
if gprefs['bookshelf_make_space_for_second_line']:
author_template = db.pref('bookshelf_author_template', get_default_from_defaults=True) or ''
@ -1099,6 +1133,8 @@ class BookCase(QObject):
current_case_item = CaseItem(y=y, height=lc.spine_height, idx=len(self.items))
if case_end_divider:
current_case_item.add_group_divider(case_end_divider, lc)
elif start_with_divider:
current_case_item.add_group_divider(group_name, lc)
current_case_item.add_book(book_id, spine_width, group_name, lc)
book_id_to_item_map[book_id] = current_case_item.items[-1]
book_id_visual_order_map[book_id] = len(book_id_visual_order_map)
@ -1839,24 +1875,44 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
second_line = fm.elidedText(second_line, Qt.TextElideMode.ElideRight, width)
return first_line, second_line, font
def divider_color(self) -> QColor:
return QColor('#b4b4b6' if is_dark_theme() else '#4a4a6a')
def draw_inline_divider(self, painter: QPainter, divider: ShelfItem, scroll_y: int):
'''Draw an inline group divider with it group name write vertically and a gradient line.'''
lc = self.layout_constraints
rect = divider.rect(lc).translated(0, -scroll_y)
divider_rect = QRect(-rect.height() // 2, -rect.width() // 2, rect.height(), rect.width())
text_right = gprefs['bookshelf_divider_text_right']
def draw_rounded_divider(corner_radius: int, offset: int):
p = self.case_renderer.divider_as_pixmap(rect.width(), rect.height(), corner_radius, offset)
painter.drawPixmap(rect.adjusted(0, -offset, 0, 0), p)
match gprefs['bookshelf_divider_style']:
case 'block':
painter.fillRect(rect, self.case_renderer.theme.divider_color)
case 'round_top':
radius = rect.width() // 2
offset = radius // 4
draw_rounded_divider(radius, offset)
case 'rounded_corner':
radius = rect.width() // 4
offset = radius // 3
draw_rounded_divider(radius, offset)
# Bottom margin
text_rect = divider_rect.adjusted(self.TEXT_MARGIN, 0, 0, 0)
text_rect = divider_rect.adjusted(
0 if text_right else self.TEXT_MARGIN,
0,
-self.TEXT_MARGIN if text_right else 0,
0,
)
elided_text, _, font = self.get_text_metrics(divider.group_name, '', text_rect.size(), bold=True)
painter.save()
painter.setFont(font)
painter.setPen(self.palette().color(QPalette.ColorRole.WindowText))
painter.translate(rect.left() + rect.width() // 2, rect.top() + rect.height() // 2)
painter.rotate(90 if gprefs['bookshelf_up_to_down'] else -90)
sized_rect = painter.drawText(text_rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, elided_text)
alignment = Qt.AlignmentFlag.AlignRight if text_right else Qt.AlignmentFlag.AlignLeft
sized_rect = painter.drawText(text_rect, alignment | Qt.AlignmentFlag.AlignVCenter, elided_text)
# Calculate line dimensions
line_rect = text_rect.adjusted(sized_rect.width(), 0, 0, 0)
overflow = (line_rect.height() - self.DIVIDER_LINE_WIDTH) // 2
@ -1864,7 +1920,9 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
# Draw vertical gradient line if long enough
if line_rect.width() > 8:
color1 = self.divider_color().toRgb()
if text_right:
line_rect.translate(-sized_rect.width(), 0)
color1 = self.case_renderer.theme.divider_line_color.toRgb()
color2 = color1.toRgb()
color1.setAlphaF(0.0) # Transparent at top/bottom
color2.setAlphaF(0.75) # Visible in middle

View File

@ -79,6 +79,15 @@ class BookshelfTab(QTabWidget, LazyConfigWidgetBase, Ui_Form):
r('bookshelf_height', gprefs)
r('bookshelf_make_space_for_second_line', gprefs)
r('bookshelf_divider_text_right', gprefs)
r('bookshelf_start_with_divider', gprefs)
r('bookshelf_divider_style', gprefs, choices=[
(_('Simple text'), 'text'),
(_('Block'), 'block'),
(_('Rounded corners'), 'rounded_corner'),
(_('Round top'), 'round_top'),
])
r('bookshelf_thumbnail_opacity', gprefs)
r('bookshelf_thumbnail', gprefs, choices=[
(_('Full'), 'full'),

View File

@ -227,7 +227,7 @@
<item row="7" column="0">
<widget class="QLabel" name="label_fade_time">
<property name="text">
<string>Duration of hover &amp;animation</string>
<string>Duration of hover &amp;animation:</string>
</property>
<property name="buddy">
<cstring>opt_bookshelf_fade_time</cstring>
@ -281,7 +281,48 @@
</item>
</layout>
</item>
<item row="9" column="0" colspan="2">
<item row="9" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Groups divider &amp;style:</string>
</property>
<property name="buddy">
<cstring>opt_bookshelf_divider_style</cstring>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QComboBox" name="opt_bookshelf_divider_style">
<property name="toolTip">
<string>Control the style to use for the groups divider</string>
</property>
</widget>
</item>
<item row="10" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QCheckBox" name="opt_bookshelf_divider_text_right">
<property name="toolTip">
<string>Control whether the text of the groups divider aligned on the right or left</string>
</property>
<property name="text">
<string>Groups divider text &amp;aligned to the right</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="opt_bookshelf_start_with_divider">
<property name="toolTip">
<string>When groupins the books and that a group extends on more than one shelf, place a group divider at the beginning of the additional shelves</string>
</property>
<property name="text">
<string>&amp;Start shelves with a group divider</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="11" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="recount_button">
@ -315,7 +356,7 @@
</item>
</layout>
</item>
<item row="10" column="0">
<item row="12" column="0">
<spacer name="vertical_spacer_1">
<property name="orientation">
<enum>Qt::Vertical</enum>