home · contact · privacy
In browser, add option for recursive browsing.
authorChristian Heller <c.heller@plomlompom.de>
Sun, 8 Sep 2024 04:20:44 +0000 (06:20 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 8 Sep 2024 04:20:44 +0000 (06:20 +0200)
browser.py

index 99cfad905a9fe5fdb2d52e2b1b7e1ee4e8d9abf9..c8d5d305e687c26515f0842c862c937f87255a0d 100755 (executable)
@@ -94,6 +94,7 @@ class MainWindow(Gtk.Window):
     gallery_store_filtered: Gtk.FilterListModel
     gallery_selection: Gtk.SingleSelection
     include_dirs: bool
+    recurse_dirs: bool
     per_row: int
     metadata: Gtk.TextBuffer
     sort_order: list
@@ -110,7 +111,7 @@ class MainWindow(Gtk.Window):
                 btn.connect('clicked', on_click)
                 parent_box.append(btn)
             navbar = Gtk.Box(orientation=OR_H)
-            add_button('folder_view', lambda _: self.toggle_side_box(), navbar)
+            add_button('sidebar', lambda _: self.toggle_side_box(), navbar)
             add_button('reload', lambda _: self.load_directory(), navbar)
             navbar.append(Gtk.Label(label=' per row: '))
             add_button('less', lambda _: self.inc_per_row(-1), navbar)
@@ -118,6 +119,9 @@ class MainWindow(Gtk.Window):
             btn = Gtk.CheckButton(label='show directories')
             btn.connect('toggled', self.reset_include_dirs)
             navbar.append(btn)
+            btn = Gtk.CheckButton(label='recurse directories')
+            btn.connect('toggled', self.reset_recurse)
+            navbar.append(btn)
             return navbar
 
         def init_gallery_widgets():
@@ -127,16 +131,17 @@ class MainWindow(Gtk.Window):
                     lambda _: self.update_file_selection())
             self.gallery.connect(
                     'child-activated', lambda _, __: self.hit_file_selection())
-            gallery_scroller = Gtk.ScrolledWindow(
-                    child=self.gallery, propagate_natural_height=True)
-            gallery_scroller.get_vadjustment().connect(
+            scroller = Gtk.ScrolledWindow(propagate_natural_height=True)
+            scroller.get_vadjustment().connect(
                     'value-changed', lambda _: self.update_gallery_view())
-            # attach a maximally expanded dummy that will be destroyed once we
-            # bind self.gallery to a model; seems necessary to pre-stretch the
-            # gallery_scroller's viewport for our first calculation (in the
-            # first run of update_gallery) of what images to load into it
-            self.gallery.append(Gtk.Box(hexpand=True, vexpand=True))
-            return gallery_scroller
+            # We want our viewport at always maximum possible size (so we can
+            # safely calculate what's in it and what not), even if the gallery
+            # would be smaller. Therefore we frame the gallery in an expanding
+            # Fixed, to stretch out the viewport even if the gallery is small.
+            viewport_stretcher = Gtk.Fixed(hexpand=True, vexpand=True)
+            viewport_stretcher.put(self.gallery, 0, 0)
+            scroller.props.child = viewport_stretcher
+            return scroller
 
         def init_metadata_box():
             text_view = Gtk.TextView()
@@ -174,6 +179,7 @@ class MainWindow(Gtk.Window):
             self.gallery_selection = Gtk.SingleSelection.new(
                     self.gallery_store_filtered)
             self.include_dirs = False
+            self.recurse_dirs = False
             self.per_row = 3
 
         def init_key_control():
@@ -264,9 +270,9 @@ class MainWindow(Gtk.Window):
             i = 0
             while True:
                 gallery_item_at_i = self.gallery.get_child_at_index(i)
-                item_path = gallery_item_at_i.props.child.item.full_path
                 if gallery_item_at_i is None:
                     break
+                item_path = gallery_item_at_i.props.child.item.full_path
                 if suggested_selection.full_path == item_path:
                     to_select = gallery_item_at_i
                     break
@@ -278,19 +284,21 @@ class MainWindow(Gtk.Window):
 
     def update_gallery_view(self):
         """Load/unload gallery's file images based on viewport visibility."""
-        viewport = self.gallery.get_parent()
+        viewport = self.gallery.get_parent().get_parent()
         vp_height = viewport.get_height()
         vp_width = viewport.get_width()
         vp_top = viewport.get_vadjustment().get_value()
         vp_bottom = vp_top + vp_height
-        max_slot_width = vp_width / self.per_row - 6
-        slot_size = min(vp_height, max_slot_width)
+        margin = 6
+        max_slot_width = vp_width / self.per_row - margin
+        prefered_slot_height = vp_height - margin
+        slot_size = min(prefered_slot_height, max_slot_width)
         for i in range(self.gallery_store_filtered.get_n_items()):
             slot = self.gallery.get_child_at_index(i).props.child
             if isinstance(slot.item, DirItem):
                 slot.content.set_size_request(slot_size, slot_size)
                 continue
-            slot_top = (i // self.per_row) * slot_size
+            slot_top = (i // self.per_row) * (slot_size + margin)
             slot_bottom = slot_top + slot_size
             in_viewport = (slot_bottom >= vp_top and slot_top <= vp_bottom)
             if in_viewport:
@@ -371,28 +379,32 @@ class MainWindow(Gtk.Window):
     def load_directory(self, update_gallery=True):
         """Load into gallery directory at self.img_dir_absolute."""
 
-        def read_directory_into_gallery_items(cache):
-            directory = Gio.File.new_for_path(self.img_dir_absolute)
+        def read_directory_into_gallery_items(cache, dir_path,
+                                              make_parent=False):
+            directory = Gio.File.new_for_path(dir_path)
             query_attrs = 'standard::name,time::*'
-            parent_path = abspath(path_join(self.img_dir_absolute, UPPER_DIR))
-            parent_dir = directory.get_parent()
-            parent_dir_info = parent_dir.query_info(
-                    query_attrs, Gio.FileQueryInfoFlags.NONE, None)
-            parent_dir_item = DirItem(
-                    parent_path, parent_dir_info, is_parent=True)
-            self.gallery_store.append(parent_dir_item)
+            if make_parent:
+                parent_path = abspath(path_join(dir_path, UPPER_DIR))
+                parent_dir = directory.get_parent()
+                parent_dir_info = parent_dir.query_info(
+                        query_attrs, Gio.FileQueryInfoFlags.NONE, None)
+                parent_dir_item = DirItem(
+                        parent_path, parent_dir_info, is_parent=True)
+                self.gallery_store.append(parent_dir_item)
             query_attrs = query_attrs + ',standard::content-type'
             enumerator = directory.enumerate_children(
                     query_attrs, Gio.FileQueryInfoFlags.NONE, None)
             to_set_metadata_on = []
             for info in enumerator:
-                if self.include_dirs\
-                        and info.get_file_type() == Gio.FileType.DIRECTORY:
-                    self.gallery_store.append(DirItem(self.img_dir_absolute,
-                                                      info))
+                if info.get_file_type() == Gio.FileType.DIRECTORY:
+                    if self.include_dirs:
+                        self.gallery_store.append(DirItem(dir_path, info))
+                    if self.recurse_dirs:
+                        read_directory_into_gallery_items(
+                                cache, path_join(dir_path, info.get_name()))
                 elif info.get_content_type()\
                         and info.get_content_type().startswith('image/'):
-                    item = ImgItem(self.img_dir_absolute, info, cache)
+                    item = ImgItem(dir_path, info, cache)
                     if '' == item.model:
                         to_set_metadata_on += [item]
                     self.gallery_store.append(item)
@@ -409,7 +421,7 @@ class MainWindow(Gtk.Window):
                 json_dump({}, f)
         with open(CACHE_PATH, 'r', encoding='utf8') as f:
             cache = json_load(f)
-        read_directory_into_gallery_items(cache)
+        read_directory_into_gallery_items(cache, self.img_dir_absolute, True)
         with open(CACHE_PATH, 'w', encoding='utf8') as f:
             json_dump(cache, f)
         if update_gallery:
@@ -433,6 +445,11 @@ class MainWindow(Gtk.Window):
             self.update_gallery(self.gallery_selection.props.selected_item,
                                 sort=False)
 
+    def reset_recurse(self, button):
+        """By button's .active, de-/activate recursion on image collection."""
+        self.recurse_dirs = button.props.active
+        self.load_directory()
+
     # movement
 
     def move_sort(self, direction):