From 279eb08deaebf6f24e453538e77c3d0fadd4fcb9 Mon Sep 17 00:00:00 2001 From: gramps Date: Sun, 1 Nov 2026 08:10:04 -0800 Subject: [PATCH] feat(preview): right-click is the menu, zoom is the single-handed gesture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creator ruling 2026-11-01, and the reason is live-stream ergonomics: no two-handed chords while a broadcast or recording is running. - Edit mode: left selects / drags / resizes, right opens the context menu, Ctrl+right toggles the zoom box. Chords are acceptable here because editing is a stopped activity. - Preview mode: left toggles the zoom box, right opens the context menu, and Ctrl+right is swallowed outright — handled rather than left to fall through, so Ctrl+right cannot open a menu by accident while one hand is on the mouse. This also removes the old live behaviour where right-click activated the magnifier at the cursor point (and dismissed it). Zoom in preview is now a plain toggle; if point-zoom is wanted back it needs a one-button gesture, not a chord. Build: Release 0 warnings. Debug is currently blocked by the running app holding the exe (PID 47252) — close it and the Debug build copies normally. --- Controls/PreviewPane.xaml.cs | 40 +++++++++++++++++++----------------- docs/ai/ui-surface.md | 1 + 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Controls/PreviewPane.xaml.cs b/Controls/PreviewPane.xaml.cs index a9cceb4..e37ce85 100644 --- a/Controls/PreviewPane.xaml.cs +++ b/Controls/PreviewPane.xaml.cs @@ -451,7 +451,17 @@ public partial class PreviewPane : UserControl private void Preview_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var vm = DataContext as MainViewModel; - if (vm is null || !vm.IsEditMode) return; + if (vm is null) return; + + // Preview mode (EDIT off): the left button drives the zoom box — the common gesture in + // the mode where nothing is editable. Dragging it afterwards is handled by MouseMove. + if (!vm.IsEditMode) + { + vm.ToggleZoom(); + e.Handled = true; + return; + } + var grid = (Grid)sender; var toCanvas = CanvasGrid.TransformToVisual(grid).Inverse; var p = toCanvas.Transform(e.GetPosition(grid)); @@ -576,29 +586,21 @@ public partial class PreviewPane : UserControl private void Preview_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { - // Ctrl + right-click opens the element / backdrop context menu — sources and windows can - // be changed at ANY time, live or not (creator ruling 2026-10-30). Plain right-click stays - // with the zoom box, which is why the menu needed its own chord. - // Returning without Handled lets WPF's ContextMenuService open the menu under the cursor. - if ((Keyboard.Modifiers & ModifierKeys.Control) != 0) return; - + // Right-click = the context menu, in both modes (creator ruling 2026-11-01). + // Ctrl+right-click = the zoom box, EDIT MODE ONLY: the creator asked for no two-handed + // chords while live or recording, so the preview swallows Ctrl+right-click instead of + // letting it fall through to the menu. Returning without Handled is what lets WPF's + // ContextMenuService open the menu under the cursor. var vm = DataContext as MainViewModel; - if (vm == null) return; - var grid = (Grid)sender; - var toCanvas = CanvasGrid.TransformToVisual(grid).Inverse; - var p = toCanvas.Transform(e.GetPosition(grid)); - if ((vm.IsLive || vm.IsRecording) && !vm.IsEditMode) + if (vm is null) return; + if ((Keyboard.Modifiers & ModifierKeys.Control) == 0) return; + + if (!vm.IsEditMode) { - if (vm.IsZoomActive) - { - vm.DismissZoom(); - e.Handled = true; - return; - } - vm.ActivateZoom(p.X, p.Y); e.Handled = true; return; } + vm.ToggleZoom(); e.Handled = true; } private void Preview_MouseRightButtonUp(object sender, MouseButtonEventArgs e) diff --git a/docs/ai/ui-surface.md b/docs/ai/ui-surface.md index 0200022..105982a 100644 --- a/docs/ai/ui-surface.md +++ b/docs/ai/ui-surface.md @@ -104,6 +104,7 @@ The app is a **director's control surface**, not an editor. This replaces the OB - **Left panel:** `EditingActive` is an explicit `EDIT` toggle, not a mode inferred from `StreamStatus`. On = scene layers + properties; off = YouTube chat. Go Live turns editing off and stopping restores it; recording does not automatically switch it off. The staged scene is the preview/edit target; `LiveScene` is the program output. - **Top bar:** the `EDIT` toggle sits beside the REC/ON-AIR session controls; the brand and account surfaces remain at the sides. Its **right edge aligns with the layers panel below** (`TopBar` column 0 is a fixed **352** = work-area margin 8 + 360px column − the bar's own 16px padding), so the button and the panel share an edge. The **settings gear moved to the preview rail** (2026-11-01): first tab, orange (`Brush.Attention`), gear icon only — left-click opens the app menu (Settings / Bug / Feature / About), right-click goes straight to App Settings. +- **Preview gestures (2026-11-01):** *edit mode* — left selects/drags/resizes, right opens the context menu, **Ctrl+right toggles the zoom box**. *Preview mode* — **left toggles the zoom box**, right opens the context menu; **Ctrl+right is swallowed** (no chord while live or recording — one hand, always). Menus exist on the webcam layer and the backdrop; every other layer right-clicks through to the backdrop menu. Left-dragging with the zoom box open moves it, in both modes. - **Preview undo/redo (2026-11-01):** `Ctrl+Z` / `Ctrl+Shift+Z`, window-level bindings, live only while `EditingActive` — undo with the preview showing program output would rewrite a scene nobody is looking at. `Models/PreviewHistory` snapshots element **references** plus geometry, visibility, order and membership, so move / resize / reorder / add / remove / hide all undo in one step per gesture (`PushPreviewUndo` is called once at drag or resize start, never inside the move loop). Property edits (colour, opacity, border, font) are **not** captured yet — stated in the type's doc comment so nobody assumes otherwise. The stack clears on layout load. Guard: `ytLive.Tests/PreviewHistoryTests.cs`. - **Developer affordances are compiled out of release, not hidden.** Any control that must never ship (today: the all-resolutions override) lives in its own `UserControl` whose `Page` and `Compile` items are removed by `ytLive.csproj` when `$(Configuration) != 'Debug'`, and is added to its host at runtime inside `#if DEBUG` (`OverlayHost`). A collapsed row still exists in the visual tree of every shipped copy; "not compiled" is the only claim worth making. Label such rows with `Brush.Attention` (orange) while they are visible in Debug. Guard: `ytLive.Tests/OutputProfileTests.cs` asserts the build rule, and `Helpers/DevBuild.cs` is the single source for "is this a dev build". - **Scene strip:** five fixed-size cards above the preview show the scene name and hotkey; the staged scene carries a `PROGRAM` badge, and staged/live borders use orange. Click stages a scene while idle; while streaming or recording it changes program output and starts the selected transition.