aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Baumgartner <dev@igsor.net>2023-01-25 11:31:08 +0100
committerMatthias Baumgartner <dev@igsor.net>2023-01-25 11:31:08 +0100
commitf6de8a2f568419fd4ea818f3791242f177a87fba (patch)
tree1e105c50f8238accb747ea4050e574e50a30f108
parentb243b02de05fc247e554723137911f7d05b00b82 (diff)
downloadtagit-f6de8a2f568419fd4ea818f3791242f177a87fba.tar.gz
tagit-f6de8a2f568419fd4ea818f3791242f177a87fba.tar.bz2
tagit-f6de8a2f568419fd4ea818f3791242f177a87fba.zip
search actions early port
-rw-r--r--.gitignore1
-rw-r--r--tagit/actions/__init__.py8
-rw-r--r--tagit/actions/search.kv25
-rw-r--r--tagit/actions/search.py334
-rw-r--r--tagit/assets/icons/scalable/search/apply.svg144
-rw-r--r--tagit/assets/icons/scalable/search/exclude_filter.svg173
-rw-r--r--tagit/assets/icons/scalable/search/exclusive_filter.svg203
-rw-r--r--tagit/assets/icons/scalable/search/sort_key.svg158
-rw-r--r--tagit/assets/icons/scalable/search/sort_order_down.svg170
-rw-r--r--tagit/assets/icons/scalable/search/sort_order_up.svg166
-rw-r--r--tagit/widgets/filter.py5
11 files changed, 1380 insertions, 7 deletions
diff --git a/.gitignore b/.gitignore
index 767d1af..4b3bd39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,5 +36,6 @@ tagit/assets/icons/kivy/browser*
tagit/assets/icons/kivy/filter*
tagit/assets/icons/kivy/misc*
tagit/assets/icons/kivy/planes*
+tagit/assets/icons/kivy/search*
## EOF ##
diff --git a/tagit/actions/__init__.py b/tagit/actions/__init__.py
index c138655..bf99807 100644
--- a/tagit/actions/__init__.py
+++ b/tagit/actions/__init__.py
@@ -97,11 +97,11 @@ class ActionBuilder(BuilderBase):
'ShowBrowsing': planes.ShowBrowsing,
'ShowCodash': planes.ShowCodash,
## search
- #'Search': search.Search,
- #'ShowSelected': search.ShowSelected,
- #'RemoveSelected': search.RemoveSelected,
+ 'Search': search.Search,
+ 'ShowSelected': search.ShowSelected,
+ 'RemoveSelected': search.RemoveSelected,
#'SortKey': search.SortKey,
- #'SortOrder': search.SortOrder,
+ 'SortOrder': search.SortOrder,
## session
#'LoadSession': session.LoadSession,
#'CreateSession': session.CreateSession,
diff --git a/tagit/actions/search.kv b/tagit/actions/search.kv
new file mode 100644
index 0000000..00f6d6d
--- /dev/null
+++ b/tagit/actions/search.kv
@@ -0,0 +1,25 @@
+#:import resource_find kivy.resources.resource_find
+
+<Search>:
+ source: resource_find('atlas://search/search')
+ tooltip: 'Apply the current search filter'
+
+<ShowSelected>:
+ source: resource_find('atlas://search/exclusive_filter')
+ tooltip: 'Show only selected items'
+
+<RemoveSelected>:
+ source: resource_find('atlas://search/exclude_filter')
+ tooltip: 'Exclude selected items'
+
+<SortKey>:
+ source: resource_find('atlas://search/sort_key')
+ tooltip: 'Specify the sort key'
+
+<SortOrder>:
+ source_up: resource_find('atlas://search/sort_order_up')
+ source_down: resource_find('atlas://search/sort_order_down')
+ source: self.source_up
+ tooltip: 'Sort order'
+
+## EOF ##
diff --git a/tagit/actions/search.py b/tagit/actions/search.py
new file mode 100644
index 0000000..323f53e
--- /dev/null
+++ b/tagit/actions/search.py
@@ -0,0 +1,334 @@
+"""
+
+Part of the tagit module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# standard imports
+from functools import partial
+import os
+
+# kivy imports
+from kivy.cache import Cache
+from kivy.lang import Builder
+import kivy.properties as kp
+
+# tagit imports
+from tagit import config, dialogues
+from tagit.external.kivy_garden.contextmenu import ContextMenu
+from tagit.utils import clamp, errors
+from tagit.utils import ns
+from tagit.utils.bsfs import ast
+from tagit.widgets import Binding
+from tagit.widgets.filter import FilterAwareMixin
+from tagit.widgets.session import StorageAwareMixin, ConfigAwareMixin
+#from tagit.ai.features.content import ContentFeature, FeatureBuilder # FIXME: mb/port
+#from tagit.parsing.search import sortkeys # FIXME: mb/port
+
+
+# inner-module imports
+from .action import Action
+
+# exports
+__all__ = []
+
+
+## code ##
+
+# load kv
+Builder.load_file(os.path.join(os.path.dirname(__file__), 'search.kv'))
+
+# classes
+class Search(Action, StorageAwareMixin, ConfigAwareMixin):
+ """Apply the current search filter and update the browser."""
+ text = kp.StringProperty('Search')
+
+ # internal category for the cache
+ _CACHE_CATEGORY = 'tagit.search'
+
+ def ktrigger(self, evt):
+ return Binding.check(evt, self.cfg('bindings', 'search', 'search'))
+
+ def on_root(self, wx, root):
+ Action.on_root(self, wx, root)
+ ConfigAwareMixin.on_root(self, wx, root)
+ StorageAwareMixin.on_root(self, wx, root)
+
+ def on_config_changed(self, session, key, value):
+ """Update cache settings."""
+ if self._CACHE_CATEGORY not in Cache._categories:
+ pass
+ elif key == ('ui', 'standalone', 'search', 'cache_items'):
+ value = None if value <= 0 else value
+ Cache._categories[self._CACHE_CATEGORY]['limit'] = value
+ elif key == ('ui', 'standalone', 'search', 'cache_timeout'):
+ value = None if value <= 0 else value
+ Cache._categories[self._CACHE_CATEGORY]['timeout'] = value
+
+ def on_cfg(self, wx, cfg):
+ """Initialize the cache."""
+ if self._CACHE_CATEGORY not in Cache._categories:
+ n_items = self.cfg('ui', 'standalone', 'search', 'cache_items')
+ n_items = None if n_items <= 0 else n_items
+ timeout = self.cfg('ui', 'standalone', 'search', 'cache_timeout')
+ timeout = None if timeout <= 0 else timeout
+ Cache.register(self._CACHE_CATEGORY, n_items, timeout)
+
+ def on_storage_modified(self, sender):
+ # clear the whole cache
+ Cache.remove(self._CACHE_CATEGORY, None)
+
+ def on_predicate_modified(self, sender, predicate, objects, diff):
+ self.apply()
+ return # FIXME: mb/port
+ tbd = set()
+ # walk through cache
+ for ast, sort in Cache._objects[self._CACHE_CATEGORY]:
+ # check ast
+ if ast is not None:
+ for token in ast:
+ if token.predicate() == predicate:
+ if predicate in ('tag', 'group') and \
+ len(set(diff) & set(token.condition())) == 0:
+ # tag predicate but the tag in question was not changed; skip
+ continue
+ tbd.add((ast, sort))
+ break # no need to search further
+
+ # check sort
+ if sort is not None:
+ if sort.predicate() == predicate:
+ tbd.add((ast, sort))
+
+ for key in tbd:
+ Cache.remove(self._CACHE_CATEGORY, key)
+
+ # re-apply searches
+ self.apply()
+
+ def apply(self):
+ browser = self.root.browser
+ filter = self.root.filter
+ session = self.root.session
+
+ with browser:
+ # get query
+ query, sort = filter.get_query()
+ # log search
+ # FIXME: mb/port/log
+ #session.log.log_search(
+ # 'filter',
+ # session.storage.lib.meta,
+ # (filter.t_head, filter.t_tail),
+ # (filter.f_head + [browser.frame], filter.f_tail),
+ # )
+
+ # apply search or fetch it from the cache
+ items = Cache.get(self._CACHE_CATEGORY, (query, sort), None)
+ if items is None:
+ # FIXME: mb/port: consider sort
+ items = list(session.storage.get(ns.bsfs.File, query))
+ Cache.append(self._CACHE_CATEGORY, (query, sort), items)
+
+ # apply search order because it's cheaper to do it here rather
+ # than in the backend (also see uix.kivy.filter.get_query).
+ items = list(reversed(items[:])) if filter.sortdir else items[:]
+ # update browser
+ browser.set_items(items)
+
+
+class ShowSelected(Action):
+ """Show only selected items."""
+ text = kp.StringProperty('Selected only')
+
+ def ktrigger(self, evt):
+ return Binding.check(evt, self.cfg('bindings', 'search', 'exclusive'))
+
+ def apply(self):
+ with self.root.browser as browser:
+ if len(browser.selection) == 0:
+ # silently ignore if no images selected
+ pass
+ elif len(browser.selection) == 1 and list(browser.selection)[0] in browser.folds:
+ # selection is a group
+ self.root.trigger('OpenGroup', list(browser.selection)[0])
+ else:
+ token = ast.filter.IsIn(browser.unfold(browser.selection))
+ self.root.trigger('AddToken', token)
+
+
+class RemoveSelected(Action):
+ """Exclude selected items."""
+ text = kp.StringProperty('Exclude selection')
+
+ def ktrigger(self, evt):
+ return Binding.check(evt, self.cfg('bindings', 'search', 'remove'))
+
+ def apply(self):
+ with self.root.browser as browser:
+ if len(browser.selection) == 0:
+ # silently ignore if no images selected
+ pass
+ else:
+ new_cursor = browser.neighboring_unselected()
+ token = ast.filter.IsNotIn(browser.unfold(browser.selection))
+ self.root.trigger('AddToken', token)
+ # fix frame
+ browser.cursor = new_cursor
+ browser.selection = {browser.cursor} if browser.cursor is not None else set()
+ self.root.trigger('JumpToCursor')
+
+
+class SortKey(Action):
+ """Select by which property items are ordered."""
+ text = kp.StringProperty('Sort by')
+
+ def apply(self, predicate=None):
+ if predicate is None:
+ x = self.pos[0] + self.width
+ y = self.pos[1] + self.height
+ self.menu.show(x, y)
+ else:
+ self.set_sortkey(predicate)
+
+ def on_root(self, wx, root):
+ super(SortKey, self).on_root(wx, root)
+
+ # Order is essential here:
+ # 1. the menu has to be created
+ # 2. the menu has to be attached to a parent
+ # 3. the menu has to be populated
+ # The visibility has to be triggered after (2) or (3)
+ self.menu = ContextMenu(
+ bounding_box_widget = self.root,
+ cancel_handler_widget = self.root)
+ self.root.add_widget(self.menu)
+ self.menu._on_visible(False)
+
+ # TODO: The whole sortkeys setup is rather brittle
+ # e.g. what happens if new features become available at runtime?
+
+ # default sortkeys
+ return # FIXME: mb/port
+
+ options = sortkeys.scope.library | sortkeys.typedef.anchored # FIXME: mb/port
+ # apply whitelist and blacklist config
+ options -= set(self.cfg('ui', 'standalone', 'search', 'sort_blacklist'))
+ whitelist = set(self.cfg('ui', 'standalone', 'search', 'sort_whitelist'))
+ whitelist = whitelist if len(whitelist) else options
+ options &= whitelist
+ # TODO: If there are several versions of the same feature class, keep only the most frequent
+ # * get feature predicates and their feature class (i.e. name)
+ # * if needed, get their frequencies via Features.Entities(ctrl.stor.num, fid)
+ # For now, all known features are used.
+
+ # populate menu
+ for sortkey in sorted(options):
+ text = sortkey
+ if ContentFeature.is_feature_id(sortkey):
+ text = FeatureBuilder.class_from_guid(sortkey).friendly_guid(sortkey)
+
+ self.menu.add_text_item(
+ text=text,
+ on_release=partial(self.release_wrapper, sortkey)
+ )
+
+ def release_wrapper(self, sortkey, *args):
+ # hide
+ self.menu.hide()
+ # trigger event
+ self.set_sortkey(sortkey)
+
+ def set_sortkey(self, predicate):
+ return # FIXME: mb/port
+ with self.root.filter as filter:
+ try:
+ # TODO: What if a predicate accepts several types (e.g. num and anchored)
+ if predicate in sortkeys.typedef.anchored:
+ cursor = self.root.browser.cursor
+ if cursor is None:
+ raise errors.UserError('an image needs to be selected for similarity sort.')
+ # TODO: We normally want the anchored search to be sorted most similar
+ # to least similar (sortdir=False). We could adjust the sortdir automatically.
+ # Note that VFilterAction_SortOrder would *not* get notified automatically.
+ filter.sortkey = partial(ast.AnchoredSort, predicate, cursor.guid)
+ elif predicate in sortkeys.typedef.numerical:
+ filter.sortkey = partial(ast.NumericalSort, predicate)
+ elif predicate in sortkeys.typedef.alphabetical:
+ filter.sortkey = partial(ast.AlphabeticalSort, predicate)
+ else:
+ raise errors.UserError('invalid sort key selected')
+
+ except Exception as e:
+ dialogues.Error(text=str(e)).open()
+
+ # stick to cursor
+ self.root.trigger('JumpToCursor')
+
+
+class SortOrder(Action, FilterAwareMixin):
+ """Switch between ascending and descending order."""
+ text = kp.StringProperty('Toggle sort order')
+
+ def on_root(self, wx, root):
+ Action.on_root(self, wx, root)
+ FilterAwareMixin.on_root(self, wx, root)
+
+ def on_sortdir(self, wx, sortdir):
+ if self._image is not None:
+ self._image.source = self.source_down if sortdir else self.source_up
+
+ def on_filter(self, wx, filter):
+ # remove old binding
+ if self.filter is not None:
+ self.filter.unbind(sortdir=self.on_sortdir)
+ # add new binding
+ self.filter = filter
+ if self.filter is not None:
+ self.filter.bind(sortdir=self.on_sortdir)
+ self.on_sortdir(self.filter, self.filter.sortdir)
+
+ def __del__(self):
+ # remove old binding
+ if self.filter is not None:
+ self.filter.unbind(sortdir=self.on_sortdir)
+ self.filter = None
+
+ def apply(self):
+ with self.root.filter as filter, \
+ self.root.browser as browser:
+ filter.sortdir = not filter.sortdir
+ # keep the same field of view as before
+ browser.offset = clamp(browser.n_items - (browser.offset + browser.page_size),
+ browser.max_offset)
+
+
+## config ##
+
+config.declare(('ui', 'standalone', 'search', 'sort_blacklist'), config.List(config.String()), [],
+ __name__, 'Blacklisted sortkeys', 'Sort keys that will not be shown in the sort selection. This does not affect whitelisted keys.')
+
+config.declare(('ui', 'standalone', 'search', 'sort_whitelist'), config.List(config.String()), [],
+ __name__, 'Whitelisted sortkeys', 'Sort keys that will always be shown in the sort selection. Overrules blacklisted keys.')
+
+config.declare(('ui', 'standalone', 'search', 'cache_items'), config.Unsigned(), 0,
+ __name__, 'Search cache size', 'Number of searches that are held in cache. Zero means no limit.')
+
+config.declare(('ui', 'standalone', 'search', 'cache_timeout'), config.Unsigned(), 0,
+ __name__, 'Search cache timeout', 'Number of seconds until searches are discarded from the search cache. Zero means no limit.')
+
+# keybindings
+
+config.declare(('bindings', 'search', 'search'),
+ config.Keybind(), Binding.simple(Binding.F5),
+ __name__, Search.text.defaultvalue, Search.__doc__)
+
+config.declare(('bindings', 'search', 'exclusive'),
+ config.Keybind(), Binding.simple(Binding.ENTER, Binding.mCTRL, Binding.mREST),
+ __name__, ShowSelected.text.defaultvalue, ShowSelected.__doc__)
+
+config.declare(('bindings', 'search', 'remove'),
+ config.Keybind(), Binding.simple(Binding.DEL, None, Binding.mALL),
+ __name__, RemoveSelected.text.defaultvalue, RemoveSelected.__doc__)
+
+## EOF ##
diff --git a/tagit/assets/icons/scalable/search/apply.svg b/tagit/assets/icons/scalable/search/apply.svg
new file mode 100644
index 0000000..6549ee6
--- /dev/null
+++ b/tagit/assets/icons/scalable/search/apply.svg
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="apply.svg"
+ inkscape:export-filename="../../kivy/search/search.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="43.605974"
+ inkscape:cy="205.85327"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1031"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="233.588,370"
+ orientation="1,0"
+ id="guide1107"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="144.36496,311.42857"
+ orientation="1,0"
+ id="guide1109"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="-77.142857,144.36496"
+ orientation="0,1"
+ id="guide1111"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="5.000315,233.58779"
+ orientation="0,1"
+ id="guide1113"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <rect
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:17.3116684;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="rect822"
+ width="60.920605"
+ height="170.42346"
+ x="-650.75458"
+ y="-357.06061"
+ transform="matrix(-0.71864414,-0.69537803,0.71864414,-0.69537803,0,0)" />
+ <circle
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:30;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="path824"
+ cx="400.86923"
+ cy="467.22452"
+ r="129.5289" />
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/search/exclude_filter.svg b/tagit/assets/icons/scalable/search/exclude_filter.svg
new file mode 100644
index 0000000..ff6ebcf
--- /dev/null
+++ b/tagit/assets/icons/scalable/search/exclude_filter.svg
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="exclude_filter.svg"
+ inkscape:export-filename="../../kivy/browser/select_single.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8"
+ inkscape:cx="171.2059"
+ inkscape:cy="214.89061"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1151"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="-25.75889,207.87402"
+ orientation="0,1"
+ id="guide837"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="-43.941635,170.07874"
+ orientation="0,1"
+ id="guide854"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="170.07874,284.018"
+ orientation="1,0"
+ id="guide877"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="207.87402,258.09397"
+ orientation="1,0"
+ id="guide879"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#9d9ef3;fill-opacity:1;fill-rule:nonzero;stroke:#9d9df3;stroke-width:13.7840004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect4136-3-5"
+ width="156.29443"
+ height="103.39367"
+ x="174.17337"
+ y="382.65268" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#c8c8c8;stroke-width:13.7840004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect4136-3-5-2"
+ width="156.29443"
+ height="103.39367"
+ x="382.04739"
+ y="382.65268" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#c8c8c8;stroke-width:13.7840004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect4136-3-5-9"
+ width="156.29443"
+ height="103.39367"
+ x="174.17337"
+ y="537.62592" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#c8c8c8;stroke-width:13.7840004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect4136-3-5-1"
+ width="156.29443"
+ height="103.39367"
+ x="382.04739"
+ y="537.62592" />
+ <g
+ id="g842"
+ transform="translate(-212.19278,0.94769901)"
+ style="stroke:#ff2f2f;stroke-width:15;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ inkscape:connector-curvature="0"
+ id="path823"
+ d="m 431.92408,400.81253 65.17857,65.17857"
+ style="fill:none;stroke:#ff2f2f;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path823-3"
+ d="m 431.92408,465.9911 65.17857,-65.17857"
+ style="fill:none;stroke:#ff2f2f;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/search/exclusive_filter.svg b/tagit/assets/icons/scalable/search/exclusive_filter.svg
new file mode 100644
index 0000000..d0539f1
--- /dev/null
+++ b/tagit/assets/icons/scalable/search/exclusive_filter.svg
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="exclusive_filter.svg"
+ inkscape:export-filename="../../kivy/browser/select_single.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8"
+ inkscape:cx="171.2059"
+ inkscape:cy="214.89061"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1151"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="-25.75889,207.87402"
+ orientation="0,1"
+ id="guide837"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="-43.941635,170.07874"
+ orientation="0,1"
+ id="guide854"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="170.07874,284.018"
+ orientation="1,0"
+ id="guide877"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="207.87402,258.09397"
+ orientation="1,0"
+ id="guide879"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#9d9ef3;fill-opacity:1;fill-rule:nonzero;stroke:#9d9df3;stroke-width:13.7840004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect4136-3-5"
+ width="156.29443"
+ height="103.39367"
+ x="174.17337"
+ y="382.65268" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#c8c8c8;stroke-width:13.7840004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect4136-3-5-2"
+ width="156.29443"
+ height="103.39367"
+ x="382.04739"
+ y="382.65268" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#c8c8c8;stroke-width:13.7840004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect4136-3-5-9"
+ width="156.29443"
+ height="103.39367"
+ x="174.17337"
+ y="537.62592" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#c8c8c8;stroke-width:13.7840004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:23.10000038;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect4136-3-5-1"
+ width="156.29443"
+ height="103.39367"
+ x="382.04739"
+ y="537.62592" />
+ <g
+ id="g842"
+ transform="translate(-4.3187544,0.94769901)"
+ style="stroke:#f39d9d;stroke-width:15;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ inkscape:connector-curvature="0"
+ id="path823"
+ d="m 431.92408,400.81253 65.17857,65.17857"
+ style="fill:none;stroke:#f39d9d;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path823-3"
+ d="m 431.92408,465.9911 65.17857,-65.17857"
+ style="fill:none;stroke:#f39d9d;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g842-3"
+ transform="translate(-4.3187697,155.92094)"
+ style="stroke:#f39d9d;stroke-width:15;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ inkscape:connector-curvature="0"
+ id="path823-6"
+ d="m 431.92408,400.81253 65.17857,65.17857"
+ style="fill:none;stroke:#f39d9d;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path823-3-7"
+ d="m 431.92408,465.9911 65.17857,-65.17857"
+ style="fill:none;stroke:#f39d9d;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g842-3-5"
+ transform="translate(-212.19278,155.92094)"
+ style="stroke:#f39d9d;stroke-width:15;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ inkscape:connector-curvature="0"
+ id="path823-6-3"
+ d="m 431.92408,400.81253 65.17857,65.17857"
+ style="fill:none;stroke:#f39d9d;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path823-3-7-5"
+ d="m 431.92408,465.9911 65.17857,-65.17857"
+ style="fill:none;stroke:#f39d9d;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/search/sort_key.svg b/tagit/assets/icons/scalable/search/sort_key.svg
new file mode 100644
index 0000000..eda2b6b
--- /dev/null
+++ b/tagit/assets/icons/scalable/search/sort_key.svg
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="sort_key.svg"
+ inkscape:export-filename="../../kivy/search/sort_key.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="257.45035"
+ inkscape:cy="244.18321"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1031"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="233.588,370"
+ orientation="1,0"
+ id="guide1107"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="144.36496,311.42857"
+ orientation="1,0"
+ id="guide1109"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="-77.142857,144.36496"
+ orientation="0,1"
+ id="guide1111"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="5.000315,233.58779"
+ orientation="0,1"
+ id="guide1113"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <rect
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:2.93663239;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="rect819"
+ width="377.9527"
+ height="53.669292"
+ x="167.28125"
+ y="322.85977" />
+ <rect
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:2.07651258;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="rect819-3"
+ width="188.97635"
+ height="53.669292"
+ x="167.28125"
+ y="647.14325" />
+ <rect
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:2.68023014;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="rect819-3-6"
+ width="314.83466"
+ height="53.669292"
+ x="167.28122"
+ y="435.48969" />
+ <rect
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:2.39655113;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="rect819-3-7"
+ width="251.71654"
+ height="53.669292"
+ x="167.28122"
+ y="541.31647" />
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/search/sort_order_down.svg b/tagit/assets/icons/scalable/search/sort_order_down.svg
new file mode 100644
index 0000000..933a668
--- /dev/null
+++ b/tagit/assets/icons/scalable/search/sort_order_down.svg
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="sort_order_down.svg"
+ inkscape:export-filename="../../kivy/search/sort_order_down.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="273.1116"
+ inkscape:cy="189.42948"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1031"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="233.588,370"
+ orientation="1,0"
+ id="guide1107"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="144.36496,311.42857"
+ orientation="1,0"
+ id="guide1109"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="-77.142857,144.36496"
+ orientation="0,1"
+ id="guide1111"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="5.000315,233.58779"
+ orientation="0,1"
+ id="guide1113"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <g
+ id="g922"
+ transform="rotate(-180,356.2576,511.83615)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path2995"
+ d="m 234.80113,332.39648 v 202.50423 h -57.98394 l 47.89217,78.18799 47.89217,78.18799 47.89031,-78.18799 47.89216,-78.18799 H 310.40006 V 332.39648 Z"
+ style="fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:19.07342148;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <g
+ style="fill:#c8c8c8;fill-opacity:1"
+ id="g1182-6"
+ transform="matrix(-0.60812511,0,0,-0.59731394,691.90628,717.38738)">
+ <path
+ sodipodi:type="star"
+ style="fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:2.83461595;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2995-2"
+ sodipodi:sides="3"
+ sodipodi:cx="619.66785"
+ sodipodi:cy="345.2388"
+ sodipodi:r1="259.16489"
+ sodipodi:r2="129.58244"
+ sodipodi:arg1="0"
+ sodipodi:arg2="1.0471976"
+ inkscape:flatsided="false"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="M 878.83273,345.2388 684.45906,457.46049 490.0854,569.68218 l 0,-224.44339 0,-224.44337 194.37367,112.2217 z"
+ inkscape:transform-center-x="-64.791235"
+ inkscape:transform-center-y="-2.2785204e-06"
+ transform="matrix(0,0.70615451,-0.73585447,0,660.39089,37.167262)" />
+ <rect
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:3.10856915;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6.21713833, 3.10856917;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="rect4750-9"
+ width="386.10318"
+ height="130.35136"
+ x="27.748983"
+ y="-471.52106"
+ transform="rotate(90)" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/tagit/assets/icons/scalable/search/sort_order_up.svg b/tagit/assets/icons/scalable/search/sort_order_up.svg
new file mode 100644
index 0000000..96a70f7
--- /dev/null
+++ b/tagit/assets/icons/scalable/search/sort_order_up.svg
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="sort_order_up.svg"
+ inkscape:export-filename="../../kivy/search/sort_order_up.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="195.58239"
+ inkscape:cy="187.40917"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1031"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="233.588,370"
+ orientation="1,0"
+ id="guide1107"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="144.36496,311.42857"
+ orientation="1,0"
+ id="guide1109"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="-77.142857,144.36496"
+ orientation="0,1"
+ id="guide1111"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="5.000315,233.58779"
+ orientation="0,1"
+ id="guide1113"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <path
+ style="fill:none;fill-opacity:1;stroke:#c8c8c8;stroke-width:19.07342148;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="m 234.80113,332.39648 v 202.50423 h -57.98394 l 47.89217,78.18799 47.89217,78.18799 47.89031,-78.18799 47.89216,-78.18799 H 310.40006 V 332.39648 Z"
+ id="path2995"
+ inkscape:connector-curvature="0" />
+ <g
+ transform="matrix(-0.60812511,0,0,-0.59731394,691.90628,717.38738)"
+ id="g1182-6"
+ style="fill:#c8c8c8;fill-opacity:1">
+ <path
+ transform="matrix(0,0.70615451,-0.73585447,0,660.39089,37.167262)"
+ inkscape:transform-center-y="-2.2785204e-06"
+ inkscape:transform-center-x="-64.791235"
+ d="M 878.83273,345.2388 684.45906,457.46049 490.0854,569.68218 l 0,-224.44339 0,-224.44337 194.37367,112.2217 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.0471976"
+ sodipodi:arg1="0"
+ sodipodi:r2="129.58244"
+ sodipodi:r1="259.16489"
+ sodipodi:cy="345.2388"
+ sodipodi:cx="619.66785"
+ sodipodi:sides="3"
+ id="path2995-2"
+ style="fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:2.83461595;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="star" />
+ <rect
+ transform="rotate(90)"
+ y="-471.52106"
+ x="27.748983"
+ height="130.35136"
+ width="386.10318"
+ id="rect4750-9"
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:3.10856915;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6.21713833, 3.10856916999999999;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
+ </g>
+ </g>
+</svg>
diff --git a/tagit/widgets/filter.py b/tagit/widgets/filter.py
index 332ad34..8a7c1a2 100644
--- a/tagit/widgets/filter.py
+++ b/tagit/widgets/filter.py
@@ -168,9 +168,8 @@ class Filter(BoxLayout, ConfigAwareMixin):
if self.changed:
self.redraw()
# issue search
- # FIXME: mb/port/parsing
- #if self.run_search:
- # self.root.trigger('Search')
+ if self.run_search:
+ self.root.trigger('Search')
def redraw(self):
self.tokens.clear_widgets()