From: Christian Heller Date: Mon, 4 Nov 2024 15:46:31 +0000 (+0100) Subject: Browser: Further typify and refactor code. X-Git-Url: https://plomlompom.com/repos/%7B%7Bdb.prefix%7D%7D/%7B%7B%20web_path%20%7D%7D/decks/template?a=commitdiff_plain;h=2abb1bb43ed1067d422c0f52b9b90d97e28486b9;p=stable_plom Browser: Further typify and refactor code. --- diff --git a/browser.py b/browser.py index e1f2e60..ff99733 100755 --- a/browser.py +++ b/browser.py @@ -28,7 +28,8 @@ from stable.gen_params import (GenParams, GEN_PARAMS_FLOAT, # noqa: E402 AttrVals: TypeAlias = list[str] AttrValsByVisibility: TypeAlias = dict[str, AttrVals] ItemsAttrs: TypeAlias = dict[str, AttrValsByVisibility] -Cache: TypeAlias = dict[str, dict[str, dict[str, str | float | int]]] +CachedImg: TypeAlias = dict[str, str | float | int] +Cache: TypeAlias = dict[str, dict[str, CachedImg]] Bookmarks: TypeAlias = list[str] Db: TypeAlias = Cache | Bookmarks FilterInputs: TypeAlias = dict[str, str] @@ -83,10 +84,10 @@ def _add_button(parent: Gtk.Widget, class JsonDb: """Representation of our simple .json DB files.""" + _content: Db def __init__(self, path: str) -> None: self._path = path - self._content: Db = {} self._is_open = False if not path_exists(path): with open(path, 'w', encoding='utf8') as f: @@ -111,14 +112,29 @@ class JsonDb: json_dump(self._content, f) self._close() - def as_copy(self) -> Db: + +class BookmarksDb(JsonDb): + """Representation of Bookmarks DB files.""" + _content: Bookmarks + + def as_ref(self) -> Bookmarks: + """Return content at ._path as ref so that .write() stores changes.""" + self._open() + return self._content + + def as_copy(self) -> Bookmarks: """Return content at ._path for read-only purposes.""" self._open() - dict_copy = self._content.copy() + copy = self._content.copy() self._close() - return dict_copy + return copy + + +class CacheDb(JsonDb): + """Representation of Cache DB files.""" + _content: Cache - def as_ref(self) -> Db: + def as_ref(self) -> Cache: """Return content at ._path as ref so that .write() stores changes.""" self._open() return self._content @@ -134,8 +150,8 @@ 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.bookmarks_db = JsonDb(BOOKMARKS_PATH) - self.cache_db = JsonDb(CACHE_PATH) + self.bookmarks_db = BookmarksDb(BOOKMARKS_PATH) + self.cache_db = CacheDb(CACHE_PATH) self.sort_order = SorterAndFiltererOrder.from_suggestion( opts.sort_order.split(',')) @@ -149,28 +165,27 @@ class Application(Gtk.Application): class SorterAndFilterer(GObject.GObject): """Sort order box representation of sorting/filtering attribute.""" widget: Gtk.Box - label: Gtk.Label def __init__(self, name: str) -> None: super().__init__() self.name = name def setup_on_bind(self, - widget: Gtk.Widget, + widget: Gtk.Box, on_filter_activate: Callable, filter_text: str, - vals: dict[str, str], + vals: AttrValsByVisibility, ) -> None: """Set up SorterAndFilterer label, values listing, filter entry.""" self.widget = widget # label len_incl = len(vals['incl']) - len_semi_total = len_incl + len(vals['semi']) - len_total = len_semi_total + len(vals['excl']) + len_semi_total: int = len_incl + len(vals['semi']) + len_total: int = len_semi_total + len(vals['excl']) title = f'{self.name} ({len_incl}/{len_semi_total}/{len_total}) ' self.widget.label.set_text(title) # values listing - vals_listed = [f'{v}' for v in vals['incl']] + vals_listed: list[str] = [f'{v}' for v in vals['incl']] vals_listed += [f'{v}' for v in vals['semi']] vals_listed += [f'{v}' for v in vals['excl']] self.widget.values.set_text(', '.join(vals_listed)) @@ -215,12 +230,12 @@ class SorterAndFiltererOrder: @classmethod def from_suggestion(cls, suggestion: list[str]) -> Self: """Create new, interpreting order of strings in suggestion.""" - names = [p.lower() for p in GEN_PARAMS] + ['bookmarked'] - order = [] + names: list[str] = [p.lower() for p in GEN_PARAMS] + ['bookmarked'] + order: list[SorterAndFilterer] = [] for name in names: order += [SorterAndFilterer(name)] - new_order = [] - do_reverse = '-' in suggestion + new_order: list[SorterAndFilterer] = [] + do_reverse: bool = '-' in suggestion for pattern in suggestion: for sorter in [sorter for sorter in order if sorter.name.startswith(pattern)]: @@ -252,9 +267,9 @@ class SorterAndFiltererOrder: def switch_at(self, selected_idx: int, forward: bool) -> None: """Switch elements at selected_idx and its neighbor.""" - selected = self[selected_idx] - other_idx = selected_idx + (1 if forward else -1) - other = self[other_idx] + selected: SorterAndFilterer = self[selected_idx] + other_idx: int = selected_idx + (1 if forward else -1) + other: SorterAndFilterer = self[other_idx] self._list[other_idx] = selected self._list[selected_idx] = other @@ -266,11 +281,11 @@ class GalleryItem(GObject.GObject): def __init__(self, path: str, name: str) -> None: super().__init__() self.name = name - self.full_path = path_join(path, self.name) + self.full_path: str = path_join(path, self.name) self.slot: GallerySlot def __hash__(self) -> int: - hashable_values = [] + hashable_values: list[str | bool] = [] for attr_name in self._to_hash: hashable_values += [getattr(self, attr_name)] return hash(tuple(hashable_values)) @@ -295,8 +310,8 @@ class ImgItem(GalleryItem): super().__init__(path, name) mtime = getmtime(self.full_path) dt = datetime.fromtimestamp(mtime, tz=timezone.utc) - iso8601_str = dt.isoformat(timespec='microseconds') - self.last_mod_time = iso8601_str.replace('+00:00', 'Z') + iso8601_str: str = dt.isoformat(timespec='microseconds') + self.last_mod_time: str = iso8601_str.replace('+00:00', 'Z') self.bookmarked = False self.with_others = False self.has_metadata = False @@ -321,7 +336,7 @@ class ImgItem(GalleryItem): gen_params = GenParams.from_str(gen_params_as_str) for k, v_ in gen_params.as_dict.items(): setattr(self, k, v_) - cached = {} + cached: CachedImg = {} for k in (k.lower() for k in GEN_PARAMS): cached[k] = getattr(self, k) cache[self.full_path] = {self.last_mod_time: cached} @@ -336,10 +351,11 @@ class GallerySlotsGeometry: """Collect variable sizes shared among all GallerySlots.""" def __init__(self) -> None: - self._margin = GALLERY_SLOT_MARGIN + self._margin: int = 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 + self.side_margin: int = self._margin // 2 + self.size: int = -1 + self.size_sans_margins: int = -1 def set_size(self, size: int) -> None: """Not only set .size but also update .size_sans_margins.""" @@ -386,7 +402,7 @@ class GallerySlot(Gtk.Button): def update_widget(self, is_in_vp: bool) -> None: """(Un-)load slot, for Imgs if (not) is_in_vp, update CSS class.""" - new_content = None + new_content: Optional[Gtk.Image | Gtk.Label] = None if isinstance(self.item, ImgItem): if is_in_vp and not isinstance(self.item, Gtk.Image): new_content = Gtk.Image.new_from_file(self.item.full_path) @@ -422,7 +438,6 @@ class GalleryConfig(): def __init__(self, box: Gtk.Box, - # sort_order: list[SorterAndFilterer], sort_order: SorterAndFiltererOrder, request_update: Callable, update_settings: Callable, @@ -451,14 +466,14 @@ class GalleryConfig(): def on_filter_activate(entry: Gtk.Box) -> None: entry.remove_css_class('temp') - text = entry.get_buffer().get_text() + text: str = entry.get_buffer().get_text() if '' != text.rstrip(): self.filter_inputs[sorter.name] = text elif sorter.name in self.filter_inputs: del self.filter_inputs[sorter.name] self._filter_inputs_changed = True - sorter = list_item.props.item + sorter: SorterAndFilterer = list_item.props.item sorter.setup_on_bind(list_item.props.child, on_filter_activate, self.filter_inputs.get(sorter.name, ''), self._gallery_items_attrs[sorter.name]) @@ -572,7 +587,7 @@ class GalleryConfig(): self.update_box(tmp_sort_order, cur_idx + direction) self._sort_sel.props.selected = cur_idx + direction for i in range(self._store.get_n_items()): - sort_item = self._store.get_item(i) + sort_item: SorterAndFilterer = self._store.get_item(i) sort_item.widget.add_css_class('temp') def update_box(self, @@ -630,15 +645,15 @@ class Gallery: def __init__(self, on_hit_item: Callable, on_selection_change: Callable, - bookmarks_db: JsonDb, - cache_db: JsonDb + bookmarks_db: BookmarksDb, + cache_db: CacheDb ) -> None: self._on_hit_item = on_hit_item self._on_selection_change = on_selection_change self._bookmarks_db, self._cache_db = bookmarks_db, cache_db self._sort_order = SorterAndFiltererOrder([]) self._filter_inputs: FilterInputs = {} - self._img_dir_path = None + self._img_dir_path = '' self._shall_load = False self._shall_build = False @@ -675,7 +690,7 @@ class Gallery: self._viewport.set_scroll_to_focus(False) # prefer our own handling def ensure_uptodate() -> bool: - if self._img_dir_path is None: + if not self._img_dir_path: return True if self._shall_load: self._load_directory() @@ -725,13 +740,18 @@ class Gallery: def _load_directory(self) -> None: """Rewrite .dir_entries from ._img_dir_path, trigger rebuild.""" + self._shall_load = False + self.dir_entries.clear() + bookmarks = self._bookmarks_db.as_copy() + cache = self._cache_db.as_ref() - def read_directory(dir_path, make_parent=False): + def read_directory(dir_path: str, make_parent: bool = False) -> None: if make_parent: parent_dir = DirItem(abspath(path_join(dir_path, UPPER_DIR)), UPPER_DIR, is_parent=True) self.dir_entries += [parent_dir] - dirs_to_enter, to_set_metadata_on = [], [] + dirs_to_enter: list[str] = [] + to_set_metadata_on: list[ImgItem] = [] dir_entries = list(listdir(dir_path)) for i, filename in enumerate(dir_entries): msg = f'loading {dir_path}: entry {i+1}/{len(dir_entries)}' @@ -763,10 +783,6 @@ class Gallery: print(f'{prefix}{i+1}/{len(dirs_to_enter)}') read_directory(path) - self._shall_load = False - self.dir_entries = [] - bookmarks = self._bookmarks_db.as_copy() - cache = self._cache_db.as_ref() read_directory(self._img_dir_path, make_parent=True) self._cache_db.write() self.request_update(build=True) @@ -871,6 +887,8 @@ class Gallery: def _build(self) -> None: """(Re-)build slot grid from .dir_entries, filters, layout settings.""" + self._shall_build = False + old_selected_item: Optional[GalleryItem] = self.selected_item def build_items_attrs() -> None: self.items_attrs.clear() @@ -881,7 +899,7 @@ class Gallery: items_attrs_tmp: ItemsAttrs = {} for attr_name in (s.name for s in self._sort_order): items_attrs_tmp[attr_name] = {'incl': [], 'excl': []} - vals = set() + vals: set[str] = set() for entry in [e for e in entries if isinstance(e, ImgItem)]: val = (getattr(entry, attr_name) @@ -909,7 +927,7 @@ class Gallery: self.items_attrs[attr_name] = final_values def filter_entries(items_attrs: ItemsAttrs) -> list[GalleryItem]: - entries_filtered = [] + entries_filtered: list[GalleryItem] = [] for entry in self.dir_entries: if (not self._show_dirs) and isinstance(entry, DirItem): continue @@ -927,6 +945,14 @@ class Gallery: def build_grid(entries_filtered: list[GalleryItem]) -> None: i_row_ref, i_slot_ref = [0], [0] + if self._grid.get_parent(): + self._fixed_frame.remove(self._grid) + self._grid = Gtk.Grid() + if self._col_headers_grid.get_parent(): + self._col_headers_frame.remove(self._col_headers_grid) + self._col_headers_grid = Gtk.Grid() + self.slots.clear() + self._fixed_frame.put(self._grid, 0, 0) def build_rows_by_attrs( remaining: list[tuple[str, AttrVals]], @@ -966,29 +992,21 @@ class Gallery: build_rows_by_attrs(remaining[1:], items_of_attr_value, ancestors + [(attr_name, attr_value)]) - if self._grid.get_parent(): - self._fixed_frame.remove(self._grid) - self._grid = Gtk.Grid() - if self._col_headers_grid.get_parent(): - self._col_headers_frame.remove(self._col_headers_grid) - self._col_headers_grid = Gtk.Grid() - self.slots = [] - self._fixed_frame.put(self._grid, 0, 0) if self._by_1st: self._show_dirs = False - sort_attrs = [] + sort_attrs: list[tuple[str, AttrVals]] = [] for sorter in reversed(self._sort_order): - vals = self.items_attrs[sorter.name]['incl'] + vals: AttrVals = self.items_attrs[sorter.name]['incl'] if len(vals) > 1: sort_attrs += [(sorter.name, vals)] if not sort_attrs: - s_name = self._sort_order[0].name + s_name: str = self._sort_order[0].name sort_attrs += [(s_name, self.items_attrs[s_name]['incl'])] self._per_row = len(sort_attrs[-1][1]) build_rows_by_attrs(sort_attrs, entries_filtered, []) self._col_headers_frame.put(self._col_headers_grid, 0, 0) self._col_headers_grid.attach(Gtk.Box(), 0, 0, 1, 1) - top_attr_name = sort_attrs[-1][0] + top_attr_name: str = sort_attrs[-1][0] for i, val in enumerate(sort_attrs[-1][1]): label = Gtk.Label(label=f'{top_attr_name}: {val}', xalign=0, @@ -997,7 +1015,7 @@ class Gallery: self._col_headers_grid.attach(label, i + 1, 0, 1, 1) else: - dir_entries_filtered_sorted = sorted( + dir_entries_filtered_sorted: list[GalleryItem] = sorted( entries_filtered, key=cmp_to_key(self._sort_cmp)) i_row, i_col = 0, 0 for i, item in enumerate(dir_entries_filtered_sorted): @@ -1011,8 +1029,6 @@ class Gallery: i_col += 1 self.update_config_box() - self._shall_build = False - old_selected_item = self.selected_item build_items_attrs() entries_filtered = filter_entries(self.items_attrs) build_grid(entries_filtered) @@ -1071,33 +1087,35 @@ class Gallery: def _redraw_and_check_focus(self) -> None: """Draw gallery; possibly notice and first follow need to re-focus.""" - vp_width = (self._force_width if self._force_width - else self._viewport.get_width()) - vp_height = (self._force_height if self._force_height - else self._viewport.get_height()) + vp_width: int = (self._force_width if self._force_width + else self._viewport.get_width()) + vp_height: int = (self._force_height if self._force_height + else self._viewport.get_height()) self._force_width, self._force_height = 0, 0 - vp_scroll = self._viewport.get_vadjustment() - vp_top = vp_scroll.get_value() - vp_bottom = vp_top + vp_height + vp_scroll: Gtk.Adjustment = self._viewport.get_vadjustment() + vp_top: int = vp_scroll.get_value() + vp_bottom: int = vp_top + vp_height side_offset, i_vlabels = 0, 0 if self._by_1st: while True: - widget = self._grid.get_child_at(i_vlabels, 0) - if isinstance(widget, VerticalLabel): - side_offset += widget.width + gal_widget: VerticalLabel | GalleryItem + gal_widget = self._grid.get_child_at(i_vlabels, 0) + if isinstance(gal_widget, VerticalLabel): + side_offset += gal_widget.width else: break i_vlabels += 1 - max_slot_width = (vp_width - side_offset) // self._per_row + max_slot_width: int = (vp_width - side_offset) // self._per_row self._slots_geometry.set_size(min(vp_height, max_slot_width)) if self._by_1st: i_widgets = 0 while True: - widget = self._col_headers_grid.get_child_at(i_widgets, 0) + head_widget: Gtk.Box | Gtk.Label | None + head_widget = self._col_headers_grid.get_child_at(i_widgets, 0) if 0 == i_widgets: - widget.set_size_request(side_offset, -1) - elif isinstance(widget, Gtk.Label): - widget.set_size_request(self._slots_geometry.size, -1) + head_widget.set_size_request(side_offset, -1) + elif isinstance(head_widget, Gtk.Label): + head_widget.set_size_request(self._slots_geometry.size, -1) else: break i_widgets += 1 @@ -1119,8 +1137,8 @@ class Gallery: vp_bottom: int, in_vp_greedy: bool = False ) -> tuple[bool, int, int]: - slot_top = (idx // self._per_row) * self._slots_geometry.size - slot_bottom = slot_top + self._slots_geometry.size + slot_top: int = (idx // self._per_row) * self._slots_geometry.size + slot_bottom: int = slot_top + self._slots_geometry.size if in_vp_greedy: in_vp = (slot_bottom >= vp_top and slot_top <= vp_bottom) else: @@ -1132,7 +1150,7 @@ class Gallery: vp_top: int, vp_bottom: int ) -> bool: - scroll_to_focus = self._shall_scroll_to_focus + scroll_to_focus: bool = 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( @@ -1184,76 +1202,67 @@ class Gallery: class MainWindow(Gtk.Window): """Image browser app top-level window.""" - metadata: Gtk.TextBuffer - prev_key: list - topbar: Gtk.Label def __init__(self, app: Application, **kwargs) -> None: super().__init__(**kwargs) self.app = app - - def init_navbar() -> Gtk.Box: - navbar = Gtk.Box(orientation=OR_H) - _add_button(navbar, 'sidebar', lambda _: self.toggle_side_box()) - self.topbar = Gtk.Label() - navbar.append(self.topbar) - return navbar - - def init_metadata_box() -> Gtk.TextView: - text_view = Gtk.TextView(wrap_mode=Gtk.WrapMode.WORD_CHAR, - editable=False) - text_view.set_size_request(300, -1) - self.metadata = text_view.get_buffer() - return text_view - - def init_key_control() -> None: - key_ctl = Gtk.EventControllerKey( - propagation_phase=Gtk.PropagationPhase.CAPTURE) - key_ctl.connect('key-pressed', - lambda _, kval, _0, _1: self.handle_keypress(kval)) - self.add_controller(key_ctl) - self.prev_key = [0] - - def setup_css() -> None: - css_provider = Gtk.CssProvider() - css_provider.load_from_data(CSS) - Gtk.StyleContext.add_provider_for_display( - self.get_display(), css_provider, - Gtk.STYLE_PROVIDER_PRIORITY_USER) - self.gallery = Gallery( on_hit_item=self.hit_gallery_item, on_selection_change=self.update_metadata_on_gallery_selection, bookmarks_db=self.app.bookmarks_db, cache_db=self.app.cache_db) + config_box = Gtk.Box(orientation=OR_V) + self.conf = GalleryConfig( + sort_order=self.app.sort_order, + box=config_box, + request_update=self.gallery.request_update, + update_settings=self.gallery.update_settings, + items_attrs=self.gallery.items_attrs) + self.gallery.update_config_box = self.conf.update_box + metadata_textview = Gtk.TextView(wrap_mode=Gtk.WrapMode.WORD_CHAR, + editable=False) + self.metadata = metadata_textview.get_buffer() + self.idx_display = Gtk.Label() - setup_css() - viewer = Gtk.Box(orientation=OR_V) - self.navbar = init_navbar() - viewer.append(self.navbar) - viewer.append(self.gallery.frame) + # layout: outer box, CSS, sizings + box_outer = Gtk.Box(orientation=OR_H) + self.set_child(box_outer) + css_provider = Gtk.CssProvider() + css_provider.load_from_data(CSS) + Gtk.StyleContext.add_provider_for_display( + self.get_display(), css_provider, + Gtk.STYLE_PROVIDER_PRIORITY_USER) + metadata_textview.set_size_request(300, -1) + self.connect('notify::default-width', lambda _, __: self.on_resize()) + self.connect('notify::default-height', lambda _, __: self.on_resize()) + + # layout: sidebar self.side_box = Gtk.Notebook.new() - self.side_box.append_page(init_metadata_box(), + self.side_box.append_page(metadata_textview, Gtk.Label(label='metadata')) - config_box = Gtk.Box(orientation=OR_V) self.side_box.append_page(config_box, Gtk.Label(label='config')) - box_outer = Gtk.Box(orientation=OR_H) box_outer.append(self.side_box) + + # layout: gallery viewer + viewer = Gtk.Box(orientation=OR_V) + self.navbar = Gtk.Box(orientation=OR_H) + _add_button(self.navbar, 'sidebar', lambda _: self.toggle_side_box()) + self.navbar.append(self.idx_display) + viewer.append(self.navbar) + viewer.append(self.gallery.frame) box_outer.append(viewer) - self.props.child = box_outer - self.connect('notify::default-width', lambda _, __: self.on_resize()) - self.connect('notify::default-height', lambda _, __: self.on_resize()) - init_key_control() + # init key and focus control + key_ctl = Gtk.EventControllerKey( + propagation_phase=Gtk.PropagationPhase.CAPTURE) + key_ctl.connect('key-pressed', + lambda _, kval, _0, _1: self.handle_keypress(kval)) + self.add_controller(key_ctl) + self.prev_key_ref = [0] self.connect('notify::focus-widget', lambda _, __: self.on_focus_change()) - self.conf = GalleryConfig( - sort_order=self.app.sort_order, - box=config_box, - request_update=self.gallery.request_update, - update_settings=self.gallery.update_settings, - items_attrs=self.gallery.items_attrs) - self.gallery.update_config_box = self.conf.update_box + + # only now we're ready for actually running the gallery GLib.idle_add(lambda: self.gallery.update_settings( img_dir_path=self.app.img_dir_absolute, sort_order=self.conf.order.copy(), @@ -1261,7 +1270,7 @@ class MainWindow(Gtk.Window): def on_focus_change(self) -> None: """Handle reactions on focus changes in .gallery and .conf.""" - focused = self.get_focus() + focused: Optional[Gtk.Widget] = self.get_focus() if not focused: return if isinstance(focused, GallerySlot): @@ -1274,8 +1283,8 @@ class MainWindow(Gtk.Window): if self.get_width() > 0: # So we don't call this on initial resize. # NB: We .measure side_box because its width is changing, whereas # for the unchanging navbar .get_height is sufficient. - side_box_width = self.side_box.measure(OR_H, -1).natural - default_size = self.get_default_size() + side_box_width: int = self.side_box.measure(OR_H, -1).natural + default_size: tuple[int, int] = self.get_default_size() self.gallery.on_resize(default_size[0] - side_box_width, default_size[1] - self.navbar.get_height()) @@ -1284,7 +1293,6 @@ class MainWindow(Gtk.Window): if not isinstance(self.gallery.selected_item, ImgItem): return bookmarks = self.app.bookmarks_db.as_ref() - assert isinstance(bookmarks, list) if self.gallery.selected_item.bookmarked: self.gallery.selected_item.bookmark(False) bookmarks.remove(self.gallery.selected_item.full_path) @@ -1296,7 +1304,7 @@ class MainWindow(Gtk.Window): def hit_gallery_item(self) -> None: """If current file selection is directory, reload into that one.""" - selected = self.gallery.selected_item + selected: Optional[GalleryItem] = self.gallery.selected_item if isinstance(selected, DirItem): self.gallery.update_settings(img_dir_path=selected.full_path) @@ -1305,13 +1313,13 @@ class MainWindow(Gtk.Window): self.side_box.props.visible = not self.side_box.get_visible() # Calculate new viewport directly, because GTK's respective viewport # measurement happens too late for our needs. - side_box_width = self.side_box.measure(OR_H, -1).natural + side_box_width: int = self.side_box.measure(OR_H, -1).natural self.gallery.on_resize(self.get_width() - side_box_width) def update_metadata_on_gallery_selection(self) -> None: - """Update .metadata about individual file, .topbar also on idx/total""" + """Update .metadata about individual file, and .idx_display.""" self.metadata.set_text('') - selected_item = self.gallery.selected_item + selected_item: Optional[GalleryItem] = self.gallery.selected_item display_name = '(none)' if selected_item: if isinstance(selected_item, ImgItem): @@ -1326,10 +1334,10 @@ class MainWindow(Gtk.Window): display_name = selected_item.full_path total = len([s for s in self.gallery.slots if isinstance(s.item, (DirItem, ImgItem))]) - n_selected = self.gallery.selected_idx + 1 + n_selected: int = self.gallery.selected_idx + 1 txt = f' {n_selected} of {total} – {display_name}' - self.topbar.set_text(txt) - self.topbar.set_use_markup(True) + self.idx_display.set_text(txt) + self.idx_display.set_use_markup(True) def handle_keypress(self, keyval: int) -> bool: """Handle keys if not in Gtk.Entry, return True if key handling done""" @@ -1348,7 +1356,7 @@ class MainWindow(Gtk.Window): self.gallery.move_selection(None, -1, None) elif Gdk.KEY_l == keyval: self.gallery.move_selection(+1, None, None) - elif Gdk.KEY_g == keyval and Gdk.KEY_g == self.prev_key[0]: + elif Gdk.KEY_g == keyval and Gdk.KEY_g == self.prev_key_ref[0]: self.gallery.move_selection(None, None, -1) elif Gdk.KEY_w == keyval: self.conf.move_selection(-1) @@ -1361,7 +1369,7 @@ class MainWindow(Gtk.Window): elif Gdk.KEY_b == keyval: self.bookmark() else: - self.prev_key[0] = keyval + self.prev_key_ref[0] = keyval return False return True