Zoom: fix preview zoom box + shape-aspect anchoring (Unit: zoom box visuals)

Right-click in the live preview now procs a box that is actually visible and
populated with the zoomed region, and each Zoom View Shape sculpts the box per
spec. OBS-style derivative: magnifier box shows the composited region and dims
the surroundings (same pattern as OBS "zoom" / preview region highlights).

Root causes of the invisible box (two WPF traps, recorded in MyMistakes.md):
- Empty <VisualBrush Stretch="None"/> used as an OpacityMask paints nothing,
  so the mask alpha is 0 and the WHOLE box (border included) went transparent.
- ZoomBox lived inside CanvasGrid while its VisualBrush sampled CanvasGrid — a
  circular reference WPF renders as nothing. Hoisted ZoomBox to a sibling of
  CanvasGrid inside a new PreviewCanvas wrapper (same 1920x1080 space).

Shape semantics (MainViewModel.ActivateZoom):
- Circle: square, click point = center.
- VerticalRectangle (phone): tall 9:16, click point = top-left corner.
- HorizontalRectangle (tablet): wide 16:9, click point = top-left corner.
- Boxes stay fully inside 1920x1080 via Math.Clamp.

UI: ZoomRectOutline/ZoomCircleOutline shapes replace the square Border;
circle clips the box to an EllipseGeometry; brush Visual released when inactive.
Zoom View Shape dropdown now uses the standard YtComboBox style.

Test (Good Dog rule, one integration test): ZoomBoxInteractionTests verifies
aspect/anchoring/clamp per shape and the matching outline+clip in the real
MainWindow. Full suite 404 passed, 0 warnings.

Memory: HANDOFF.md zoom state updated; WPF VisualBrush traps appended to
MyMistakes.md.
This commit is contained in:
2026-10-07 20:46:45 -07:00
parent 6b6284d05c
commit 966e718f1e
13 changed files with 530 additions and 567 deletions
+19
View File
@@ -1475,3 +1475,22 @@ a cycle debugging a test failure, ask what the machine was doing: an audio test
gaming session is an environment fact, not a regression.** (Same family as the known
`LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder` interactive-desktop
flake — both are in the "run it in a quiet session" bucket, and neither is a code defect.)
## 2026-10-07 — WPF VisualBrush: two silent "invisible box" traps
Working on the zoom-magnifier box in PreviewPane. Two gold-plated WPF gotchas that made the box
render NOTHING (no border, no content):
1. **An empty `<VisualBrush Stretch="None"/>` used as an `OpacityMask` paints nothing → the mask
alpha is 0 → the WHOLE element (border included) becomes transparent.** A VisualBrush without a
`Visual` is not "mask = none", it is "mask = fully transparent". An opacity mask wants a tile or a
visual source; if you meant "no mask", OMIT the Grid.OpacityMask element entirely.
2. **A `VisualBrush` whose `Visual` is an *ancestor* of the element it is painted on is a circular
reference — WPF renders nothing (no exception, no content).** The zoom box lived inside CanvasGrid
and the brush sampled CanvasGrid. Fix: hoist the box OUT so it becomes a *sibling* of the visual
being sampled (wrap CanvasGrid + ZoomBox in a container Canvas, coordinates unchanged since both
are in the same 1920x1080 space). Then `VisualBrush.Visual = CanvasGrid` samples cleanly and the
box magnifies the already-composited preview.
Release the retained visual when the box hides (`VisualBrush.Visual = null`) or the long-lived
source visual stays pinned for the app lifetime.