c01206fb8a
The ~30Hz CapturePreviewAsync PNG poll capped real cadence at ~20Hz (35–165ms full-HD encode+decode), so a 60fps widget still juddered at ~1/6 speed. Replaced polling with frame-driven capture of the composition controller's root visual — the mechanism WebView2CompositionControl and Flutter's webview_windows use (graphics_context.cc captures the root surface_ visual via CreateGraphicsCaptureItemFromVisual; reference: github.com/microsoft/Windows.UI.Composition.WinUI / flutter-internal webview_windows). Frames now arrive at the renderer's own pace; capture memory is epoch'd ring reuse + one crop-sized shared WriteableBitmap. New Services/WebCaptureFrameSource.cs owns GraphicsCaptureItem + free- threaded Direct3D11CaptureFramePool + session (Straight alpha readback, per-frame FindContentBounds → CropBounds). WebView2Manager reworked around per-session composition controllers + one UI-thread Compositor created via the CoreMessaging CreateDispatcherQueueController P/Invoke (the 19041 projection lacks CreateOnCurrentThread); internal seam ctor (Dispatcher, Func<string,IScreenCaptureSource>?) for hermetic tests. CaptureScheduler.cs deleted; the three SetCaptureInterval cadence hooks removed; InitWebView2() moved from MainWindow ctor to Loaded (a parent HWND must exist for the composition controller); the hidden WebViewHostPanel overlay deleted. TransparentBackgroundScript unchanged. Tests: WebView2ManagerTests reworked — 4 control-size + scheduler tests dropped, FindContentBounds tests moved to WebCaptureFrameSource, ONE integration test (Frames_PublishCroppedPreview_And_CoalesceToLatest_CarryingCropBounds) drives the seam with a FakeWebSource + background-STA DispatcherPump. Suite 293/293, 0 warnings. NOTE: composition path NOT yet verified on a device — the take is the next step. Web work committed locally only (no push per standing rule). Docs same-commit: ai.md Slice 14 + supersede marker on Slice 11, HANDOFF, MyMistakes (CoreMessaging DQ + namespace-landmine recipe), TASK 17, Controls/ViewModels/Services indexes.
62 lines
4.1 KiB
Markdown
62 lines
4.1 KiB
Markdown
# Controls — index
|
||
|
||
The phase-2 carved pieces of what used to be one monolithic `MainWindow.xaml`
|
||
(1,681 lines → a 123-line shell). Each is a `UserControl` in the `ytLive.Controls`
|
||
namespace, extracted one roll-back commit at a time (refactor-commit-12..18).
|
||
See [`schema.md`](../schema.md) for the memory-map conventions.
|
||
|
||
| File | Purpose |
|
||
|------|---------|
|
||
| `BottomBar.xaml(.cs)` | Footer (Row 3): gear menu (stop/mic/game-mute/quality dropdown), stream stats (bitrate/fps/dropped/duration/health), resolution dropdown |
|
||
| `HealthBanner.xaml(.cs)` | Row 1 status banner + the 7 value→visibility converters live in the app theme, not here |
|
||
| `TopBar.xaml(.cs)` | Row 0 stream controls: REC/ON-AIR pills+signs, elapsed timer, start/stop, account avatar/name (`RefreshAvatar()`) |
|
||
| `PreviewPane.xaml(.cs)` | Row 1 Col 1 center: the 1920×1080 `OverlayCanvas`/`SelectionOverlay`/`BrandFlashLayer`/`SocialBarElement`, text pull-out drawer, and the live-controls row (TRAX / game / mic meters + sliders + speakers + socials) |
|
||
| `SceneThumbnailStrip.xaml(.cs)` | Row 0 scene-picker strip (`ItemsControl` over `Scenes`), staged-red / live-green border highlight |
|
||
| `LeftPanel.xaml(.cs)` | Row 1 Col 0 left panel: edit mode → layers+properties; live → YouTube chat. Source add/edit/remove, list drag-to-reorder, snapshot revert-accept |
|
||
| `OverlayHost.xaml(.cs)` | Root-grid overlay dialogs: Settings / Report Bug / Feature Request / About (+ licenses + license-key entry). `BackToAbout_Click` lives here |
|
||
| `SceneThumbnailStrip.xaml.cs` | Code-behind for the strip above |
|
||
| `LeftPanel.xaml.cs` | Code-behind for the panel above |
|
||
|
||
## Window ↔ control contract
|
||
|
||
The shell (`MainWindow.xaml(.cs)`) wires up each control and routes the few
|
||
cross-cutting calls through it:
|
||
|
||
- Ctor finds each control by `x:Name` (`TopBar`, `PreviewPane`, `LeftPanel`)
|
||
AFTER `InitializeComponent`, **before** any method touches it (null-ref guard).
|
||
- `_previewPane.IsClickInsidePreview/Drawer` + `_leftPanel.IsClickInside` →
|
||
deselection guard in `Window_PreviewMouseLeftButtonDown`.
|
||
- `_previewPane.UpdateSelectionOverlay(bool)` + `_leftPanel.OnSelectionChanged(vm)`
|
||
→ selection/edit-mode routing from the VM's `SelectedElement` change.
|
||
- `_topBar.RefreshAvatar()` on `IsConnected`/`AccountAvatarUrl` change.
|
||
- Handlers in moved controls cast `(DataContext as MainViewModel)` — controls
|
||
inherit the window's DataContext; never pass the VM through a property.
|
||
|
||
## Gotchas learned the hard way
|
||
|
||
- **A UserControl cannot see `Window.Resources`** (StaticResource resolves by
|
||
element lookup, so it stops at the control boundary). The 7 converters
|
||
(`BoolToVis`, `InverseBoolToVis`, `NotNullToVis`, `NullToVis`, `HiddenBoolToVis`,
|
||
`EnumToBool`, `AllTrueToVis`) were promoted from `MainWindow.Resources` into
|
||
`Themes/Controls.xaml` (see [`Themes/index.md`](../Themes/index.md)).
|
||
- **Moved XAML can lose its namespace**: a block cut from MainWindow carries no
|
||
root xmlns, so any `models:`/`Helpers:` ref in it breaks the build with
|
||
`MC2000: Value cannot be null (key)`. Declare `xmlns:models` / `xmlns:Helpers`
|
||
on each control's root (LeftPanel hit this).
|
||
- **Name-looked-up host stubs must stay in the window**, not a control:
|
||
`WebViewHostPanel` (ctor `InitWebView2(WebViewHostPanel)`) and `ToastArea`
|
||
(`NotificationService.InArea("ToastArea")`) are resolved by name from the
|
||
window root — so `OverlayHost` deliberately leaves them as thin window-root
|
||
children (Commit 18). Since the 2026-09-14 composition-capture redesign only
|
||
`ToastArea` remains — `WebViewHostPanel` is deleted (web sources no longer
|
||
host a multiline XAML control; `InitWebView2()` runs from `MainWindow_Loaded`
|
||
with no panel argument).
|
||
- When cutting visual wrappers, don't drop the outer `Border`'s
|
||
background/glow/margin — Commit 15 initially lost the preview's outer border
|
||
and had to restore it same-commit.
|
||
|
||
Related: [`ViewModels/index.md`](../ViewModels/index.md) (MainViewModel the
|
||
controls bind to), [`Themes/index.md`](../Themes/index.md) (styles/converters),
|
||
[`Helpers/index.md`](../Helpers/index.md) (ViewModelBase, converters), the
|
||
`MainWindow.xaml` shell at repo root that hosts these.
|