From 368cd8c631a53e5a8c2b4d4828d6e98cba31b333 Mon Sep 17 00:00:00 2001 From: DonislawDev Date: Thu, 20 Aug 2026 19:30:06 +0200 Subject: [PATCH] fix(gui): give paired checkboxes the gap every other pair already had Reported from the running build the day after the two LAN switches landed side by side: "Internet only" started exactly where the "LAN mode" label ended, so the pair read as one control rather than two. The BOOL branch of the form packs its Checkbutton straight into the row with no padding at all, while every other kind goes through a cell carrying padx=(0, scaled(22)). That difference was invisible for as long as no two checkboxes ever shared a row, which is a state that existed for one day. Both paths ask _gap_after(field) now, so the two cannot drift apart again: a field that owns its row only has to clear the card edge, two that share one get a real gap. Measured on real Tk at 1366x768: 22 px between them in both languages, the same as any other pair of fields, with neither label clipped and 59-63 px still spare to the right of the second one. Guard: the gap assertion added to the test that already checks the two share a row, plus a MUTATIONS entry that reddens it. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 3 +++ beantester/gui/form.py | 19 ++++++++++++++++--- tests/test_gui_layout.py | 8 ++++++++ tests/test_mutation_registry.py | 10 ++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb83891..d12bc58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,9 @@ The format follows [Keep a Changelog](https://keepachangelog.com/); versions fol ### Fixed +- **The two LAN checkboxes were touching.** "Internet only" started right where the "LAN mode" + label ended, with nothing between them, so the pair read as one control. They now have the same + gap as any other two settings sharing a row. - The right-click copy menu on the Statistics page opened with a white background instead of the dark one used everywhere else. It now looks like the menu in the Connections table, on both Live and Session. diff --git a/beantester/gui/form.py b/beantester/gui/form.py index 4f06bf0..ad148e6 100644 --- a/beantester/gui/form.py +++ b/beantester/gui/form.py @@ -48,6 +48,20 @@ def _takes_a_row(field): neighbour. Only the registry may say so - the caller reads the answer. """ return field.kind in SPAN_KINDS if field.span is None else field.span + + +def _gap_after(field): + """How much room to leave to the RIGHT of a field inside its row. + + A field that owns its row only has to clear the card's edge. Two that SHARE + one need a real gap between them, or they read as a single control: the pair + of LAN switches shipped touching, the second one's box hard against the end + of the first one's label, because the checkbox branch packed with no padding + at all while every other kind went through a cell that had some. + + One place, so the two paths cannot drift apart again. + """ + return scaled(6) if _takes_a_row(field) else scaled(22) VALIDATED_KINDS = (F.NUMBER, F.EXPR, F.SCHEDULE, F.SEED) # Below this width a second column of sections would squeeze the wider rows @@ -216,7 +230,7 @@ def _place_one(self, row, field, sec): widget = ttk.Checkbutton(row, text=T(field.label), variable=app.vars[field.key], command=app.on_form_changed) - widget.pack(side="left", anchor="w") + widget.pack(side="left", anchor="w", padx=(0, _gap_after(field))) add_tooltip(widget, field.tip) self.entries[field.key] = widget return @@ -240,8 +254,7 @@ def _place_one(self, row, field, sec): span = _takes_a_row(field) cell = ttk.Frame(row, style="Card.TFrame") - cell.pack(side="left", fill="x", expand=span, - padx=(0, scaled(6) if span else scaled(22))) + cell.pack(side="left", fill="x", expand=span, padx=(0, _gap_after(field))) label = ttk.Label(cell, text=T(field.label), style="Card.TLabel") label.pack(side="left", padx=(0, scaled(6))) diff --git a/tests/test_gui_layout.py b/tests/test_gui_layout.py index 4029e27..2e30b18 100644 --- a/tests/test_gui_layout.py +++ b/tests/test_gui_layout.py @@ -932,4 +932,12 @@ def test_the_two_lan_switches_share_one_row(): assert lan.master is net.master, "the LAN switches are not in one row" assert app.form.entries["filter"].master is not lan.master, ( "the traffic dropdown was pulled into the checkbox row") + + # ...and they must not TOUCH. They shipped with the second one's box hard + # against the end of the first one's label, because the checkbox branch + # packed with no padding while every other kind went through a cell that + # had some. Two controls with nothing between them read as one. + gap = (lan.pack_info or {}).get("padx") + gap = gap[1] if isinstance(gap, (tuple, list)) else gap + assert gap, "no room to the right of the first switch: %r" % (lan.pack_info,) """) diff --git a/tests/test_mutation_registry.py b/tests/test_mutation_registry.py index 8851a15..830fa38 100644 --- a/tests/test_mutation_registry.py +++ b/tests/test_mutation_registry.py @@ -1345,6 +1345,16 @@ "new": " return field.kind in SPAN_KINDS", "test": "test_the_two_lan_switches_share_one_row", }, + { + # How they shipped touching: the checkbox branch packed with no padding + # while every other kind went through a cell that had some, so the pair + # only looked wrong once two of them ended up in one row. + "label": "gui: paired checkboxes lose the gap between them", + "file": "beantester/gui/form.py", + "old": ' widget.pack(side="left", anchor="w", padx=(0, _gap_after(field)))', + "new": ' widget.pack(side="left", anchor="w")', + "test": "test_the_two_lan_switches_share_one_row", + }, { # Without the idle hint the row is one cluster in a band of nothing - # the shape that has now been reported twice.