feat(settings): make the settings category list scrollable (#886)
* feat(settings): make the settings category list scrollable Wrap the sidebar category ListBox in a Gtk.ScrolledWindow (matching the page wrappers) so the dialog can shrink vertically without clipping categories and new pages do not pin the minimum window height. Fixes #678 * fix(settings): reveal the selected sidebar row in the scroller * fix(settings): retry sidebar scroll until the selected row is laid out --------- Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
This commit is contained in:
co-authored by
Jatin K Malik
parent
ff108595fd
commit
9e6a03cd20
@@ -2697,7 +2697,15 @@ class SettingsDialog(Gtk.Dialog):
|
||||
for page in self._pages:
|
||||
self.sidebar_listbox.add(self._build_sidebar_row(page))
|
||||
self.sidebar_listbox.connect("row-selected", self._on_sidebar_row_selected)
|
||||
sidebar_box.pack_start(self.sidebar_listbox, True, True, 0)
|
||||
# Same treatment as the pages: without a ScrolledWindow the category
|
||||
# list's natural height pins the dialog's minimum height, so the
|
||||
# window can never shrink past the list and new categories clip.
|
||||
self.sidebar_scroller = Gtk.ScrolledWindow()
|
||||
self.sidebar_scroller.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
||||
self.sidebar_scroller.set_shadow_type(Gtk.ShadowType.NONE)
|
||||
self.sidebar_scroller.add(self.sidebar_listbox)
|
||||
sidebar_scroller = self.sidebar_scroller
|
||||
sidebar_box.pack_start(sidebar_scroller, True, True, 0)
|
||||
|
||||
main_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
||||
main_box.pack_start(sidebar_box, False, False, 0)
|
||||
@@ -2749,6 +2757,7 @@ class SettingsDialog(Gtk.Dialog):
|
||||
self.navigate_to_page(self._initial_page)
|
||||
else:
|
||||
self.sidebar_listbox.select_row(self.sidebar_listbox.get_row_at_index(0))
|
||||
self._scroll_sidebar_selection_into_view()
|
||||
|
||||
# Restore the saved mode and point the simple questions at the live model
|
||||
# before the first visibility pass, so nothing flashes the wrong group.
|
||||
@@ -2773,11 +2782,50 @@ class SettingsDialog(Gtk.Dialog):
|
||||
# Navigation: sidebar, stack, and settings search
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _scroll_sidebar_selection_into_view(self) -> None:
|
||||
"""Bring the selected sidebar row into the scroller's viewport."""
|
||||
row = self.sidebar_listbox.get_selected_row()
|
||||
if row is None:
|
||||
return
|
||||
|
||||
def scroll_to_row() -> bool:
|
||||
allocation = row.get_allocation()
|
||||
if allocation.height <= 0:
|
||||
return True # not laid out yet — try again
|
||||
adjustment = self.sidebar_scroller.get_vadjustment()
|
||||
page_size = adjustment.get_page_size()
|
||||
value = adjustment.get_value()
|
||||
if allocation.y < value:
|
||||
adjustment.set_value(max(adjustment.get_lower(), allocation.y))
|
||||
elif allocation.y + allocation.height > value + page_size:
|
||||
adjustment.set_value(
|
||||
min(
|
||||
adjustment.get_upper() - page_size,
|
||||
allocation.y + allocation.height - page_size,
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
||||
# Allocations exist only once the row is mapped, which may be after
|
||||
# one idle turn; retry briefly instead of leaving the row hidden.
|
||||
# A new selection aborts the retry for the previously chosen row.
|
||||
retries_left = 20
|
||||
|
||||
def scroll_once() -> bool:
|
||||
nonlocal retries_left
|
||||
retries_left -= 1
|
||||
if self.sidebar_listbox.get_selected_row() is not row:
|
||||
return False
|
||||
return scroll_to_row() and retries_left > 0
|
||||
|
||||
GLib.idle_add(scroll_once)
|
||||
|
||||
def navigate_to_page(self, page_name: str) -> bool:
|
||||
"""Select a settings page by its internal name (e.g. ``about``)."""
|
||||
for page in self._pages:
|
||||
if page.name == page_name and page.sidebar_row is not None:
|
||||
self.sidebar_listbox.select_row(page.sidebar_row)
|
||||
self._scroll_sidebar_selection_into_view()
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -2932,6 +2980,7 @@ class SettingsDialog(Gtk.Dialog):
|
||||
self._pages[0],
|
||||
)
|
||||
self.sidebar_listbox.select_row(page.sidebar_row)
|
||||
self._scroll_sidebar_selection_into_view()
|
||||
self.settings_stack.set_visible_child_name(page.name)
|
||||
self._search_previous_page = None
|
||||
|
||||
@@ -2996,6 +3045,7 @@ class SettingsDialog(Gtk.Dialog):
|
||||
|
||||
if first_match_page is not None:
|
||||
self.sidebar_listbox.select_row(first_match_page.sidebar_row)
|
||||
self._scroll_sidebar_selection_into_view()
|
||||
self.settings_stack.set_visible_child_name(first_match_page.name)
|
||||
else:
|
||||
self.sidebar_listbox.unselect_all()
|
||||
|
||||
Reference in New Issue
Block a user