class GallerySlot(Gtk.Button):
"""Slot in Gallery representing a GalleryItem."""
- def __init__(self, item, on_click_file=None):
+ def __init__(self, item, slots_geometry, on_click_file=None):
super().__init__()
+ self._geometry = slots_geometry
self.add_css_class('slot')
self.set_hexpand(True)
self.item = item
self.item.slot = self
if on_click_file:
self.connect('clicked', on_click_file)
- self._slot_size = None
- self._side_margin = None
def mark(self, css_class, do_add=True):
"""Add or remove css_class from self."""
else:
self.remove_css_class(css_class)
- def ensure_slot_size(self, slot_size, margin):
+ def ensure_slot_size(self):
"""Call ._size_widget to size .props.child; if none, make empty one."""
- self._slot_size = slot_size
- self._side_margin = margin // 2
if self.get_child() is None:
self.set_child(Gtk.Label(label='+'))
self._size_widget()
def _size_widget(self):
for s in ('bottom', 'top', 'start', 'end'):
- setattr(self.get_child().props, f'margin_{s}', self._side_margin)
- self.get_child().set_size_request(self._slot_size, self._slot_size)
+ setattr(self.get_child().props, f'margin_{s}',
+ self._geometry.side_margin)
+ self.get_child().set_size_request(self._geometry.size_sans_margins,
+ self._geometry.size_sans_margins)
def update_widget(self, is_in_vp):
"""(Un-)load slot, for Imgs if (not) is_in_vp, update CSS class."""
self.mark('bookmarked', self.item.bookmarked)
+class GallerySlotsGeometry:
+ """Collect variable sizes shared among all GallerySlots."""
+
+ def __init__(self):
+ self._margin = GALLERY_SLOT_MARGIN
+ assert 0 == self._margin % 2 # avoid ._margin != 2 * .side_margin
+ self.side_margin = self._margin // 2
+ self.size, self.size_sans_margins = -1, -1
+
+ def set_size(self, size):
+ """Not only set .size but also update .size_sans_margins."""
+ self.size = size
+ self.size_sans_margins = self.size - self._margin
+
+
class GalleryItem(GObject.GObject):
"""Gallery representation of filesystem entry, base to DirItem, ImgItem."""
slot: GallerySlot
class VerticalLabel(Gtk.DrawingArea):
"""Label of vertical text (rotated -90°)."""
- def __init__(self, text, max_height_ref):
+ def __init__(self, text, slots_geometry):
super().__init__()
self._text = text
- self._max_height_ref = max_height_ref
+ self._slots_geometry = slots_geometry
test_layout = self.create_pango_layout()
test_layout.set_markup(text)
_, self._text_height = test_layout.get_pixel_size()
"""Create layout, rotate by 90°, size widget to measurements."""
layout = self.create_pango_layout()
layout.set_markup(self._text)
- layout.set_width(self._max_height_ref[0] * Pango.SCALE)
+ layout.set_width(self._slots_geometry.size * Pango.SCALE)
layout.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
text_width, _ = layout.get_pixel_size()
cairo_ctx.translate(0, text_width + (height - text_width))
self._recurse_dirs = False
self._by_1st = False
self._per_row = GALLERY_PER_ROW_DEFAULT
- self._slot_margin = GALLERY_SLOT_MARGIN
+ self._slots_geometry = GallerySlotsGeometry()
self.dir_entries = []
self.items_attrs = {}
scroller = Gtk.ScrolledWindow(propagate_natural_height=True)
self._col_headers_frame = Gtk.Fixed()
self._col_headers_grid = None
- self._slot_size_ref = [0]
self.frame = Gtk.Box(orientation=OR_V)
self.frame.append(self._col_headers_frame)
self.frame.append(scroller)
if 1 == len(remaining):
for i, attr in enumerate(ancestors):
vlabel = VerticalLabel(f'<b>{attr[0]}</b>: {attr[1]}',
- self._slot_size_ref)
+ self._slots_geometry)
self._grid.attach(vlabel, i, i_row_ref[0], 1, 1)
row = [None] * len(attr_values)
for item in items_of_parent:
row[idx_val_in_attr_values] = item
for i_col, item in enumerate(row):
if item:
- slot = GallerySlot(item,
- item_clicker(i_slot_ref[0]))
+ slot = GallerySlot(item, self._slots_geometry)
else:
- slot = GallerySlot(GalleryItem('', '')) # dummy
+ slot = GallerySlot(GalleryItem('', ''), # dummy
+ self._slots_geometry)
self.slots += [slot]
i_slot_ref[0] += 1
self._grid.attach(slot, i_col + len(ancestors),
if self._per_row == i_col:
i_col = 0
i_row += 1
- slot = GallerySlot(item, item_clicker(i))
+ slot = GallerySlot(item, self._slots_geometry,
+ item_clicker(i))
self._grid.attach(slot, i_col, i_row, 1, 1)
self.slots += [slot]
i_col += 1
i_vlabels += 1
max_slot_width = (vp_width - side_offset) // self._per_row
slot_size = min(vp_height, max_slot_width)
- self._slot_size_ref[0] = slot_size
+ self._slots_geometry.set_size(slot_size)
if self._by_1st:
i_widgets = 0
while True:
if 0 == i_widgets:
widget.set_size_request(side_offset, -1)
elif isinstance(widget, Gtk.Label):
- widget.set_size_request(slot_size, -1)
+ widget.set_size_request(self._slots_geometry.size, -1)
else:
break
i_widgets += 1
- slot_size_sans_margin = slot_size - self._slot_margin
for idx, slot in enumerate(self.slots):
- slot.ensure_slot_size(slot_size_sans_margin, self._slot_margin)
- vp_scroll.set_upper(slot_size * ceil(len(self.slots) / self._per_row))
+ slot.ensure_slot_size()
+ vp_scroll.set_upper(self._slots_geometry.size * ceil(len(self.slots)
+ / self._per_row))
if self._scroll_to_focus(vp_scroll, vp_top, vp_bottom):
return
for idx, slot in enumerate(self.slots):
def _position_to_viewport(
self, idx, vp_top, vp_bottom, in_vp_greedy=False):
- slot_size = self._slot_size_ref[0]
- slot_top = (idx // self._per_row) * slot_size
- slot_bottom = slot_top + slot_size
+ slot_top = (idx // self._per_row) * self._slots_geometry.size
+ slot_bottom = slot_top + self._slots_geometry.size
if in_vp_greedy:
in_vp = (slot_bottom >= vp_top and slot_top <= vp_bottom)
else:
return in_vp, slot_top, slot_bottom
def _scroll_to_focus(self, vp_scroll, vp_top, vp_bottom):
- slot_size = self._slot_size_ref[0]
scroll_to_focus = self._shall_scroll_to_focus
self._shall_redraw, self._shall_scroll_to_focus = False, False
if scroll_to_focus:
in_vp, slot_top, slot_bottom = self._position_to_viewport(
- self.selected_idx, slot_size, vp_top, vp_bottom)
+ self.selected_idx, vp_top, vp_bottom)
if not in_vp:
self._shall_redraw, self._shall_scroll_to_focus = True, True
if slot_top < vp_top:
vp_scroll.set_value(slot_top)
else:
- vp_scroll.set_value(slot_bottom - slot_size)
+ vp_scroll.set_value(slot_bottom
+ - self._slots_geometry.size)
return True
return False