Skip to content

feat(pointer): add an outline tone with a transparent fill and white edge - #353

Merged
LeadcodeDev merged 1 commit into
mainfrom
feat/pointer-outline-tone
Sep 26, 2026
Merged

LeadcodeDev merged 1 commit into
mainfrom
feat/pointer-outline-tone

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Refs #344 — its point 5.

PointerTone was light | dark, both filled. A reference video uses a pointer that is a white outline over a transparent fill. PointerTone::Outline adds it.

Why a third tone rather than a fill field

tone exists precisely so a scene picks one coherent word instead of two matched hex values, and colors() already dispatches fill and outline from a single match arm — a third arm extends that uniformly. A separate fill field would need its own "no fill" semantics, interact awkwardly with the existing color/outline_color overrides, and reintroduce exactly the two-hex-values problem tone was built to avoid. The override mechanism keeps working: a scenario can still set color explicitly for a translucent fill under an outline pointer.

A bug found on the way

The click ring defaulted to the arrow's fill colour. Under Outline, fill is "transparent" — which parses to rgb (0,0,0) with alpha supplied separately at draw time — so the ring would have rendered near-black under a white outline. Not a hard-coded bug, but visually incoherent, and it would have shipped looking like the feature was broken.

ring_fallback_color(&self, fill, outline) threads the already-resolved strings: Outline falls back to outline (white, or the author's outline_color override), Light and Dark keep falling back to fill exactly as before.

Verification

Four tests, all pixel-level:

  • deep_interior_point_is_well_clear_of_the_outline_stroke — the probe point is found via Path::contains plus a margin check, not hand-picked coordinates, so the real test cannot pass by sampling the wrong place
  • outline_tone_leaves_the_interior_transparent_and_paints_a_pointer_coloured_edge — interior must equal the background exactly (0,128,0,255), edge must be solid white
  • filled_tones_still_paint_pointer_colour_in_both_interior_and_edge — pins Light and Dark byte-for-byte, the backward-compatibility guard
  • outline_tone_keeps_the_click_ring_visible_and_matched_to_the_white_outline

Both proven to catch the absence:

# Outline painted filled instead of transparent
an outline pointer must let the background show through its interior,
got (255, 255, 255, 255)
  left: (255, 255, 255, 255)
 right: (0, 128, 0, 255)

# ring_fallback_color forced back to always returning fill
the click ring on an outline pointer must default to the outline's white,
not a hard-coded or transparent-derived colour, got (9, 9, 9, 255)

cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace (1529) all clean.

SKILL.md needs no change: it references pointer only by component name and links to pointer-walkthrough.md, which this PR extends. It never enumerated the tone values.

Written comment-free, per the codebase-wide rule from #345.

…edge

Issue #344 point 5: pointer only had filled tones (light/dark), but a
walkthrough over a busy screenshot or mockup needs an arrow that marks a
spot without covering it — a white outline over a transparent fill.

Chose a third PointerTone variant (Outline) over a separate `fill` field.
`tone` already exists precisely to let a scene pick one coherent word
instead of juggling matched fill/outline hex pairs (see its own doc
comment), and `colors()` already dispatches fill+outline from it in one
match. A `fill` field would need its own semantics for what "no fill"
means, interact awkwardly with `color`/`outline_color`, and reintroduce
the two-hex-values problem `tone` was built to avoid. A third arm keeps
the existing override mechanism (`color`/`outline_color`) working
unchanged: a scenario can still ask for a translucent fill under an
outline pointer by setting `color` explicitly.

The click ring used to default to the arrow's fill color. For Outline,
fill is "transparent" (parses to rgb (0,0,0), alpha only from ring
draw time), so with the old default this would render a black ring
under a white outline: no hardcoded bug, but not "coherent with the
outline style" either. Added `ring_fallback_color`, which threads
through the *already resolved* fill/outline strings instead of adding
a second hardcoded white: outline tone falls back to `outline`, the
two filled tones keep falling back to `fill` exactly as before (so
existing light/dark scenarios render byte-identically — pinned by
`filled_tones_still_paint_pointer_colour_in_both_interior_and_edge`).

Tests measure actual pixels, not field values: an interior point (found
via Path::contains with a margin, not hand-picked) must stay
background-colored for an outline pointer since its fill paint has
alpha 0, while an edge point sitting on the stroke must be opaque and
pointer-colored. Confirmed the interior/edge test fails before the fix
(temporarily reusing the filled arm's colors for Outline): assertion
`left == right` failed, got `(255, 255, 255, 255)` where `(0, 128, 0, 255)`
(the background) was expected. A second pixel test pins the click ring
default: temporarily hardcoding the fallback to `fill` reproduces a
(9, 9, 9, 255) near-black ring against a white outline, which the test
catches and which `ring_fallback_color` fixes.

Extended pointer-walkthrough.md with the new tone and the ring-default
rule. SKILL.md doesn't enumerate PointerTone's values anywhere, so it
needs no update for this change.
@LeadcodeDev LeadcodeDev added the enhancement New feature or request label Sep 26, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 26, 2026
@LeadcodeDev
LeadcodeDev merged commit 1dc49d3 into main Sep 26, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant