diff --git a/sims/modules/launcher/base.py b/sims/modules/launcher/base.py index 89800a5..d7924a1 100644 --- a/sims/modules/launcher/base.py +++ b/sims/modules/launcher/base.py @@ -90,6 +90,7 @@ class FuzzyMenu(Window): self._items: list[Any] = [] self._filtered: list[Any] = [] self._selected_index: int = 0 + self._scroll_offset: int = 0 self.viewport = Box(name="viewport", spacing=4, orientation="v") @@ -115,12 +116,14 @@ class FuzzyMenu(Window): self._items = self._provider.items() self.search_entry.set_text("") self._selected_index = 0 + self._scroll_offset = 0 self._refresh_viewport("") super().show() self.search_entry.grab_focus() def _on_text_changed(self, entry, *_): self._selected_index = 0 + self._scroll_offset = 0 self._refresh_viewport(entry.get_text()) def _on_key_press(self, _widget, event): @@ -130,6 +133,9 @@ class FuzzyMenu(Window): if keyval == Gdk.KEY_Escape: self.hide() return True + if ctrl and keyval in (Gdk.KEY_g, Gdk.KEY_G): + self.hide() + return True if keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter): self._activate_selected() return True @@ -141,27 +147,51 @@ class FuzzyMenu(Window): return True return False + def _window_size(self) -> int: + return self._max_results if self._max_results is not None else len(self._filtered) + def _move_selection(self, delta: int): if not self._filtered: return - self._selected_index = (self._selected_index + delta) % len(self._filtered) - self._update_selection_highlight() + new_index = self._selected_index + delta + new_index = max(0, min(new_index, len(self._filtered) - 1)) + if new_index == self._selected_index: + return + self._selected_index = new_index + window = self._window_size() + if window <= 0: + self._scroll_offset = 0 + elif self._selected_index < self._scroll_offset: + self._scroll_offset = self._selected_index + elif self._selected_index >= self._scroll_offset + window: + self._scroll_offset = self._selected_index - window + 1 + self._render_visible() def _refresh_viewport(self, query: str): self._filtered = self._provider.filter(self._items, query) - if self._max_results is not None: - self._filtered = self._filtered[: self._max_results] if self._selected_index >= len(self._filtered): self._selected_index = 0 + self._scroll_offset = 0 + self._render_visible() + + def _render_visible(self): + window = self._window_size() + if window <= 0: + visible: list[Any] = [] + else: + max_offset = max(0, len(self._filtered) - window) + self._scroll_offset = min(self._scroll_offset, max_offset) + visible = self._filtered[self._scroll_offset : self._scroll_offset + window] self.viewport.children = [] - for item in self._filtered: + for item in visible: self.viewport.add(self._provider.render(item)) self._update_selection_highlight() def _update_selection_highlight(self): + visible_index = self._selected_index - self._scroll_offset for i, child in enumerate(self.viewport.get_children()): ctx = child.get_style_context() - if i == self._selected_index: + if i == visible_index: ctx.add_class("selected") else: ctx.remove_class("selected") diff --git a/sims/modules/launcher/clipboard.py b/sims/modules/launcher/clipboard.py index d78a57c..28548cd 100644 --- a/sims/modules/launcher/clipboard.py +++ b/sims/modules/launcher/clipboard.py @@ -48,6 +48,7 @@ class ClipboardProvider: label=item.preview, h_align="start", ellipsization="end", + max_chars_width=120, name="clip-preview", ), ], diff --git a/sims/styles/launcher.css b/sims/styles/launcher.css index 4e28575..bea4a7e 100644 --- a/sims/styles/launcher.css +++ b/sims/styles/launcher.css @@ -9,6 +9,7 @@ font-family: sans-serif; font-size: 14px; color: white; + min-width: 720px; } #viewport { @@ -53,3 +54,4 @@ font-size: 11px; opacity: 0.6; } +