home · contact · privacy
Browser: As prompt summary in by_1st view, only show part actually differing.
authorChristian Heller <c.heller@plomlompom.de>
Wed, 13 Nov 2024 02:12:15 +0000 (03:12 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Wed, 13 Nov 2024 02:12:15 +0000 (03:12 +0100)
browser.py

index 63b20ec2a33d3ce1e11e957313c27df07b778dc5..1e6fc424aa1e574dafe38045ee7f0628430df82f 100755 (executable)
@@ -25,6 +25,8 @@ from stable.gen_params import (GenParams,  GEN_PARAMS_FLOAT,  # noqa: E402
                                GEN_PARAMS_INT, GEN_PARAMS_STR,  # noqa: E402
                                GEN_PARAMS)  # noqa: E402
 
+PromptDiff = tuple[str, str, str]
+PromptsDiff = dict[str, PromptDiff]
 BasicItemsAttrs = dict[str, set[str]]
 AttrVals: TypeAlias = list[str]
 AttrValsByVisibility: TypeAlias = dict[str, AttrVals]
@@ -210,7 +212,7 @@ class SorterAndFilterer(GObject.GObject):
             'deleted_text',
             lambda a, b, c: self.widget.filter_input.add_css_class('temp'))
 
-    def passes_filter(self, value: str | int | float) -> bool:
+    def filter_allows_value(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) |
@@ -745,6 +747,7 @@ class Gallery:
         self.items_attrs: ItemsAttrs = {}
         self.selected_idx = 0
         self.slots: list[GallerySlot] = []
+        self._prompts_diff: PromptsDiff = {}
 
         self._grid = Gtk.Grid()
         self._force_width, self._force_height = 0, 0
@@ -812,9 +815,49 @@ class Gallery:
                 else:
                     self.request_update(build_grid=True)
 
-    def _make_basic_items_attrs(self,
-                                entries: list[GalleryItem]
-                                ) -> BasicItemsAttrs:
+    def _prep_items_attrs(self,
+                          entries: list[GalleryItem]
+                          ) -> tuple[BasicItemsAttrs, PromptsDiff]:
+
+        def diff_prompts(basic_items_attrs: BasicItemsAttrs) -> PromptsDiff:
+            if 'prompt' not in basic_items_attrs:
+                return {}
+            prompts_diff: PromptsDiff = {}
+            prompts = list(basic_items_attrs['prompt'])
+
+            def find_longest_equal(prompts, j, matcher):
+                longest_total, temp_longest = '', ''
+                while j < len(prompts[0]):
+                    if 'end' == matcher:
+                        temp_longest = prompts[0][-j] + temp_longest
+                    else:
+                        temp_longest += prompts[0][j]
+                    if len(temp_longest) > len(longest_total):
+                        found_in_all = True
+                        for prompt in prompts[1:]:
+                            if ('start' == matcher
+                                and not prompt.startswith(temp_longest)) or\
+                                    ('end' == matcher
+                                     and not prompt.endswith(temp_longest)) or\
+                                    ('in' == matcher
+                                     and temp_longest not in prompt):
+                                found_in_all = False
+                                break
+                        if not found_in_all:
+                            break
+                        longest_total = temp_longest
+                    j += 1
+                return longest_total
+
+            prefix = find_longest_equal(prompts, 0, 'start')
+            suffix = find_longest_equal(prompts, 1, matcher='end')
+            cores = [p[len(prefix):] for p in prompts]
+            if suffix:
+                for i, p in enumerate(cores):
+                    cores[i] = p[:-len(suffix)]
+            for i, p in enumerate(prompts):
+                prompts_diff[p] = (cores[i], '', '')
+            return prompts_diff
 
         basic_items_attrs = {}
         for attr_name in (s.name for s in self._sort_order):
@@ -825,7 +868,8 @@ class Gallery:
                 if val is not None:
                     vals.add(val)
             basic_items_attrs[attr_name] = vals
-        return basic_items_attrs
+        prompts_diff = diff_prompts(basic_items_attrs)
+        return basic_items_attrs, prompts_diff
 
     def _load_directory(self) -> None:
         """(Re-)build .dir_entries from ._img_dir_path, ._basic_items_attrs."""
@@ -872,14 +916,15 @@ class Gallery:
                     read_directory(path)
 
         read_directory(self._img_dir_path, make_parent=True)
-        basic_items_attrs = self._make_basic_items_attrs(self.dir_entries)
+        self._basic_items_attrs, self._prompts_diff = self._prep_items_attrs(
+                self.dir_entries)
         ignorable_attrs = []
-        for attr_name, attr_vals in basic_items_attrs.items():
+        for attr_name, attr_vals in self._basic_items_attrs.items():
             if len(attr_vals) < 2:
                 ignorable_attrs += [attr_name]
         for attr_name in ignorable_attrs:
             self._sort_order.remove(attr_name)
-        self._basic_items_attrs = basic_items_attrs
+            del self._basic_items_attrs[attr_name]
         self._cache_db.write()
 
     @property
@@ -923,14 +968,16 @@ class Gallery:
                     sorter = self._sort_order.by_name(attr_name)
                     items_attrs[attr_name] = {'incl': [], 'excl': []}
                     for v in vals:
-                        k = ('incl' if (not sorter or sorter.passes_filter(v))
-                             else 'excl')
+                        passes_filter = sorter is None
+                        if sorter:
+                            passes_filter = sorter.filter_allows_value(v)
+                        k = 'incl' if passes_filter else 'excl'
                         items_attrs[attr_name][k] += [v]
                 return items_attrs
 
             items_attrs_tmp_1 = separate_items_attrs(self._basic_items_attrs)
             filtered = filter_entries(items_attrs_tmp_1)
-            reduced_basic_items_attrs = self._make_basic_items_attrs(filtered)
+            reduced_basic_items_attrs = self._prep_items_attrs(filtered)[0]
             items_attrs_tmp_2 = separate_items_attrs(reduced_basic_items_attrs)
             for attr_name in (s.name for s in self._sort_order):
                 final_values: AttrValsByVisibility = {'incl': [], 'semi': []}
@@ -982,6 +1029,9 @@ class Gallery:
                 if 1 == len(remaining):
                     for i, attr in enumerate(ancestors):
                         parent_attr_name, parent_attr_value = attr
+                        if 'prompt' == parent_attr_name:
+                            prompt_diff = self._prompts_diff[parent_attr_value]
+                            parent_attr_value = f'…{prompt_diff[0]}…'
                         txt = f'<b>{parent_attr_name}</b>: {parent_attr_value}'
                         vlabel = VerticalLabel(txt, self._slots_geometry)
                         self._grid.attach(vlabel, i, i_row_ref[0], 1, 1)
@@ -1026,6 +1076,8 @@ class Gallery:
                 self._col_headers_grid.attach(Gtk.Box(), 0, 0, 1, 1)
                 top_attr_name: str = sort_attrs[-1][0]
                 for i, val in enumerate(sort_attrs[-1][1]):
+                    if 'prompt' == top_attr_name:
+                        val = f'…{self._prompts_diff[val][0]}…'
                     label = Gtk.Label(label=f'<b>{top_attr_name}</b>: {val}',
                                       xalign=0,
                                       ellipsize=Pango.EllipsizeMode.MIDDLE)