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
+14
View File
@@ -2,6 +2,7 @@ using System.Diagnostics;
using System.IO;
using System.Windows.Input;
using ytLive.Helpers;
using ytLive.Models;
namespace ytLive.ViewModels;
@@ -75,6 +76,19 @@ public partial class MainViewModel : ViewModelBase
public bool IsAnyOverlayOpen => IsSettingsOpen || IsBugOpen || IsFeatureOpen || IsAboutOpen;
private ZoomShape _zoomShape = ZoomShape.Circle;
public ZoomShape ZoomShape
{
get => _zoomShape;
set
{
if (SetProperty(ref _zoomShape, value))
{
try { _layoutStore.UpsertSetting("ZoomShape", value.ToString()); } catch { }
}
}
}
public string OverlayTitle
{
get => _overlayTitle;