diff --git a/src/codex32/_bitcoin_core.py b/src/codex32/_bitcoin_core.py index b2689ea5..b1eae17c 100644 --- a/src/codex32/_bitcoin_core.py +++ b/src/codex32/_bitcoin_core.py @@ -449,12 +449,15 @@ def initialize( continue # Checked last, after any wait above: a pruning node keeps pruning while it waits. self.check_history(timestamp) + # Core's "now" is its tip's time: the genesis block's on an offline signer that never + # synced, and watch-only exports carry it. A new wallet takes this computer's clock, + # a day early like a record's date, so a fast clock cannot skip its first payments. records = core_descriptors( secret, account=account, testnet=self.chain != "main", private=True, - timestamp=timestamp, + timestamp=max(0, int(time.time()) - 86400) if timestamp == "now" else timestamp, ) imported = self._rpc( "importdescriptors", 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_bitcoin_core.py b/tests/test_bitcoin_core.py index f0ecee7e..557f7962 100644 --- a/tests/test_bitcoin_core.py +++ b/tests/test_bitcoin_core.py @@ -391,6 +391,7 @@ def test_encrypted_import_retries_without_a_passphrase_verifies_and_relocks( "_rpc", lambda client, *args, wallet=None, stdin=None: rpc(client, *args, wallet=wallet, stdin=stdin), ) + monkeypatch.setattr("codex32._bitcoin_core.time.time", lambda: 1_700_000_000.5) client = BitcoinCore("bitcoin-cli", "main", 300000) messages: list[str] = [] delays: list[int] = [] @@ -411,7 +412,8 @@ def unlock(seconds: int) -> None: assert arguments == ("importdescriptors",) and wallet == "signer" assert private_stdin is not None and private_stdin.endswith("\n") records = json.loads(private_stdin) - assert len(records) == 4 and all(record["timestamp"] == "now" for record in records) + # A new wallet's "now" is this computer's clock less a day, not Core's tip (genesis offline). + assert len(records) == 4 and all(record["timestamp"] == 1_700_000_000 - 86400 for record in records) assert all("xprv" in record["desc"] for record in records) assert all("xprv" not in " ".join((*args, selected or "")) for args, selected, _data in rpc.calls) assert sum(args[:2] == ("-named", "derivehdkey") for args, _wallet, _stdin in rpc.calls) == 4 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: