diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 818e5e6b36..7332ddf56b 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -425,6 +425,7 @@ def create_defs(): defs['allow_keyboard_search_in_library_views'] = True defs['show_links_in_tag_browser'] = False defs['show_notes_in_tag_browser'] = False + defs['icons_on_right_in_tag_browser'] = True def migrate_tweak(tweak_name, pref_name): # If the tweak has been changed then leave the tweak in the file so diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index c56e94aa45..1995f3f32a 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -648,6 +648,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('show_avg_rating', config) r('show_links_in_tag_browser', gprefs) r('show_notes_in_tag_browser', gprefs) + r('icons_on_right_in_tag_browser', gprefs) r('disable_animations', config) r('systray_icon', config, restart_required=True) r('show_splash_screen', gprefs) diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index cf852832df..808c489180 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -1370,6 +1370,21 @@ box will cause these empty categories to be hidden.</p> + + + + Place icons on the &right, in columns + + + If checked the notes and links icons will be placed at the right, after +the count and in columns. If unchecked, the icons will be placed immediately after the text, +to the left of the count and not in columns. + + + true + + + diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index 4c20adc2fd..3e68d5d0b3 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -782,10 +782,16 @@ class TagBrowserWidget(QFrame): # {{{ l.m.aboutToShow.connect(self.about_to_show_configure_menu) # Show/hide counts l.m.show_counts_action = ac = l.m.addAction('counts') + parent.keyboard.register_shortcut('tag browser toggle counts', + _('Toggle counts'), default_keys=(), + action=ac, group=_('Tag browser')) ac.triggered.connect(self.toggle_counts) # Show/hide average rating l.m.show_avg_rating_action = ac = l.m.addAction(QIcon.ic('rating.png'), 'avg rating') ac.triggered.connect(self.toggle_avg_rating) + parent.keyboard.register_shortcut('tag browser toggle average ratings', + _('Toggle average ratings'), default_keys=(), + action=ac, group=_('Tag browser')) # Show/hide notes icon l.m.show_notes_icon_action = ac = l.m.addAction(QIcon.ic('notes.png'), 'notes icon') ac.triggered.connect(self.toggle_notes_icon) diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py index e8d7b0bf0a..53ca5edbef 100644 --- a/src/calibre/gui2/tag_browser/view.py +++ b/src/calibre/gui2/tag_browser/view.py @@ -87,50 +87,86 @@ class TagDelegate(QStyledItemDelegate): # {{{ def draw_text(self, style, painter, option, widget, index, item): tr = style.subElementRect(QStyle.SubElement.SE_ItemViewItemText, option, widget) text = index.data(Qt.ItemDataRole.DisplayRole) + flags = Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft | Qt.TextFlag.TextSingleLine + lr = QRect(tr) + lr.setRight(lr.right() * 2) + text_rec = painter.boundingRect(lr, flags, text) hover = option.state & QStyle.StateFlag.State_MouseOver is_search = (True if item.type == TagTreeItem.TAG and item.tag.category == 'search' else False) - show_notes = gprefs['show_notes_in_tag_browser'] - show_links = gprefs['show_links_in_tag_browser'] + def render_count(): + if not is_search and (hover or gprefs['tag_browser_show_counts']): + count = str(index.data(COUNT_ROLE)) + width = painter.fontMetrics().boundingRect(count).width() + r = QRect(tr) + r.setRight(r.right() - 1), r.setLeft(r.right() - width - 4) + self.paint_text(painter, r, Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextSingleLine, count, hover, option) + tr.setRight(r.left() - 1) + else: + tr.setRight(tr.right() - 1) + if item.type == TagTreeItem.TAG: category = item.tag.category name = item.tag.original_name tv = self.tags_view m = tv._model - if show_links and m.category_has_links(category): + positions = {'links': (-1, -1), 'notes': (-1, -1)} + + # The icons fits in a rectangle height/2 + 4 x height/2 + 4. This + # ensures they are a 'pleasant' size compared to the text. + icon_width = int(tr.height()/2) + 4 + + def render_link_icon(): icon = self.links_icon if m.item_has_link(category, name) else self.blank_icon - width = int(tr.height()/2) r = QRect(tr) - r.setRight(r.right() - 1), r.setLeft(r.right() - width - 4) - tv.category_button_positions[category]['links'] = (r.left(), r.left()+r.width()) + r.setRight(r.right() - 1) + r.setLeft(r.right() - icon_width) + positions['links'] = (r.left(), r.left()+r.width()) icon.paint(painter, r, option.decorationAlignment, QIcon.Mode.Normal, QIcon.State.On) tr.setRight(r.left() - 1) - if show_notes and m.category_has_notes(category): + def render_note_icon(): icon = self.notes_icon if m.item_has_note(category, name) else self.blank_icon - width = int(tr.height()/2) r = QRect(tr) - r.setRight(r.right() - 1), r.setLeft(r.right() - width - 4) - tv.category_button_positions[category]['notes'] = (r.left(), r.left()+r.width()) + r.setRight(r.right() - 1) + r.setLeft(r.right() - icon_width) + positions['notes'] = (r.left(), r.left()+r.width()) icon.paint(painter, r, option.decorationAlignment, QIcon.Mode.Normal, QIcon.State.On) tr.setRight(r.left() - 1) - if not is_search and (hover or gprefs['tag_browser_show_counts']): - count = str(index.data(COUNT_ROLE)) - width = painter.fontMetrics().boundingRect(count).width() - r = QRect(tr) - r.setRight(r.right() - 1), r.setLeft(r.right() - width - 4) - self.paint_text(painter, r, Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextSingleLine, count, hover, option) - tr.setRight(r.left() - 1) + + if gprefs['icons_on_right_in_tag_browser']: + # Icons go far right, in columns after the counts + show_note_icon = gprefs['show_notes_in_tag_browser'] and m.category_has_notes(category) + show_link_icon = gprefs['show_links_in_tag_browser'] and m.category_has_links(category) + if show_link_icon: + render_link_icon() + if show_note_icon: + render_note_icon() + render_count() + else: + # Icons go after the text to the left of the counts, not in columns + show_note_icon = gprefs['show_notes_in_tag_browser'] and m.item_has_note(category, name) + show_link_icon = gprefs['show_links_in_tag_browser'] and m.item_has_link(category, name) + + render_count() + # The link icon has a margin of 1 px on each side. Account for + # this when computing the width of the icons. If you change the + # order of the icons then you must change this calculation + w = (int(show_link_icon) * (icon_width + 2)) + (int(show_note_icon) * icon_width) + # Leave a 5 px margin between the text and the icon. + tr.setWidth(min(tr.width(), text_rec.width() + 5 + w)) + if show_link_icon: + render_link_icon() + if show_note_icon: + render_note_icon() + tv.category_button_positions[category][name] = positions else: - tr.setRight(tr.right() - 1) + render_count() + is_rating = item.type == TagTreeItem.TAG and not self.rating_pat.sub('', text) if is_rating: painter.setFont(self.rating_font) - flags = Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft | Qt.TextFlag.TextSingleLine - lr = QRect(tr) - lr.setRight(lr.right() * 2) - br = painter.boundingRect(lr, flags, text) - if br.width() > tr.width(): + if text_rec.width() > tr.width(): g = QLinearGradient(QPointF(tr.topLeft()), QPointF(tr.topRight())) c = option.palette.color(QPalette.ColorRole.WindowText) g.setColorAt(0, c), g.setColorAt(0.8, c) @@ -522,8 +558,8 @@ class TagsView(QTreeView): # {{{ joiner = ' and ' if self.match_all else ' or ' return joiner.join(tokens) - def click_in_button_range(self, val, category, kind): - range_tuple = self.category_button_positions[category].get(kind) + def click_in_button_range(self, val, category, item_name, kind): + range_tuple = self.category_button_positions[category].get(item_name, {}).get(kind) return range_tuple and range_tuple[0] <= val <= range_tuple[1] def toggle_current_index(self): @@ -540,13 +576,13 @@ class TagsView(QTreeView): # {{{ category = t.tag.category orig_name = t.tag.original_name x = self.mouse_clicked_point.x() - if self.click_in_button_range(x, category, 'notes'): + if self.click_in_button_range(x, category, orig_name, 'notes'): from calibre.gui2.dialogs.show_category_note import ShowNoteDialog item_id = db.get_item_id(category, orig_name) if db.notes_for(category, item_id): ShowNoteDialog(category, item_id, db, parent=self).show() return - if self.click_in_button_range(x, category, 'links'): + if self.click_in_button_range(x, category, orig_name, 'links'): link = db.get_link_map(category).get(orig_name) if link: safe_open_url(link)