home · contact · privacy
Move all table config setters into dedicated sidebar box.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 30 Sep 2024 11:16:31 +0000 (13:16 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 30 Sep 2024 11:16:31 +0000 (13:16 +0200)
browser.py

index 63cff44ec632712e1005cbbcb39f48d79db6168f..410fe3d1a4da7c9ac95fc8ccb5e6b6668af85382 100755 (executable)
@@ -83,11 +83,12 @@ class Sorter(GObject.GObject):
                 lambda a, b, c: filter_entry.add_css_class('temp'))
 
 
-class Sorting():
+class TableConfig():
     """Representation of sort and filtering settings."""
     _gallery_update = None
     _gallery_items_attrs_full = None
     _gallery_items_attrs_filtered = None
+    _gallery_set_by_1st_sorter = None
 
     def __init__(self, sort_order):
 
@@ -125,28 +126,52 @@ class Sorting():
         def select_sort_order(_a, _b, _c):
             if self._last_selected:
                 self._last_selected.get_last_child().hide()
-            list_item = self.selection.props.selected_item.widget
+            list_item = self._selection.props.selected_item.widget
             list_item.get_parent().grab_focus()
             self._last_selected = list_item
 
+        def on_by_1st_sorter_toggle(btn):
+            self._btn_per_row.set_sensitive(not btn.props.active)
+            self._btn_show_dirs.set_sensitive(not btn.props.active)
+            if btn.props.active:
+                self._btn_show_dirs.set_active(False)
+            self._gallery_set_by_1st_sorter(btn.props.active)
+
         self.order = sort_order
         self.filter_inputs = {}
         self._last_selected = None
-        self.store = Gio.ListStore(item_type=Sorter)
-        self.selection = Gtk.SingleSelection.new(self.store)
-        self.selection.connect('selection-changed', select_sort_order)
+        self._store = Gio.ListStore(item_type=Sorter)
+        self._selection = Gtk.SingleSelection.new(self._store)
+        self._selection.connect('selection-changed', select_sort_order)
         factory = Gtk.SignalListItemFactory()
         factory.connect('setup', setup_sort_order_item)
         factory.connect('bind', bind_sort_order_item)
-        self.view = Gtk.ListView(model=self.selection, factory=factory)
+        self.view = Gtk.ListView(model=self._selection, factory=factory)
+
+        self._btn_show_dirs = Gtk.CheckButton(label='show')
+        self._btn_recurse_dirs = Gtk.CheckButton(label='recurse')
+        self._btn_reload_dir = Gtk.Button(label='reload')
+        self._btn_by_1st_sorter = Gtk.CheckButton(label='by 1st sorter')
+        self._btn_by_1st_sorter.connect('toggled', on_by_1st_sorter_toggle)
+        self._btn_per_row = Gtk.SpinButton.new_with_range(1, 9, 1)
+
+        dirs_box = Gtk.Box(orientation=OR_H)
+        dirs_box.append(Gtk.Label(label='directories:'))
+        dirs_box.append(self._btn_show_dirs)
+        dirs_box.append(self._btn_recurse_dirs)
+        dirs_box.append(self._btn_reload_dir)
+
+        per_row_box = Gtk.Box(orientation=OR_H)
+        per_row_box.append(Gtk.Label(label='cols/row:'))
+        per_row_box.append(self._btn_by_1st_sorter)
+        per_row_box.append(self._btn_per_row)
+
         self.box = Gtk.Box(orientation=OR_V)
-        self.box.append(Gtk.Label(label='<b>sort order</b>', use_markup=True))
+        title = Gtk.Label(label='<b>table config</b>', use_markup=True)
+        self.box.append(title)
+        self.box.append(dirs_box)
         self.box.append(self.view)
-        self.btn_activate = Gtk.Button(label='activate')
-        self.btn_activate.props.sensitive = False
-        self.btn_activate.connect(
-                'clicked', lambda _: self.activate_order())
-        self.box.append(self.btn_activate)
+        self.box.append(per_row_box)
 
     @classmethod
     def from_suggestion(cls, suggestion_fused):
@@ -168,26 +193,36 @@ class Sorting():
             sort_order.reverse()
         return cls(sort_order)
 
-    def bind_gallery(self, on_update, items_attrs_full, items_attrs_filtered):
+    def bind_gallery(self, on_update, update_per_row, per_row_initial, reload,
+                     toggle_showdirs, toggle_recurse, set_by_1st_sorter,
+                     items_attrs_full, items_attrs_filtered):
         """Connect to Gallery interfaces where necessary."""
         self._gallery_update = on_update
+        self._btn_show_dirs.connect('toggled', toggle_showdirs)
+        self._btn_recurse_dirs.connect('toggled', toggle_recurse)
+        self._btn_reload_dir.connect('clicked', reload)
+        self._gallery_set_by_1st_sorter = set_by_1st_sorter
         self._gallery_items_attrs_full = items_attrs_full
         self._gallery_items_attrs_filtered = items_attrs_filtered
+        self._btn_per_row.set_value(per_row_initial)
+        self._btn_per_row.connect(
+                'value-changed',
+                lambda btn: update_per_row(btn.get_value_as_int()))
 
     def move_selection(self, direction):
         """Move sort order selection by direction (-1 or +1)."""
         min_idx, max_idx = 0, len(self.order) - 1
-        cur_idx = self.selection.props.selected
+        cur_idx = self._selection.props.selected
         if (1 == direction and cur_idx < max_idx)\
                 or (-1 == direction and cur_idx > min_idx):
-            self.selection.props.selected = cur_idx + direction
+            self._selection.props.selected = cur_idx + direction
 
     def move_sorter(self, direction):
         """Move selected item in sort order view, ensure temporary state."""
         tmp_sort_order = []
-        for i in range(self.store.get_n_items()):
-            tmp_sort_order += [self.store.get_item(i)]
-        cur_idx = self.selection.props.selected
+        for i in range(self._store.get_n_items()):
+            tmp_sort_order += [self._store.get_item(i)]
+        cur_idx = self._selection.props.selected
         selected = tmp_sort_order[cur_idx]
         if direction == -1 and cur_idx > 0:
             prev_i = cur_idx - 1
@@ -202,28 +237,26 @@ class Sorting():
         else:  # to catch movement beyond limits
             return
         self.update_box(tmp_sort_order, cur_idx + direction)
-        self.selection.props.selected = cur_idx + direction
-        for i in range(self.store.get_n_items()):
-            sort_item = self.store.get_item(i)
+        self._selection.props.selected = cur_idx + direction
+        for i in range(self._store.get_n_items()):
+            sort_item = self._store.get_item(i)
             sort_item.widget.add_css_class('temp')
-        self.btn_activate.props.sensitive = True
 
     def update_box(self, alt_order=None, cur_selection=0):
         """Rebuild .store from .order, or alt_order if provided."""
         sort_order = alt_order if alt_order else self.order
-        self.store.remove_all()
+        self._store.remove_all()
         for sorter in sort_order:
-            self.store.append(sorter)
-        self.selection.props.selected = cur_selection
+            self._store.append(sorter)
+        self._selection.props.selected = cur_selection
 
     def activate_order(self):
         """Write sort order box order into .order, mark finalized."""
         self.order.clear()
-        for i in range(self.store.get_n_items()):
-            sorter = self.store.get_item(i)
+        for i in range(self._store.get_n_items()):
+            sorter = self._store.get_item(i)
             sorter.widget.remove_css_class('temp')
             self.order += [sorter]
-        self.btn_activate.props.sensitive = False
         self._gallery_update()
 
 
@@ -349,8 +382,8 @@ class Gallery:
         self._on_selection_change = on_selection_change
         self.show_dirs = False
 
-        self.per_row_by_first_sorter = False
-        self.per_row = GALLERY_PER_ROW_DEFAULT
+        self.per_row_by_1st_sorter = False
+        self._per_row = GALLERY_PER_ROW_DEFAULT
         self._slot_margin = GALLERY_SLOT_MARGIN
         self._grid = None
         self._force_width, self._force_height = 0, 0
@@ -387,6 +420,20 @@ class Gallery:
             self._update_view(refocus=False, force=True)
         return True
 
+    def get_per_row(self):
+        """Wrapper to ._per_row to (for setting) discourage direct access."""
+        return self._per_row
+
+    def update_per_row(self, val):
+        """Wrapper to setting ._per_row to include call to .build_and_show."""
+        self._per_row = val
+        self.build_and_show()
+
+    def set_by_1st_sorter(self, val):
+        """On .per_row_by_1st_sorter update, also call .build_and_show."""
+        self.per_row_by_1st_sorter = val
+        self.build_and_show()
+
     @property
     def selected_item(self):
         """Return slot.item at self.selected_idx."""
@@ -548,7 +595,7 @@ class Gallery:
         self.slots = []
         self._grid = Gtk.Grid()
         self._fixed_frame.put(self._grid, 0, 0)
-        if self.per_row_by_first_sorter:
+        if self.per_row_by_1st_sorter:
             self.show_dirs = False
             sort_attrs = []
             for sorter in reversed(self._sort_order):
@@ -556,14 +603,14 @@ class Gallery:
                                 self.items_attrs_filtered[sorter.name])]
             i_row_ref = [0]
             i_slot_ref = [0]
-            self.per_row = len(sort_attrs[-1][1])
+            self._per_row = len(sort_attrs[-1][1])
             build_rows_by_attrs(sort_attrs, entries_filtered)
         else:
             self.dir_entries_filtered_sorted = sorted(
                     entries_filtered, key=cmp_to_key(self._sort_cmp))
             i_row, i_col = 0, 0
             for i, item in enumerate(self.dir_entries_filtered_sorted):
-                if self.per_row == i_col:
+                if self._per_row == i_col:
                     i_col = 0
                     i_row += 1
                 slot = GallerySlot(item, item_clicker(i))
@@ -590,10 +637,10 @@ class Gallery:
     def move_selection(self, x_inc, y_inc, buf_end):
         """Move .selection, update its dependencies, redraw gallery."""
         min_idx, max_idx = 0, len(self.slots) - 1
-        if -1 == y_inc and self.selected_idx >= self.per_row:
-            new_idx = self.selected_idx - self.per_row
-        elif 1 == y_inc and self.selected_idx <= max_idx - self.per_row:
-            new_idx = self.selected_idx + self.per_row
+        if -1 == y_inc and self.selected_idx >= self._per_row:
+            new_idx = self.selected_idx - self._per_row
+        elif 1 == y_inc and self.selected_idx <= max_idx - self._per_row:
+            new_idx = self.selected_idx + self._per_row
         elif -1 == x_inc and self.selected_idx > 0:
             new_idx = self.selected_idx - 1
         elif 1 == x_inc and self.selected_idx < max_idx:
@@ -623,18 +670,18 @@ class Gallery:
         vp_bottom = vp_top + self._viewport_height
         vp_width = (self._force_width if self._force_width
                     else self._viewport.get_width())
-        max_slot_width = (vp_width // self.per_row) - self._slot_margin
+        max_slot_width = (vp_width // self._per_row) - self._slot_margin
         prefered_slot_height = self._viewport_height - self._slot_margin
         slot_size = min(prefered_slot_height, max_slot_width)
         for idx, slot in enumerate(self.slots):
-            slot_top = (idx // self.per_row) * (slot_size + self._slot_margin)
+            slot_top = (idx // self._per_row) * (slot_size + self._slot_margin)
             slot_bottom = slot_top + slot_size
             in_vp = (slot_bottom >= vp_top and slot_top <= vp_bottom)
             slot.update_widget(slot_size, self._slot_margin, in_vp)
         if self._col_headers_grid:
             self._col_headers_frame.remove(self._col_headers_grid)
             self._col_headers_grid = None
-        if self.per_row_by_first_sorter:
+        if self.per_row_by_1st_sorter:
             self._col_headers_grid = Gtk.Grid()
             self._col_headers_frame.put(self._col_headers_grid, 0, 0)
             attr_values = self.items_attrs_filtered[self._sort_order[0].name]
@@ -647,7 +694,7 @@ class Gallery:
             return
         focused_idx = self.selected_idx
         full_slot_height = slot_size + self._slot_margin
-        focused_slot_top = (focused_idx // self.per_row) * full_slot_height
+        focused_slot_top = (focused_idx // self._per_row) * full_slot_height
         focused_slot_bottom = focused_slot_top + slot_size
         if focused_slot_top < vp_top:
             vp_scroll.set_value(focused_slot_top)
@@ -698,9 +745,6 @@ class MainWindow(Gtk.Window):
     metadata: Gtk.TextBuffer
     prev_key: list
     counter: Gtk.Label
-    btn_dec_per_row: Gtk.Button
-    btn_inc_per_row: Gtk.Button
-    btn_show_dirs: Gtk.Button
 
     def __init__(self, app, **kwargs):
         super().__init__(**kwargs)
@@ -719,16 +763,6 @@ class MainWindow(Gtk.Window):
             self.counter = Gtk.Label()
             navbar.append(self.counter)
             add_button('sidebar', lambda _: self.toggle_side_box())
-            add_button('reload', lambda _: self.load_directory())
-            self.btn_show_dirs = add_button('show directories',
-                                            self.reset_show_dirs, True)
-            add_button('recurse directories ', self.reset_recurse, True)
-            navbar.append(Gtk.Label(label=' per row: '))
-            add_button('by first sorter', self.reset_row_by_first_sorter, True)
-            self.btn_dec_per_row = add_button('less',
-                                              lambda _: self.inc_per_row(-1))
-            self.btn_inc_per_row = add_button('more',
-                                              lambda _: self.inc_per_row(+1))
             return navbar
 
         def init_metadata_box():
@@ -771,10 +805,6 @@ class MainWindow(Gtk.Window):
                 on_hit_item=self.hit_gallery_item,
                 on_grid_built=self.app.sort.update_box,
                 on_selection_change=self.update_metadata_on_gallery_selection)
-        self.app.sort.bind_gallery(
-                on_update=self.gallery.build_and_show,
-                items_attrs_full=self.gallery.items_attrs,
-                items_attrs_filtered=self.gallery.items_attrs_filtered)
         self.recurse_dirs = False
 
         setup_css()
@@ -797,11 +827,23 @@ class MainWindow(Gtk.Window):
         self.load_directory(update_gallery_view=False)
         self.connect('notify::focus-widget',
                      lambda _, __: self.on_focus_change())
+        self.app.sort.bind_gallery(
+                on_update=self.gallery.build_and_show,
+                reload=lambda _: self.load_directory(),
+                update_per_row=self.gallery.update_per_row,
+                per_row_initial=self.gallery.get_per_row(),
+                toggle_showdirs=self.reset_show_dirs,
+                toggle_recurse=self.reset_recurse,
+                set_by_1st_sorter=self.gallery.set_by_1st_sorter,
+                items_attrs_full=self.gallery.items_attrs,
+                items_attrs_filtered=self.gallery.items_attrs_filtered)
         GLib.idle_add(self.gallery.build_and_show)
 
     def on_focus_change(self):
         """Handle reactions on focus changes in .gallery and .sort."""
         focused = self.get_focus()
+        if not focused:
+            return
         if isinstance(focused, GallerySlot):
             self.gallery.on_focus_slot(focused)
         elif focused.get_parent() == self.app.sort.view:
@@ -907,28 +949,11 @@ class MainWindow(Gtk.Window):
         side_box_width = self.side_box.measure(OR_H, -1).natural
         self.gallery.on_resize(self.get_width() - side_box_width)
 
-    def reset_row_by_first_sorter(self, button):
-        """By button's .active, (un-)mirror top sorter in each gallery row."""
-        self.gallery.per_row_by_first_sorter = button.props.active
-        self.btn_inc_per_row.set_sensitive(not button.props.active)
-        self.btn_dec_per_row.set_sensitive(not button.props.active)
-        self.btn_show_dirs.set_sensitive(not button.props.active)
-        if button.props.active:
-            self.btn_show_dirs.set_active(False)
-        self.btn_show_dirs.set_sensitive(not button.props.active)
-        self.gallery.build_and_show()
-
     def reset_show_dirs(self, button):
         """By button's .active, in-/exclude directories from gallery view."""
         self.gallery.show_dirs = button.props.active
         self.load_directory()
 
-    def inc_per_row(self, increment):
-        """Change by increment how many items max to display in gallery row."""
-        if self.gallery.per_row + increment > 0:
-            self.gallery.per_row += increment
-            self.gallery.build_and_show()
-
     def reset_recurse(self, button):
         """By button's .active, de-/activate recursion on image collection."""
         self.recurse_dirs = button.props.active
@@ -996,7 +1021,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.sort = Sorting.from_suggestion(opts.sort_order)
+        self.sort = TableConfig.from_suggestion(opts.sort_order)
 
     def do_activate(self, *args, **kwargs):
         """Parse arguments, start window, keep it open."""