Tag browser 'Find' improvements:

- Add exact match searching by using '=' as prefix, as in "=Science Fiction"
- Add option to show only categories containing items, as if a '*' prefix had been used
- Override the collapse option using a prefix of ':', as in :foo
- Make Quickview use exact match searching when items are double-clicked
This commit is contained in:
Charles Haley 2020-05-16 11:49:29 +01:00
parent 966d4303b1
commit 2ff634034e
6 changed files with 69 additions and 22 deletions

View File

@ -150,6 +150,7 @@ def create_defs():
defs['ui_style'] = 'calibre' if iswindows or isosx else 'system' defs['ui_style'] = 'calibre' if iswindows or isosx else 'system'
defs['tag_browser_old_look'] = False defs['tag_browser_old_look'] = False
defs['tag_browser_hide_empty_categories'] = False defs['tag_browser_hide_empty_categories'] = False
defs['tag_browser_always_autocollapse'] = False
defs['book_list_tooltips'] = True defs['book_list_tooltips'] = True
defs['show_layout_buttons'] = False defs['show_layout_buttons'] = False
defs['bd_show_cover'] = True defs['bd_show_cover'] = True

View File

@ -281,7 +281,7 @@ class Quickview(QDialog, Ui_Quickview):
def item_doubleclicked(self, item): def item_doubleclicked(self, item):
tb = self.gui.stack.tb_widget tb = self.gui.stack.tb_widget
tb.set_focus_to_find_box() tb.set_focus_to_find_box()
tb.item_search.lineEdit().setText(self.current_key + ':' + item.text()) tb.item_search.lineEdit().setText(self.current_key + ':=' + item.text())
tb.do_find() tb.do_find()
def show_context_menu(self, point): def show_context_menu(self, point):

View File

@ -412,6 +412,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('row_numbers_in_book_list', gprefs) r('row_numbers_in_book_list', gprefs)
r('tag_browser_old_look', gprefs) r('tag_browser_old_look', gprefs)
r('tag_browser_hide_empty_categories', gprefs) r('tag_browser_hide_empty_categories', gprefs)
r('tag_browser_always_autocollapse', gprefs)
r('tag_browser_show_tooltips', gprefs) r('tag_browser_show_tooltips', gprefs)
r('bd_show_cover', gprefs) r('bd_show_cover', gprefs)
r('bd_overlay_cover_size', gprefs) r('bd_overlay_cover_size', gprefs)

View File

@ -995,6 +995,18 @@ then the tags will be displayed each on their own line.</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="0" colspan="2">
<widget class="QCheckBox" name="opt_tag_browser_always_autocollapse">
<property name="toolTip">
<string>When checked, Find in the Tag browser will show all items
that match the search instead of the first one. If Hide empty categories is
also checked then only categories containing a matched item will be shown.</string>
</property>
<property name="text">
<string>Find &amp;shows all items that match in the Tag browser</string>
</property>
</widget>
</item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QComboBox" name="opt_tags_browser_partition_method"> <widget class="QComboBox" name="opt_tags_browser_partition_method">
<property name="toolTip"> <property name="toolTip">

View File

@ -994,9 +994,19 @@ class TagsModel(QAbstractItemModel): # {{{
self.restriction_error.emit() self.restriction_error.emit()
if self.filter_categories_by: if self.filter_categories_by:
if self.filter_categories_by.startswith('='):
use_exact_match = True
filter_by = self.filter_categories_by[1:]
else:
use_exact_match = False
filter_by = self.filter_categories_by
for category in data.keys(): for category in data.keys():
if use_exact_match:
data[category] = [t for t in data[category] data[category] = [t for t in data[category]
if lower(t.name).find(self.filter_categories_by) >= 0] if lower(t.name) == filter_by]
else:
data[category] = [t for t in data[category]
if lower(t.name).find(filter_by) >= 0]
# Build a dict of the keys that have data # Build a dict of the keys that have data
tb_categories = self.db.field_metadata tb_categories = self.db.field_metadata

View File

@ -429,13 +429,16 @@ class TagBrowserBar(QWidget): # {{{
self.item_search.setSizeAdjustPolicy(self.item_search.AdjustToMinimumContentsLengthWithIcon) self.item_search.setSizeAdjustPolicy(self.item_search.AdjustToMinimumContentsLengthWithIcon)
self.item_search.initialize('tag_browser_search') self.item_search.initialize('tag_browser_search')
self.item_search.completer().setCaseSensitivity(Qt.CaseSensitive) self.item_search.completer().setCaseSensitivity(Qt.CaseSensitive)
self.item_search.setToolTip(_( self.item_search.setToolTip(
'Search for items. This is a "contains" search; items containing the\n' '<p>' +_(
'text anywhere in the name will be found. You can limit the search\n' 'Search for items. If the text begins with equals (=) the search is '
'to particular categories using syntax similar to search. For example,\n' 'exact match, otherwise it is "contains" finding items containing '
'tags:foo will find foo in any tag, but not in authors etc. Entering\n' 'the text anywhere in the item name. Both exact and contains '
'*foo will filter all categories at once, showing only those items\n' 'searches ignore case. You can limit the search to particular '
'containing the text "foo"')) 'categories using syntax similar to search. For example, '
'tags:foo will find foo in any tag, but not in authors etc. Entering '
'*foo will collapse all categories then showing only those categories '
'with items containing the text "foo"') + '</p')
ac = QAction(parent) ac = QAction(parent)
parent.addAction(ac) parent.addAction(ac)
parent.keyboard.register_shortcut('tag browser find box', parent.keyboard.register_shortcut('tag browser find box',
@ -639,8 +642,32 @@ class TagBrowserWidget(QFrame): # {{{
def find(self): def find(self):
model = self.tags_view.model() model = self.tags_view.model()
model.clear_boxed() model.clear_boxed()
txt = self.find_text
# When a key is specified don't use the auto-collapsing search.
# A colon separates the lookup key from the search string.
# A leading colon says not to use autocollapsing search but search all keys
txt = self.find_text
colon = txt.find(':')
if colon >= 0:
key = self._parent.library_view.model().db.\
field_metadata.search_term_to_field_key(txt[:colon])
if key in self._parent.library_view.model().db.field_metadata:
txt = txt[colon+1:]
else:
key = ''
txt = txt[1:] if colon == 0 else txt
else:
key = None
# key is None indicates that no colon was found.
# key == '' means either a leading : was found or the key is invalid
# At this point the txt might have a leading =, in which case do an
# exact match search
if (gprefs.get('tag_browser_always_autocollapse', False) and
key is None and not txt.startswith('*')):
txt = '*' + txt
if txt.startswith('*'): if txt.startswith('*'):
self.tags_view.collapseAll() self.tags_view.collapseAll()
model.set_categories_filter(txt[1:]) model.set_categories_filter(txt[1:])
@ -659,18 +686,14 @@ class TagBrowserWidget(QFrame): # {{{
self.search_button.setFocus(True) self.search_button.setFocus(True)
self.item_search.lineEdit().blockSignals(False) self.item_search.lineEdit().blockSignals(False)
key = None if txt.startswith('='):
colon = txt.find(':') if len(txt) > 2 else 0 equals_match = True
if colon > 0: txt = txt[1:]
key = self._parent.library_view.model().db.\
field_metadata.search_term_to_field_key(txt[:colon])
if key in self._parent.library_view.model().db.field_metadata:
txt = txt[colon+1:]
else: else:
key = None equals_match = False
self.current_find_position = \ self.current_find_position = \
model.find_item_node(key, txt, self.current_find_position) model.find_item_node(key, txt, self.current_find_position,
equals_match=equals_match)
if self.current_find_position: if self.current_find_position:
self.tags_view.show_item_at_path(self.current_find_position, box=True) self.tags_view.show_item_at_path(self.current_find_position, box=True)