From ea00ebab7a54ca3e6177ede0d3c1495508f33565 Mon Sep 17 00:00:00 2001 From: Ben Westgate Date: Tue, 10 Nov 2026 22:19:25 +0000 Subject: [PATCH] gui: Grow the window so no page needs scrolling Every page now asks the window to grow, never past the screen's height, whenever its content is taller than the space it has: on arrival, when a section opens, and when a message gets longer. A window the operator shrinks grows back to fit. The scroller stays as the last resort on a screen too short for the page. Name-and-value rows (wallet identity, wallet record, new card) are one line each, value on the right, so the finished page fits the default 880x620 window and reads straight down against the paper record. The GUI size budget rises by the 13 lines this takes. Claude-Session: https://claude.ai/code/session_01N8jLK9eptxLjUcNnMk1VGr --- src/codex32_gui/pages.py | 18 +++++++++++++++--- src/codex32_gui/style.py | 5 +++++ tests/test_gui_boundaries.py | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/codex32_gui/pages.py b/src/codex32_gui/pages.py index 6ed80813..4dbccb95 100644 --- a/src/codex32_gui/pages.py +++ b/src/codex32_gui/pages.py @@ -12,7 +12,7 @@ from dataclasses import dataclass from typing import Literal -from gi.repository import Adw, Gio, GLib, Gtk +from gi.repository import Adw, Gio, GLib, Gtk, Pango from codex32 import ( ConfirmationResult, @@ -111,6 +111,7 @@ def _page( ) -> Adw.NavigationPage: scroller = Gtk.ScrolledWindow(hscrollbar_policy=Gtk.PolicyType.NEVER, vexpand=True) scroller.set_child(Adw.Clamp(maximum_size=700, child=content, margin_start=18, margin_end=18)) + scroller.get_vadjustment().connect("changed", lambda shown: _grow(scroller, shown)) bars = Adw.ToolbarView() bars.add_top_bar(Adw.HeaderBar()) bars.set_content(scroller) @@ -121,6 +122,15 @@ def _page( return page +def _grow(scroller: Gtk.ScrolledWindow, shown: Gtk.Adjustment) -> None: + """Make the window taller, never past the screen, until the page needs no scrolling.""" + window, short = scroller.get_root(), shown.get_upper() - shown.get_page_size() + if short > 0 and isinstance(window, Gtk.Window) and (surface := window.get_surface()) is not None: + width, height = window.get_default_size() + screen = window.get_display().get_monitor_at_surface(surface).get_geometry().height + window.set_default_size(width, min(height + int(short) + 1, screen)) + + def _column(*children: Gtk.Widget) -> Gtk.Box: box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=14, margin_top=18, margin_bottom=18) for child in children: @@ -215,9 +225,11 @@ def _blank(flow: Gtk.FlowBox) -> None: def _rows(title: str, values: Sequence[tuple[str, str]]) -> Adw.PreferencesGroup: - group = Adw.PreferencesGroup(title=title) + group = Adw.PreferencesGroup(title=title, css_classes=["compact"]) for label, value in values: - group.add(Adw.ActionRow(title=label, subtitle=value, subtitle_selectable=True, use_markup=False)) + row = Adw.ActionRow(title=label, use_markup=False) + row.add_suffix(Gtk.Label(label=value, selectable=True, wrap=True, wrap_mode=Pango.WrapMode.WORD_CHAR)) + group.add(row) return group diff --git a/src/codex32_gui/style.py b/src/codex32_gui/style.py index 2bc7879b..6c3cbca2 100644 --- a/src/codex32_gui/style.py +++ b/src/codex32_gui/style.py @@ -27,6 +27,11 @@ border-radius: 12px; padding: 14px; } +.compact row > box.header { + min-height: 0; + padding-top: 0; + padding-bottom: 0; +} .reading { font-size: 0.9rem; } diff --git a/tests/test_gui_boundaries.py b/tests/test_gui_boundaries.py index 1d73c9c7..e2ada80c 100644 --- a/tests/test_gui_boundaries.py +++ b/tests/test_gui_boundaries.py @@ -33,7 +33,7 @@ } ) CORE_ADAPTER = "codex32._bitcoin_core" -BUDGET = 2050 +BUDGET = 2063 def _package() -> Path: