mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 20:07:18 +01:00
Some checks failed
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
design_system tests / test (push) Has been cancelled
gatelock pre-commit / pre-commit (push) Has been cancelled
gatelock tests / test (3.10) (push) Has been cancelled
gatelock tests / test (3.11) (push) Has been cancelled
gatelock tests / test (3.12) (push) Has been cancelled
web_ui tests / test (push) Has been cancelled
Markdown across every repo had accumulated a dozen ad-hoc names (refactor_claude_todo.md x23, DESIGN_AUDIT_TODO.md, NEXT_SESSION, HANDOFF, PLAN, SPEC) with nothing distinguishing a live task from a finished record. An audit found docs asserting work was pending that shipped months earlier, and one asserting completion while the work was open. Four namespaces now: README.md, CLAUDE*.md, DOCS*.md, TODO*.md. Task files carry the literal "REMOVE ME AFTER FINISH" and are deleted when the work lands. Enforced bidirectionally -- a TODO without the marker fails, AND any file carrying the marker that is not named TODO*.md fails. Without the reverse rule a task can be renamed into the permanent namespace and quietly live forever, which is the failure being fixed. md_naming/ mirrors the file_length topology and imports its tables (EXCLUDED_DIRS, THIRD_PARTY_REPOS, VENDORED_ANYWHERE, GENERATED_PATTERN) rather than restating them, so the two gates cannot disagree about the same file. 71 tests, 100% branch coverage. stale.py warns on TODOs past 90 days but never fails: no machine can prove a task is finished, so the honest mechanism is a nudge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A47aX5DJwmA3QFn3pkEWi7
48 lines
2.7 KiB
Markdown
48 lines
2.7 KiB
Markdown
# The scrollable lock viewport
|
|
|
|
Design rationale for `gatelock/_scrollable.py` and its collaborators
|
|
(`_scrollfit.py`, `_scrollkeys.py`, `_userintent.py`). Kept here rather
|
|
than in the module docstring so the module itself stays readable in one
|
|
piece; the behaviour it describes is load-bearing, not historical.
|
|
|
|
A keyboard-scrollable, focus-following viewport for lock-surface content.
|
|
|
|
Lock surfaces cannot solve "content taller than the screen" the obvious way.
|
|
A scrollable dialog would be a second ``Toplevel``, and on an
|
|
override-redirect lock surface a second toplevel steals the Tk grab, which the
|
|
recovery tick then kills a second later -- the 2026-07-26 failure where a
|
|
frozen sport selector logged a walk as table tennis. So the viewport has to
|
|
live inside the surface's own widget tree, which is what this provides.
|
|
|
|
Three defects this exists to prevent, all of them Tk defaults rather than
|
|
oversights in any one app:
|
|
|
|
1. **Centered content clips symmetrically.** A ``place``-centered frame takes
|
|
its *requested* size, so when it is too tall the parent shears equal
|
|
amounts off the top *and* the bottom -- losing the header and the submit
|
|
button together, with no scrollbar and no hint anything is missing. This
|
|
top-anchors content inside a scroll region instead.
|
|
2. **A Canvas viewport is not keyboard-scrollable.** ``tk.Canvas`` has no
|
|
class-level key bindings at all, so ``::tk::FocusOK`` rejects it as a focus
|
|
stop and the only way to move the view is dragging the scrollbar thumb with
|
|
a pointer. This makes the canvas focusable and binds PageUp/PageDown,
|
|
Home/End and arrows.
|
|
3. **Focus walks into invisible widgets.** Canvas clipping does not *unmap* a
|
|
child, so every scrolled-out-of-view field stays ``winfo viewable`` and
|
|
stays in the tab chain, while Tk never scrolls to follow focus. Tabbing
|
|
therefore lands on fields the user cannot see and cannot bring into view.
|
|
:meth:`ScrollableSurface.scroll_into_view` fixes that, but only for focus
|
|
the *user* moved.
|
|
|
|
Wheel scrolling is bound too, but as an addition -- never as the only path.
|
|
|
|
**Scrolling is the fallback, not the design.** A lock screen is supposed to
|
|
fit its screen; content that overflows is a layout defect, and :meth:`finalize`
|
|
logs it at ``warning`` so it gets fixed rather than lived with. While the
|
|
content fits -- the normal case -- the scroll region is pinned to the viewport
|
|
and the scrollbar is hidden, so the view cannot move at all. When it does
|
|
overflow, the view still only moves in response to a keypress, a click or the
|
|
wheel: an earlier version followed *programmatic* focus too, and since the apps
|
|
re-focus a widget on every repaint, the screen scrolled itself to mid-content
|
|
and back while the user was doing nothing (reported 2026-08-03, 1366x768).
|