Skip to content

Fix six defects found by the pie rendering stack audit - #75

Merged
danielchalmers merged 7 commits into
mainfrom
claude/pie-audit-fixes
Aug 10, 2026
Merged

Fix six defects found by the pie rendering stack audit#75
danielchalmers merged 7 commits into
mainfrom
claude/pie-audit-fixes

Conversation

@danielchalmers

Copy link
Copy Markdown
Owner

Summary

An exhaustive audit of the Pie/* stack (focused on the interactions between #67-#73) verified 12 defect hypotheses via an instrumented build driven by a UI harness, STA-hosted tests, and code trace. Four confirmed bugs and three latent ones are fixed here, one commit each:

  • Double execution on hotkey release - with KeepMenuOpenAfterSliceClick, clicking a slice while holding the hotkey left the release latch armed, so releasing it executed the slice again (twice total, verified live). The latch now clears when a slice fires.
  • Phantom reorder drag - since Stop rebuilding the pie on every open and build it before the first frame聽#73 the visuals survive across opens, so a press that ended while the menu was hidden left _dragCandidate armed; moving over that slice with the button held in the next open started a drag with no press (verified live). Drag tracking now resets on open.
  • Flick fired the wrong slice in keyboard mode - releasing the hotkey triggered the mouse-hovered slice even when arrow keys had a different slice highlighted (verified live: selection B highlighted, A executed). The release decision now honors the interaction mode via a pure helper with tests.
  • Reorder commit crash - a rebuild landing during the 150 ms settle animation (settings edit, theme change) made CommitReorder throw InvalidOperationException on a stale slot lookup (verified in an STA test). It now falls back to the existing warning path.
  • Blank pie with no retry - the build clears the refresh-pending flag before its failure early-returns, and Stop rebuilding the pie on every open and build it before the first frame聽#73 removed the hide-time re-arm that used to self-heal it. Failure paths now keep the refresh pending.
  • System events off the UI thread - SystemEvents.UserPreferenceChanged can be raised on a worker thread and the handler reads dependency properties (cross-thread throw verified in an STA test; delivery was on the UI thread in all live probes). Handlers now marshal to the dispatcher.
  • Stale DPI after monitor moves - pixel snapping and the shadow BitmapCache scale are baked at build DPI and nothing re-triggered a rebuild on a DPI change (mechanism code-verified; not runtime-testable on a uniform-DPI machine). OnDpiChanged now requests a rebuild.

Verification

  • All fixes re-verified with the same harness protocols that confirmed the bugs: single execution per release, no phantom drag, flick follows keyboard selection, no crash on stale commit, coalescing of hidden refreshes unchanged (10 hidden events -> exactly 1 rebuild on reopen).
  • Pixel diff of the rendered pie interior vs main: 28 of 121,905 pixels differ within anti-aliasing tolerance (max delta 14) - no visual change.
  • dotnet test: 148/148 passing, including new cases for the release-trigger decision.

The audit also refuted five suspected defects (stale-hover flick, press-scale leak across opens, shadow-halo clipping from the #72 BitmapCache, reduced-motion reorder reentrancy, hidden-refresh coalescing) and found one new issue, filed separately as #74 (clicks fall through during the fade-in).

With KeepMenuOpenAfterSliceClick enabled, clicking a slice while holding
the activation hotkey left the release latch armed, so releasing the
hotkey executed the same slice a second time.
Since the pie stopped rebuilding on every open, a press-and-hold that
ended while the menu was hidden left the drag candidate armed. Moving
over that slice with the button held in the next open then started a
reorder drag measured against the stale press position, and a stale
active drag would silently disable the hotkey-release flick.
Releasing the activation hotkey triggered the mouse-hovered slice even
while arrow keys had a different slice highlighted, so the visual
selection and the executed action could disagree. The release decision
now lives in a pure helper that honors the interaction mode.
The reorder commit runs from the settle animation callback. A rebuild
landing during those 150 ms (settings edit, theme change, slice toggle)
replaces the visuals, and looking up the target slot with First threw
InvalidOperationException into the dispatcher. Fall back to the existing
warning path instead.
The build clears the pending flag up front, so its failure early-returns
(zero size, no enabled slices, layout failure) left a blank pie that a
later open would not repair now that opens no longer rebuild
unconditionally. Re-arm the flag on those paths so the next visibility
change retries.
SystemEvents.UserPreferenceChanged is raised on whichever thread owns
the broadcast window; the refresh path reads dependency properties and
would throw off the UI thread. MainWindow already guards its settings
handler the same way.
Pixel snapping and the shadow BitmapCache scale are captured at build
time, and a move to a different-DPI monitor changes neither the DIP size
nor any other rebuild trigger, so the ring rendered soft until an
unrelated event forced a rebuild.
@danielchalmers
danielchalmers merged commit 4dfe6d4 into main Aug 10, 2026
1 check passed
@danielchalmers
danielchalmers deleted the claude/pie-audit-fixes branch August 10, 2026 03:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant