SetMetricScale: an audit that finds the widgets ignoring it, and the first fixes - #208
Merged
Conversation
…first fixes
The knob is documented as the thing a HiDPI host sets once so "the whole toolkit
lays out and paints crisp at that resolution". Ten files out of a hundred and
sixty route anything through it. The rest carry raw pixel constants that stay
put on a screen with twice the pixels, so the chrome shrinks to half its
intended size while the window around it is sharp -- and nothing says so.
TestMetricScaleAudit finds them instead of my reading a hundred files and
guessing which numbers are pixels. It draws a widget at scale 1 and at scale 2
and compares the RUN LENGTHS along three rows and three columns: a widget whose
metrics scale gives the same runs, twice as wide.
Three things that instrument had to learn, each from being wrong:
- Widths, not boundary positions. Positions move by one pixel when a
one-pixel border fails to scale, which is indistinguishable from the
rounding of an odd metric -- that version declared the very defect this was
written to find to be within tolerance.
- Runs no wider than one LOGICAL pixel are dropped: an anti-aliased edge is a
coverage ramp, not a metric, and at twice the scale it may resolve into two
steps where it had one. In logical units, because a border that DOES scale
is one pixel at 1x and two at 2x.
- Three lines per axis, reported only when all three disagree. A metric that
does not scale is wrong on every line; a diagonal crossing an exact scanline
is not, and calling that a defect would have me contorting a widget to
satisfy a probe.
It measures chrome, not text: type carries its own scale and a centred baseline
rounds, so a scanline landing one glyph row off reports arithmetic. Whether text
should follow the metric scale is a real question and a separate one.
The fixes it produced, over eighteen widgets:
- strokeRect / strokeRoundRect ask for a stroke one LOGICAL pixel wide. This
one line fixes Button, Card, ProgressBar, Chip, Entry, Alert, Banner, Frame
and every other framed widget at once -- and it needed painter v0.11.0,
which until now took a stroke width and discarded it.
- CheckButton: the box, the label gap, and the checkmark, which was a
hard-coded twelve-pixel path sitting in the corner of a box twice its size.
The classic 12px tick is kept byte-for-byte for the classic 12px box.
- Switch: the knob inset.
- Expander/Accordion: the header height, through a new ExpanderHeaderHeight().
- Gauge: the arc thickness.
Controlled both ways: with the fixes the audit is green; reverting the stroke
width flags five of the six original widgets, and reverting only the checkbox
flags exactly CheckButton.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 16, 2026
Merged
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.
SetMetricScaleis documented as the thing a HiDPI host sets once so that "the whole toolkit lays out and paints crisp at that resolution". Ten files out of a hundred and sixty route anything through it. The rest carry raw pixel constants that stay put on a screen with twice the pixels, so the chrome shrinks to half its intended size while the window around it is sharp — and nothing anywhere says so.Now that all four
go-widgets/windowback-ends can hand a widget tree a 2× framebuffer, this is the half that was missing.The instrument
TestMetricScaleAuditfinds them, instead of my reading a hundred files and guessing which numbers are pixels. It draws a widget at scale 1 and at scale 2 and compares the run lengths along three rows and three columns: a widget whose metrics scale gives the same runs, twice as wide.Three things it had to learn, each from being wrong:
It measures chrome, not text: type carries its own scale and a centred baseline rounds. Whether text should follow the metric scale is a real question and a separate one.
The fixes, over eighteen widgets
strokeRect/strokeRoundRectask for a stroke one logical pixel wide. This one line fixes Button, Card, ProgressBar, Chip, Entry, Alert, Banner, Frame and every other framed widget at once. It needed painter#17 (v0.11.0), which until now took a stroke width and discarded it.ExpanderHeaderHeight(). Gauge: the arc thickness.Control
The catalogue is eighteen widgets of roughly a hundred and forty. Growing it is the mechanism for the rest: every widget added either passes or names its own defect.
🤖 Generated with Claude Code