def __init__(self, name: str) -> None:
super().__init__()
self.name = name
- self.filter = ''
+ self.filter_text = ''
def setup_on_bind(self,
widget: Gtk.Box,
def filter_activate() -> None:
self.widget.filter_input.remove_css_class('temp')
- self.filter = self.widget.filter_input.get_buffer().get_text()
+ self.filter_text = self.widget.filter_input.get_buffer().get_text()
on_filter_activate()
filter_buffer = self.widget.filter_input.get_buffer()
- filter_buffer.set_text(self.filter, -1) # triggers 'temp' class set,
- self.widget.filter_input.remove_css_class('temp') # that's why …
- self.widget.filter_input.connect(
- 'activate',
- lambda _: filter_activate())
+ filter_buffer.set_text(self.filter_text, -1) # triggers 'temp' class
+ self.widget.filter_input.remove_css_class('temp') # set, that's why …
+ self.widget.filter_input.connect('activate',
+ lambda _: filter_activate())
filter_buffer.connect(
- 'inserted_text',
- lambda a, b, c, d: self.widget.filter_input.add_css_class('temp'))
+ 'inserted_text',
+ lambda a, b, c, d: self.widget.filter_input.add_css_class('temp'))
filter_buffer.connect(
- 'deleted_text',
- lambda a, b, c: self.widget.filter_input.add_css_class('temp'))
+ 'deleted_text',
+ lambda a, b, c: self.widget.filter_input.add_css_class('temp'))
+
+ def passes_filter(self, value: str | int | float) -> bool:
+ """Return if value passes filter defined by .name and .filter_text."""
+ number_attributes = (set(s.lower() for s in GEN_PARAMS_INT) |
+ set(s.lower() for s in GEN_PARAMS_FLOAT) |
+ {'bookmarked'})
+ if value is None:
+ return False
+ if self.name not in number_attributes:
+ assert isinstance(value, str)
+ return bool(re_search(self.filter_text, value))
+ assert isinstance(value, (int, float))
+ use_float = self.name in {s.lower() for s in GEN_PARAMS_FLOAT}
+ numbers_or, unequal = (set(),) * 2
+ less_than, less_or_equal, more_or_equal, more_than = (None,) * 4
+ for constraint_string in self.filter_text.split(','):
+ toks = constraint_string.split()
+ if len(toks) == 1:
+ tok = toks[0]
+ if tok[0] in '<>!': # operator sans space after: split, re-try
+ if '=' == tok[1]:
+ toks = [tok[:2], tok[2:]]
+ else:
+ toks = [tok[:1], tok[1:]]
+ else:
+ pattern_number = float(tok) if use_float else int(tok)
+ numbers_or.add(pattern_number)
+ if len(toks) == 2: # assume operator followed by number
+ pattern_number = float(toks[1]) if use_float else int(toks[1])
+ if toks[0] == '!=':
+ unequal.add(pattern_number)
+ elif toks[0] == '<':
+ if less_than is None or less_than >= pattern_number:
+ less_than = pattern_number
+ elif toks[0] == '<=':
+ if less_or_equal is None or less_or_equal > pattern_number:
+ less_or_equal = pattern_number
+ elif toks[0] == '>=':
+ if more_or_equal is None or more_or_equal < pattern_number:
+ more_or_equal = pattern_number
+ elif toks[0] == '>':
+ if more_than is None or more_than <= pattern_number:
+ more_than = pattern_number
+ if value in numbers_or:
+ return True
+ if len(numbers_or) > 0 and (less_than == less_or_equal ==
+ more_or_equal == more_than):
+ return False
+ if value in unequal:
+ return False
+ return ((less_than is None or value < less_than)
+ and (less_or_equal is None or value <= less_or_equal)
+ and (more_or_equal is None or value >= more_or_equal)
+ and (more_than is None or value > more_than))
class SorterAndFiltererOrder:
"""Create new, mirroring order in store."""
return cls(cls._list_from_store(store))
+ def by_name(self, name: str) -> Optional[SorterAndFilterer]:
+ """Return included SorterAndFilterer of name."""
+ for s in [s for s in self._list if name == s.name]:
+ return s
+ return None
+
def copy(self) -> Self:
"""Create new, of equal order."""
return self.__class__(self._list[:])
def remove(self, sorter_name: str) -> None:
"""Remove sorter of sorter_name from self."""
- for sorter in [s for s in self._list if sorter_name == s.name]:
- self._list.remove(sorter)
+ candidate = self.by_name(sorter_name)
+ assert candidate is not None
+ self._list.remove(candidate)
def update_from_store(self, store: Gio.ListStore) -> None:
"""Update self from store."""
"""(Re-)build slot grid from .dir_entries, filters, layout settings."""
old_selected_item: Optional[GalleryItem] = self.selected_item
- def passes_filter(attr_name: str, val: str) -> bool:
- number_attributes = (set(s.lower() for s in GEN_PARAMS_INT) |
- set(s.lower() for s in GEN_PARAMS_FLOAT) |
- {'bookmarked'})
-
- def passes_number_filter(attr_name, pattern, val):
- use_float = attr_name in {s.lower() for s in GEN_PARAMS_FLOAT}
- constraint_strings = pattern.split(',')
- numbers_or = set()
- unequal = set()
- less_than = None
- less_or_equal = None
- more_or_equal = None
- more_than = None
- for constraint_string in constraint_strings:
- toks = constraint_string.split()
- if len(toks) == 1:
- tok = toks[0]
- if tok[0] in '<>!':
- if '=' == tok[1]:
- toks = [tok[:2], tok[2:]]
- else:
- toks = [tok[:1], tok[1:]]
- else:
- value = float(tok) if use_float else int(tok)
- numbers_or.add(value)
- if len(toks) == 2:
- value = float(toks[1]) if use_float else int(toks[1])
- if toks[0] == '!=':
- unequal.add(value)
- elif toks[0] == '<':
- if less_than is None or less_than >= value:
- less_than = value
- elif toks[0] == '<=':
- if less_or_equal is None or less_or_equal > value:
- less_or_equal = value
- elif toks[0] == '>=':
- if more_or_equal is None or more_or_equal < value:
- more_or_equal = value
- elif toks[0] == '>':
- if more_than is None or more_than <= value:
- more_than = value
- if val in numbers_or:
- return True
- if len(numbers_or) > 0 and (less_than == less_or_equal ==
- more_or_equal == more_than):
- return False
- if val in unequal:
- return False
- if (less_than is not None
- and val >= less_than)\
- or (less_or_equal is not None
- and val > less_or_equal)\
- or (more_or_equal is not None
- and val < more_or_equal)\
- or (more_than is not None
- and val <= more_than):
- return False
- return True
-
- if val is None:
- return False
- for filterer in [f for f in self._sort_order
- if f.name == attr_name]:
- if attr_name in number_attributes:
- if not passes_number_filter(attr_name, filterer.filter,
- val):
- return False
- elif not re_search(filterer.filter, val):
- return False
- return True
-
def update_items_attrs() -> None:
self.items_attrs.clear()
def separate_items_attrs(basic_items_attrs) -> ItemsAttrs:
items_attrs: ItemsAttrs = {}
for attr_name, vals in basic_items_attrs.items():
+ sorter = self._sort_order.by_name(attr_name)
items_attrs[attr_name] = {'incl': [], 'excl': []}
for v in vals:
- k = 'incl' if passes_filter(attr_name, v) else 'excl'
+ k = ('incl' if (not sorter or sorter.passes_filter(v))
+ else 'excl')
items_attrs[attr_name][k] += [v]
return items_attrs