fix(gui): bind the root <Configure> handler once, not per rebuild#41
Merged
Conversation
App._build_ui runs on every language switch and the root window outlives it, so binding <Configure> on the root inside _build_ui (add="+", nothing removing it) accumulated one handler per rebuild: measured 2 after the first build, 8 after three switches. The earlier "not fixed here" note named only _on_root_configure and undercounted - the two banners built with wrapping_label(root, ...) each bound their own <Configure> on the same persistent root and multiplied identically. - _on_root_configure is bound once in __init__; the line is gone from _build_ui. It reaches its widgets through self, so one binding always drives the freshly rebuilt ones - no unbind (which is not safe on the oldest Python in the CI matrix anyway). - The two banners are plain ttk.Labels wrapped by that same handler instead of wrapping_label; same look, no leak. labels.py and its other callers untouched. - Not user-visible (identical look/wrapping), so CHANGELOG.md is left alone. New test asserts exactly one <Configure> handler on the root across rebuilds and that the banner still wraps to the width-derived value; both halves verified by mutation. Full suite green (660 passed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Follow-up to #40. That PR's changelog flagged one thing as deliberately deferred:
App._build_uire-binds<Configure>on the root every rebuild and never removes the old one, so the handler multiplies. This closes it — and a related leak the earlier note missed._build_uiruns on every language switch, and the root window outlives it. Binding<Configure>on the root inside_build_ui(withadd="+", nothing removing it) piled up one handler per rebuild.Measured on the fake tkinter (
<Configure>handlers on the root):The #40 note named only
_on_root_configureand undercounted (PROJECT_NOTES rule 5): the two banners built withwrapping_label(root, ...)—engine_warningalways,admin_warningwhen non-admin — each bound their own<Configure>on the same persistent root and multiplied identically, half the handlers. Awrapping_labelon a short-lived container does not leak (its binding dies with the container); only the two whose container is the root do.Each handler is cheap, and after #40 the dead ones are no-ops rather than crash records — so this is
O(rebuilds)duplicated work on every resize, not a crash. Invisible, but real.Changes (app.py only)
_on_root_configureis now bound once in__init__, before the first_build_ui; the line is gone from_build_ui. It reaches its widgets throughself, so a single binding always drives the freshly rebuilt ones — nounbindneeded, and none was safe (Misc.unbind(seq, funcid)still clears the whole sequence on the oldest Python in the CI matrix, and the root carries other<Configure>bindings).ttk.Labels wrapped by that same single handler, notwrapping_label. Same look (left/anchored, an initialwraplengthrefined on the first resize).gui/labels.pyand its short-lived-container callers are untouched;wrapping_labelis no longer imported bygui/app.py.Reviewer notes
test_the_ui_rebuild_does_not_pile_up_configure_handlers_on_the_rootasserts exactly one<Configure>handler on the root after several rebuilds, and that the banner still wraps to the width-derived value (not just its build-time default — which would pass even with the fold gone). Reintroducing the per-rebuild bind, and dropping the banner wrap, each turns it red.CHANGELOG.mdline for an imperceptible change would be the filler PROJECT_NOTES rule 4 forbids. Documented inCHANGELOG-INTERNAL.md, which also corrects the stale "not fixed here" bullet from fix(gui): stop the UI rebuild from destroying the registry's windows #40.Checklist
python -m pytest testspasses locally. (660 passed; run elevated, no admin-only deselects needed.)tests/for the style). One added, mutation-verified (both halves).lang/en.jsonandlang/pl.jsonupdated. (No new UI text — nolang/edits needed.)CHANGELOG.md; technical ones and new tests inCHANGELOG-INTERNAL.md, under[Unreleased]. (Internal-only: nothing a user notices, see reviewer notes.)type(scope): summary).VERSION.txt.🤖 Generated with Claude Code