home · contact · privacy
browser.py: Refactor sorters code.
authorChristian Heller <c.heller@plomlompom.de>
Thu, 12 Sep 2024 21:57:31 +0000 (23:57 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Thu, 12 Sep 2024 21:57:31 +0000 (23:57 +0200)
browser.py

index 7849f6ba89b153b60f17068a9d344ad8b3a41302..1f25d2d69883c3eec59126f92d91f38e24a28e63 100755 (executable)
@@ -33,13 +33,30 @@ CSS = """
 """
 
 
-class SortLabelItem(GObject.GObject):
-    """Sort order list representation of sorter label."""
+class Sorter(GObject.GObject):
+    """Sort order box representation of sorting attribute."""
+    list_item: Gtk.Box
 
     def __init__(self, name):
         super().__init__()
         self.name = name
 
+    def set_label(self, gallery_store, gallery_store_filtered):
+        """Set .list_item's label to .name and n of different values for it."""
+        diversities = [0, 0]
+        for i, store in enumerate([gallery_store_filtered, gallery_store]):
+            values = set()
+            for j in range(store.get_n_items()):
+                item = store.get_item(j)
+                if isinstance(item, ImgItem):
+                    val = None
+                    if hasattr(item, self.name):
+                        val = getattr(item, self.name)
+                    values.add(val)
+            diversities[i] = len(values)
+        label = f'{self.name} ({diversities[0]}/{diversities[1]}) '
+        self.list_item.get_first_child().set_text(label)
+
 
 class FileItem(GObject.GObject):
     """Gallery representation of filesystem entry, base to DirItem, ImgItem."""
@@ -109,7 +126,6 @@ class MainWindow(Gtk.Window):
     filter_inputs = dict
     button_activate_sort: Gtk.Button
     counter: Gtk.Label
-    sort_attribute_diversities: dict
 
     def __init__(self, app, **kwargs):
         super().__init__(**kwargs)
@@ -166,52 +182,39 @@ class MainWindow(Gtk.Window):
             return metadata_box
 
         def init_sorter_and_filterer():
-            self.sort_order = [p.lower() for p in GEN_PARAMS]
-            self.sort_order += ['bookmarked']
-            new_sort_order = []
-            do_reverse = '-' in self.app.suggested_sort_order
-            for pattern in self.app.suggested_sort_order:
-                for name in [n for n in self.sort_order
-                             if n.startswith(pattern)]:
-                    self.sort_order.remove(name)
-                    new_sort_order += [name]
-            self.sort_order = new_sort_order + self.sort_order
-            if do_reverse:
-                self.sort_order.reverse()
-            self.sort_store = Gio.ListStore(item_type=SortLabelItem)
+            self.sort_store = Gio.ListStore(item_type=Sorter)
             self.sort_selection = Gtk.SingleSelection.new(self.sort_store)
             self.sort_selection.connect(
                     'selection-changed',
-                    lambda a, b, c: self.sort_selection.props.selected_item.box
-                    .props.child.get_parent().grab_focus())
+                    lambda a, b, c: self.sort_selection.props.selected_item.
+                    list_item.get_parent().grab_focus())
             factory = Gtk.SignalListItemFactory()
 
-            def setup_sort_order_item(_, item):
+            def setup_sort_order_item(_, list_item):
                 box = Gtk.Box(orientation=OR_H)
                 box.append(Gtk.Label(hexpand=True))
                 box.append(Gtk.Entry.new())
                 box.get_last_child().props.placeholder_text = 'filter?'
-                item.set_child(box)
+                list_item.set_child(box)
 
-            def bind_sort_order_item(_, item):
+            def bind_sort_order_item(_, list_item):
 
                 def on_filter_enter(entry):
                     entry.remove_css_class('temp')
                     text = entry.get_buffer().get_text()
                     if '' != text.rstrip():
-                        self.filter_inputs[item.props.item.name] = text
-                    elif item.props.item.name in self.filter_inputs:
-                        del self.filter_inputs[item.props.item.name]
+                        self.filter_inputs[sorter.name] = text
+                    elif sorter.name in self.filter_inputs:
+                        del self.filter_inputs[sorter.name]
                     self.update_gallery()
 
-                item.props.item.box = item
-                sorter_name = item.props.item.name
-                diversity = self.sort_attribute_diversities.get(sorter_name, 0)
-                label = f'{sorter_name} ({diversity}) '
-                item.props.item.filterer = item.props.child.get_last_child()
-                item.props.child.get_first_child().set_text(label)
-                filter_entry = item.props.child.get_last_child()
-                filter_text = self.filter_inputs.get(item.props.item.name, '')
+                sorter = list_item.props.item
+                sorter.list_item = list_item.props.child
+                sorter.set_label(self.gallery_store,
+                                 self.gallery_store_filtered)
+                sorter.filterer = sorter.list_item.get_last_child()
+                filter_entry = sorter.list_item.get_last_child()
+                filter_text = self.filter_inputs.get(sorter.name, '')
                 filter_buffer = filter_entry.get_buffer()
                 filter_buffer.set_text(filter_text, -1)
                 filter_entry.connect('activate', on_filter_enter)
@@ -234,7 +237,6 @@ class MainWindow(Gtk.Window):
                     'clicked', lambda _: self.activate_sort_order())
             sort_box.append(self.button_activate_sort)
             self.filter_inputs = {}
-            self.sort_attribute_diversities = {}
             return sort_box
 
         def init_gallery_content():
@@ -325,7 +327,7 @@ class MainWindow(Gtk.Window):
             json_dump(list(bookmarks), f)
         self.update_file_selection()
         self.update_gallery_view()
-        self.update_sort_order_box(self.sort_order)
+        self.update_sort_order_box()
 
     def update_gallery(self, suggested_selection=None):
         """Build gallery based on .per_row and .gallery_selection."""
@@ -344,7 +346,7 @@ class MainWindow(Gtk.Window):
                     return +1
             # apply self.sort_order within DirItems and FileItems (separately)
             ret = 0
-            for key in self.sort_order:
+            for key in [sorter.name for sorter in self.app.sort_order]:
                 a_cmp = None
                 b_cmp = None
                 if hasattr(a, key):
@@ -395,7 +397,7 @@ class MainWindow(Gtk.Window):
             to_select.activate()
         else:
             self.counter.set_text(' (nothing) ')
-        self.update_sort_order_box(self.sort_order)
+        self.update_sort_order_box()
         self.update_gallery_view()
 
     def update_gallery_view(self, refocus=False):
@@ -493,33 +495,21 @@ class MainWindow(Gtk.Window):
         total = self.gallery_selection.get_n_items()
         self.counter.set_text(f' {idx} of {total} ')
 
-    def update_sort_order_box(self, sort_order_source, cur_selection=0):
-        """Rebuild .sort_store from .sort_order, update diversity counts."""
-        values = {}
-        for k in sort_order_source:
-            values[k] = set()
-        for i in range(self.gallery_store_filtered.get_n_items()):
-            item = self.gallery.get_child_at_index(i).props.child.item
-            if isinstance(item, ImgItem):
-                for attr_name in values:
-                    val = None
-                    if hasattr(item, attr_name):
-                        val = getattr(item, attr_name)
-                    values[attr_name].add(val)
-        for attr_name in values:
-            self.sort_attribute_diversities[attr_name] = len(values[attr_name])
+    def update_sort_order_box(self, alt_order=None, cur_selection=0):
+        """Rebuild .sort_store from .sort_order or alt_order."""
+        sort_order = alt_order if alt_order else self.app.sort_order
         self.sort_store.remove_all()
-        for s in sort_order_source:
-            self.sort_store.append(SortLabelItem(s))
+        for sorter in sort_order:
+            self.sort_store.append(sorter)
         self.sort_selection.props.selected = cur_selection
 
     def activate_sort_order(self):
-        """Write sort order box order into self.sort_order, mark finalized."""
-        self.sort_order = []
+        """Write sort order box order into .app.sort_order, mark finalized."""
+        self.app.sort_order = []
         for i in range(self.sort_store.get_n_items()):
-            sort_item = self.sort_store.get_item(i)
-            sort_item.box.props.child.remove_css_class('temp')
-            self.sort_order += [sort_item.name]
+            sorter = self.sort_store.get_item(i)
+            sorter.list_item.remove_css_class('temp')
+            self.app.sort_order += [sorter]
         self.button_activate_sort.props.sensitive = False
         old_selection = self.gallery_selection.props.selected_item
         self.update_gallery(old_selection)
@@ -680,7 +670,7 @@ class MainWindow(Gtk.Window):
         """Move selected item in sort order view, ensure temporary state."""
         tmp_sort_order = []
         for i in range(self.sort_store.get_n_items()):
-            tmp_sort_order += [self.sort_store.get_item(i).name]
+            tmp_sort_order += [self.sort_store.get_item(i)]
         cur_idx = self.sort_selection.props.selected
         selected = tmp_sort_order[cur_idx]
         if direction == -1 and cur_idx > 0:
@@ -699,12 +689,12 @@ class MainWindow(Gtk.Window):
         self.sort_selection.props.selected = cur_idx + direction
         for i in range(self.sort_store.get_n_items()):
             sort_item = self.sort_store.get_item(i)
-            sort_item.box.props.child.add_css_class('temp')
+            sort_item.list_item.add_css_class('temp')
         self.button_activate_sort.props.sensitive = True
 
     def move_selection_in_sort_order(self, direction):
         """Move sort order selection by direction (-1 or +1)."""
-        min_idx, max_idx = 0, len(self.sort_order) - 1
+        min_idx, max_idx = 0, len(self.app.sort_order) - 1
         cur_idx = self.sort_selection.props.selected
         if (1 == direction and cur_idx < max_idx)\
                 or (-1 == direction and cur_idx > min_idx):
@@ -765,11 +755,29 @@ class MainWindow(Gtk.Window):
 class Application(Gtk.Application):
     """Image browser application class."""
     img_dir_absolute: str
-    suggested_sort_order: list[str]
+    sort_order: list[Sorter]
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
+    def _build_sort_order(self, suggestion_fused):
+        suggestion = suggestion_fused.split(',')
+        names = [p.lower() for p in GEN_PARAMS] + ['bookmarked']
+        sort_order = []
+        for name in names:
+            sort_order += [Sorter(name)]
+        new_sort_order = []
+        do_reverse = '-' in suggestion
+        for pattern in suggestion:
+            for sorter in [sorter for sorter in sort_order
+                           if sorter.name.startswith(pattern)]:
+                sort_order.remove(sorter)
+                new_sort_order += [sorter]
+        sort_order = new_sort_order + sort_order
+        if do_reverse:
+            sort_order.reverse()
+        return sort_order
+
     def do_activate(self, *args, **kwargs):
         """Parse arguments, start window, and keep it open."""
         parser = ArgumentParser()
@@ -777,7 +785,7 @@ class Application(Gtk.Application):
         parser.add_argument('-s', '--sort-order', default=SORT_DEFAULT)
         opts = parser.parse_args()
         self.img_dir_absolute = abspath(opts.directory)
-        self.suggested_sort_order = opts.sort_order.split(',')
+        self.sort_order = self._build_sort_order(opts.sort_order)
         win = MainWindow(self)
         win.present()
         self.hold()