feat(gui): find a setting on the Control page by name - #131
Merged
Conversation
The Control page renders the whole registry - a dozen sections, forty-odd fields, several folded away on a fresh install - and there was no way to reach a setting by name. This is the pure half of that: `build_index` turns the registry plus the current translation into entries in page order, `find` matches a query against them, `summarise` separates what this page can jump to from what lives in the Settings window. Matching covers field and section NAMES plus CLI flags, deliberately not tooltip bodies: searching the tips would make one word light up half the page, which is a symptom index rather than a way to find a field. `fold()` strips diacritics through NFKD before comparing. Polish labels carry them and people type without them, so without this "opoznienie" finds nothing while the accented spelling works - a half-broken feature nobody can diagnose from the outside. The index is built after the language is set rather than at import time: a language switch rebuilds the UI precisely so that nothing keeps the old words, and an import-time index would keep answering in them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The page renders the whole registry and there was no way to reach a setting except by opening every section and reading. A box at the top now marks what matches and scrolls to it: Enter walks the matches, Escape clears, Ctrl+F puts the caret in the box. It marks and jumps rather than filtering. Hiding what does not match would fight the page's own layout - fields are packed left to right in rows, so a hidden field leaves a hole, and sections are spread over two columns by weights computed at build time, so sections disappearing rebalances the page under the reader. Revealing a hit uses set_open, never toggle. Toggling runs the accordion's callback, which persists the fold state, so searching would permanently unfold the sections a user had chosen to keep closed. The folds are snapshotted once per search - not per keystroke, or the second letter would record what the first letter opened - and restored when the box is cleared. A field that renders in the Settings window is indexed but never offered as a jump: the page says where it lives instead of coming back empty. The query survives a rebuild (a language switch rebuilds the UI, the two-column threshold rebuilds the form) and dies with the process. ControlForm gained on_rebuilt so the marks can be put back on widgets that no longer exist. One dispatcher now owns Ctrl+F. Both pages bind it on the root, and a root binding without add= REPLACES the one before it, so the page built second would otherwise have taken the shortcut away from the other. From a page with no search box it keeps the older behaviour and brings the connection table forward. The mutation that used to break the table's Ctrl+F survives now for a good reason - two bindings mean losing one changes nothing - so it was replaced by one that breaks the dispatcher's decision instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both READMEs describe the window as it is, so a visible feature that is not in them reads as one that does not exist. The shortcut table gained Ctrl+F, which now means "search" on both pages that have a box. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three things the search got wrong, all reported after using it. With several matches nothing said which one you were on: Enter moved the page and every hit looked the same, so the counter pointed at a place the eye could not pick out. Marking is two-strength now - the current match is filled, the rest are tinted - and Shift+Enter walks back. The highlight was the page accent, which is the colour of every section header, link and "?" button, so it disappeared into the page. Hits are amber now: the one meaning this page had left, with blue for the accent, green for running, red for a fault and pink for the support button. The bar moved to the right, against the page margin. The row is built so nothing shifts while typing: the count is pinned to the margin at a fixed width, the box and its label sit left of it, and the sentence naming another window is a separate label further left, free to grow into empty space. Found on real Tk while checking those: two hits can claim the SAME widget - a dropdown has no text of its own so it marks its section header, and that header is often a hit itself. The header was painted current and then repainted as an ordinary match, so "1 / 5" showed with nothing filled anywhere. The second claim on a widget is dropped, which is also what keeps the count honest: as many marks as it says, each in its own place. The fake tkinter cannot catch this class - one widget class for everything, no style validation. Two more edge cases: the page now tracks what the SEARCH opened instead of snapshotting what was closed when it started, so a section the user opens by hand mid-search stays open; and unmarking hands the last word to apply_overrides, so a style that changed while a field was marked cannot be restored into a stale one. Co-Authored-By: Claude Opus 5 <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.
The Control page renders the whole field registry - a dozen sections, forty-odd fields, several of them folded away on a fresh install - and there was no way to reach a setting except by opening every section and reading. This adds a search box for it.
What it does
Type part of a field or section name, or a flag such as
--loss. Every match is highlighted, the one you are on is filled in, and the counter in the corner points at it.Enterwalks forward,Shift+Enterback,Escapeclears,Ctrl+Fputs the caret in the box.set_open, nevertoggle- toggling runs the accordion's callback, which persists the fold state, so searching would permanently unfold sections the user had chosen to keep closed.Structure
gui/form_search.pyholds the matching: registry plus the current translation in, entries in page order out. It has no Tk, so it is tested directly instead of through the fake-Tk subprocess. The page owns the highlighting, the scrolling and the fold rules.The index is built after the language is set rather than at import time - a language switch rebuilds the UI precisely so nothing keeps the old words. The query survives that rebuild (and the two-column one) and dies with the process;
ControlFormgainedon_rebuiltso the marks can be put back on widgets that no longer exist.Ctrl+Fnow goes through one dispatcher. Both pages with a search box bind it on the root, and a root binding withoutadd=REPLACES the one before it, so the page built second would otherwise have taken the shortcut away from the other. From a page with no box it keeps the older behaviour and brings the connection table forward.What the live pass found
The fake tkinter has one widget class for everything and never validates a style name, so the whole feature was also driven on real Tk. That is where the last defect came from: two hits can claim the SAME widget - a dropdown has no text of its own so it marks its section header, and that header is often a hit itself. It was painted as the current match and then repainted as an ordinary one by the later hit, so "1 / 5" appeared with nothing filled anywhere. The second claim on a widget is dropped now, which is what keeps the count honest: as many marks as it says, each in its own place.
Guarded
tests/test_form_search.py(pure): the index covers every registry entry, blank and punctuation-only queries find nothing, case and substring matching, section names, CLI flags in three spellings, page order, the Settings-window split, and an accented label reachable without its accents.tests/test_gui_layout.py: marking and clearing, current versus other and its wrap-around, the highlight not reusing the page accent, the section-header fallback, both fold rules, the "it lives in the Settings window" answer, the empty result, the widget collision, surviving a form rebuild, oneCtrl+Freaching whichever box is in front, and the Settings window - which renders the sameControlForm- having no search box.Ctrl+Fby moving the binding off the root, and with two pages binding the same dispatcher that no longer breaks anything, so it now breaks the dispatcher's decision instead.Full suite 1156 passed,
smoke_gui.pyOK, mutation registry 106 caught with the canary BROKEN, both real-driver smokes clean.