mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-03-02 15:10:01 -05:00
Merge branch 'bookshelf-emblem' of https://github.com/un-pogaz/calibre
This commit is contained in:
commit
68c7650dee
@ -642,6 +642,7 @@ class DB:
|
||||
defs['bookshelf_title_template'] = '{title}'
|
||||
defs['bookshelf_author_template'] = ''
|
||||
defs['bookshelf_spine_size_template'] = '{pages}'
|
||||
defs['bookshelf_icon_rules'] = []
|
||||
|
||||
# Migrate the beta bookshelf_grouping_mode
|
||||
if self.prefs.get('bookshelf_grouping_mode', '') == 'none':
|
||||
|
||||
@ -502,6 +502,7 @@ def create_defs():
|
||||
defs['bookshelf_up_to_down'] = False
|
||||
defs['bookshelf_height'] = 119
|
||||
defs['bookshelf_make_space_for_second_line'] = False
|
||||
defs['bookshelf_emblem_position'] = 'auto'
|
||||
|
||||
# Migrate beta bookshelf_thumbnail
|
||||
if isinstance(btv := gprefs.get('bookshelf_thumbnail'), bool):
|
||||
|
||||
@ -1323,6 +1323,8 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
DIVIDER_GRADIENT_LINE_1.setAlphaF(0.0) # Transparent at top/bottom
|
||||
DIVIDER_GRADIENT_LINE_2.setAlphaF(0.75) # Visible in middle
|
||||
TEXT_MARGIN = 6
|
||||
EMBLEM_SIZE = 24
|
||||
EMBLEM_MARGIN = 2
|
||||
|
||||
def __init__(self, gui):
|
||||
super().__init__(gui)
|
||||
@ -1454,6 +1456,20 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
template, mi, TEMPLATE_ERROR, mi, column_name=column_name, template_cache=self.template_cache)
|
||||
return rslt or ''
|
||||
|
||||
def render_emblem(self, book_id: int) -> str:
|
||||
if not (db := self.dbref()):
|
||||
return ''
|
||||
p = db.new_api.backend.prefs
|
||||
if not (rules := p.get('bookshelf_icon_rules', p.defaults.get('bookshelf_icon_rules'))):
|
||||
return ''
|
||||
mi = db.get_proxy_metadata(book_id)
|
||||
for (x,y,t) in rules:
|
||||
rslt = mi.formatter.safe_format(
|
||||
t, mi, TEMPLATE_ERROR, mi, column_name='bookshelf_emblem', template_cache=self.template_cache)
|
||||
if rslt:
|
||||
return rslt
|
||||
return ''
|
||||
|
||||
def refresh_settings(self):
|
||||
'''Refresh the gui and render settings.'''
|
||||
self.template_inited = False
|
||||
@ -1628,6 +1644,7 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
def draw_emblems(self, painter: QPainter, item: ShelfItem, scroll_y: int) -> None:
|
||||
book_id = item.book_id
|
||||
above, below = [], []
|
||||
top, bottom = [], []
|
||||
if m := self.model():
|
||||
from calibre.gui2.ui import get_gui
|
||||
db = m.db
|
||||
@ -1642,30 +1659,57 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
self.on_device_icon = QIcon.ic('ok.png')
|
||||
which = above if below else below
|
||||
which.append(self.on_device_icon)
|
||||
custom = self.render_emblem(book_id)
|
||||
if custom:
|
||||
match gprefs['bookshelf_emblem_position']:
|
||||
case 'above':
|
||||
which = above
|
||||
case 'below':
|
||||
which = below
|
||||
case 'top':
|
||||
which = top
|
||||
case 'bottom':
|
||||
which = bottom
|
||||
case _:
|
||||
which = above if below and not above else below
|
||||
which.append(QIcon.ic(custom))
|
||||
|
||||
def draw_horizontal(emblems: list[QIcon], above: bool = True) -> None:
|
||||
def draw_horizontal(emblems: list[QIcon], position: str) -> None:
|
||||
if not emblems:
|
||||
return
|
||||
gap = 2
|
||||
gap = self.EMBLEM_MARGIN
|
||||
max_width = (item.width - gap) // len(emblems)
|
||||
lc = self.layout_constraints
|
||||
max_height = lc.shelf_gap if above else lc.shelf_height
|
||||
match position:
|
||||
case 'above':
|
||||
max_height = lc.shelf_gap
|
||||
case 'below':
|
||||
max_height = lc.shelf_height
|
||||
case 'top' | 'bottom':
|
||||
max_height = self.EMBLEM_SIZE
|
||||
sz = min(max_width, max_height)
|
||||
width = sz
|
||||
if len(emblems) > 1:
|
||||
width += gap + sz
|
||||
x = max(0, (item.width - width) // 2) + item.start_x + lc.side_margin
|
||||
y = item.case_start_y - scroll_y
|
||||
if above:
|
||||
y += lc.shelf_gap + item.reduce_height_by - sz
|
||||
else:
|
||||
y += lc.spine_height
|
||||
match position:
|
||||
case 'above':
|
||||
y += lc.shelf_gap + item.reduce_height_by - sz
|
||||
case 'below':
|
||||
y += lc.spine_height
|
||||
case 'top':
|
||||
y += lc.shelf_gap + item.reduce_height_by + self.EMBLEM_MARGIN
|
||||
case 'bottom':
|
||||
y += lc.spine_height - sz - self.EMBLEM_MARGIN
|
||||
for ic in emblems:
|
||||
p = ic.pixmap(sz, sz)
|
||||
painter.drawPixmap(QPoint(x, y), p)
|
||||
x += sz + gap
|
||||
draw_horizontal(above)
|
||||
draw_horizontal(below, False)
|
||||
draw_horizontal(above, 'above')
|
||||
draw_horizontal(below, 'below')
|
||||
draw_horizontal(top, 'top')
|
||||
draw_horizontal(bottom, 'bottom')
|
||||
|
||||
def paintEvent(self, ev: QPaintEvent):
|
||||
'''Paint the bookshelf view.'''
|
||||
@ -1888,6 +1932,7 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
'''Draw vertically the title on the spine.'''
|
||||
first_line, second_line = self.first_line_renderer(book_id), self.second_line_renderer(book_id)
|
||||
margin = self.TEXT_MARGIN
|
||||
second_rect = None
|
||||
if second_line:
|
||||
first_rect = QRect(rect.left(), rect.top() + margin, rect.width() // 2, rect.height() - 2*margin)
|
||||
second_rect = first_rect.translated(first_rect.width(), 0)
|
||||
@ -1895,10 +1940,23 @@ class BookshelfView(MomentumScrollMixin, QAbstractScrollArea):
|
||||
first_rect, second_rect = second_rect, first_rect
|
||||
else:
|
||||
first_rect = QRect(rect.left(), rect.top() + margin, rect.width(), rect.height() - 2*margin)
|
||||
|
||||
emblem_size = -margin + (self.EMBLEM_MARGIN * 2)
|
||||
emblem_size += min(rect.width() - self.EMBLEM_MARGIN, self.EMBLEM_SIZE)
|
||||
match gprefs['bookshelf_emblem_position']:
|
||||
case 'top':
|
||||
first_rect.adjust(0, emblem_size, 0, 0)
|
||||
if second_rect:
|
||||
second_rect.adjust(0, emblem_size, 0, 0)
|
||||
case 'bottom':
|
||||
first_rect.adjust(0, 0, 0, -emblem_size)
|
||||
if second_rect:
|
||||
second_rect.adjust(0, 0, 0, -emblem_size)
|
||||
|
||||
nfl, nsl, font = self.get_text_metrics(first_line, second_line, first_rect.transposed().size())
|
||||
if not nfl and not nsl: # two lines dont fit
|
||||
second_line = ''
|
||||
first_rect = QRect(rect.left(), rect.top() + margin, rect.width(), rect.height() - 2*margin)
|
||||
first_rect = QRect(rect.left(), first_rect.top(), rect.width(), first_rect.height())
|
||||
nfl, nsl, font = self.get_text_metrics(first_line, second_line, first_rect.transposed().size())
|
||||
first_line, second_line, = nfl, nsl
|
||||
|
||||
|
||||
@ -410,16 +410,24 @@ pref_name_map = {
|
||||
'You can add emblems (small icons) that are displayed on the side of covers'
|
||||
' in the Cover grid by creating "rules" that tell calibre what image to use.'),
|
||||
},
|
||||
'bookshelf_icon_rules': {
|
||||
'kind': 'bookshelf_emblem',
|
||||
'name': _('Bookshelf emblem'),
|
||||
'label': _('Add the emblem:'),
|
||||
'text': _(
|
||||
'You can add emblem (small icon) that are displayed on the side of spines'
|
||||
' in the bookshelf by creating "rules" that tell calibre what image to use.'),
|
||||
},
|
||||
}
|
||||
kind_icons = {'emblem', 'icon'}
|
||||
kind_emblems = {'emblem'}
|
||||
kind_icons = {'emblem', 'icon', 'bookshelf_emblem'}
|
||||
kind_emblems = {'emblem', 'bookshelf_emblem'}
|
||||
kind_colors = {'color'}
|
||||
|
||||
|
||||
def get_template_dialog(parent, rule, field=None, kind=None):
|
||||
if parent.pref_name == 'column_color_rules':
|
||||
return TemplateDialog(parent, rule, mi=parent.mi, fm=parent.fm, color_field=field)
|
||||
if parent.pref_name == 'cover_grid_icon_rules':
|
||||
if parent.pref_name in {'cover_grid_icon_rules', 'bookshelf_icon_rules'}:
|
||||
return TemplateDialog(parent, rule, mi=parent.mi, fm=parent.fm, doing_emblem=True)
|
||||
return TemplateDialog(parent, rule, mi=parent.mi, fm=parent.fm, icon_field_key=field, icon_rule_kind=kind)
|
||||
|
||||
@ -960,7 +968,7 @@ class RulesModel(QAbstractListModel): # {{{
|
||||
<p>Advanced rule for column <b>%(col)s</b>:
|
||||
<pre>%(rule)s</pre>
|
||||
''')%dict(col=col, rule=prepare_string_for_xml(rule))
|
||||
elif self.rule_kind in {'emblem'}:
|
||||
elif self.rule_kind in kind_emblems:
|
||||
return _('''
|
||||
<p>Advanced rule:
|
||||
<pre>%(rule)s</pre>
|
||||
@ -981,6 +989,9 @@ class RulesModel(QAbstractListModel): # {{{
|
||||
if kind == 'emblem':
|
||||
return _('<p>Add the emblem <b>{0}</b> to the cover if the following conditions are met:</p>'
|
||||
'\n<ul>{1}</ul>').format(rule.color, ''.join(conditions))
|
||||
if kind == 'bookshelf_emblem':
|
||||
return _('<p>Add the emblem <b>{0}</b> to spine if the following conditions are met:</p>'
|
||||
'\n<ul>{1}</ul>').format(rule.color, ''.join(conditions))
|
||||
return _('''\
|
||||
<p>Set the <b>%(kind)s</b> of <b>%(col)s</b> to <b>%(color)s</b> %(sample)s
|
||||
if the following conditions are met:</p>
|
||||
@ -1056,6 +1067,16 @@ class EditRules(QWidget): # {{{
|
||||
c.setVisible(False)
|
||||
c.stateChanged.connect(self.changed)
|
||||
|
||||
self.choices_label = cl = QLabel(self)
|
||||
l.addWidget(cl, l.rowCount(), 0, 1, 1)
|
||||
cl.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
||||
cl.setVisible(False)
|
||||
self.choices = c = QComboBox(self)
|
||||
l.addWidget(c, l.rowCount() - 1, 1, 1, 1)
|
||||
c.setVisible(False)
|
||||
c.currentIndexChanged.connect(self.changed)
|
||||
cl.setBuddy(c)
|
||||
|
||||
self.l1 = l1 = QLabel('')
|
||||
l1.setWordWrap(True)
|
||||
l.addWidget(l1, l.rowCount(), 0, 1, 2)
|
||||
@ -1148,6 +1169,7 @@ class EditRules(QWidget): # {{{
|
||||
text = pref_name_map[pref_name]['text']
|
||||
text += ' ' + _('Click the "Add rule" button below to get started.'
|
||||
'<p>You can <b>change an existing rule</b> by double clicking it.')
|
||||
self.l1.setText('<p>'+ text)
|
||||
if pref_name == 'cover_grid_icon_rules':
|
||||
self.enabled.setVisible(True)
|
||||
self.enabled.setChecked(gprefs['show_emblems'])
|
||||
@ -1158,7 +1180,25 @@ class EditRules(QWidget): # {{{
|
||||
' next to the covers shown in the Cover grid, controlled by the'
|
||||
' metadata of the book.'))
|
||||
self.enabled_toggled()
|
||||
self.l1.setText('<p>'+ text)
|
||||
if pref_name == 'bookshelf_icon_rules':
|
||||
self.choices_label.setVisible(True)
|
||||
self.choices.setVisible(True)
|
||||
self.choices_label.setText(_('&Position of the emblem:'))
|
||||
self.choices.setToolTip(_(
|
||||
'<p>"Automatic" will place the icon where the space is available above or below the spine, with below first.'
|
||||
'<p>"Above" and "Below" will allway place the icon at the selected position of the spine.'
|
||||
'<p>"Top" and "Bottom" will place the icon on the spine, reducing the space allowed to the text.'))
|
||||
choice_map = (
|
||||
(_('Automatic'), 'auto'),
|
||||
(_('Above of the spine'), 'above'),
|
||||
(_('Below of the spine'), 'below'),
|
||||
(_('Top of the spine'), 'top'),
|
||||
(_('Bottom of the spine'), 'bottom'),
|
||||
)
|
||||
for idx, (text, data) in enumerate(choice_map):
|
||||
self.choices.addItem(text, data)
|
||||
if data == gprefs['bookshelf_emblem_position']:
|
||||
self.choices.setCurrentIndex(idx)
|
||||
|
||||
def enabled_toggled(self):
|
||||
enabled = self.enabled.isChecked()
|
||||
@ -1308,6 +1348,9 @@ class EditRules(QWidget): # {{{
|
||||
self.model.commit(prefs)
|
||||
if self.pref_name == 'cover_grid_icon_rules':
|
||||
gprefs['show_emblems'] = self.enabled.isChecked()
|
||||
if self.pref_name == 'bookshelf_icon_rules':
|
||||
idx = max(0, self.choices.currentIndex())
|
||||
gprefs['bookshelf_emblem_position'] = self.choices.itemData(idx)
|
||||
|
||||
def export_rules(self):
|
||||
path = choose_save_file(self, 'export-coloring-rules', _('Choose file to export to'),
|
||||
|
||||
@ -269,6 +269,10 @@ class GridEmblemnRules(LazyEditRulesBase):
|
||||
rule_set_name = 'cover_grid_icon_rules'
|
||||
|
||||
|
||||
class BookshelfEmblemRules(LazyEditRulesBase):
|
||||
rule_set_name = 'bookshelf_icon_rules'
|
||||
|
||||
|
||||
class BackgroundConfig(QGroupBox, LazyConfigWidgetBase):
|
||||
|
||||
changed_signal = pyqtSignal()
|
||||
|
||||
@ -301,6 +301,11 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="BookshelfEmblemRules" name="emblem_rules">
|
||||
<attribute name="title">
|
||||
<string>&Emblems</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="CoverCacheConfig" name="config_cache">
|
||||
<attribute name="title">
|
||||
<string>&Performance</string>
|
||||
@ -313,6 +318,11 @@
|
||||
<extends>QWidget</extends>
|
||||
<header>calibre/gui2/preferences/look_feel_tabs.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>BookshelfEmblemRules</class>
|
||||
<extends>ConfigWidgetBase</extends>
|
||||
<header>calibre/gui2/preferences/look_feel_tabs.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>CoverCacheConfig</class>
|
||||
<extends>ConfigWidgetBase</extends>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user