Add a persisted global App Settings switch. Off keeps the existing per-severity lifetimes (errors remain sticky); on makes every alert non-expiring and closes it when its body is clicked. The NotificationService supplies the current setting to each toast request. StickyNotificationIntegrationTests covers persistence and both request modes. 618 tests green, clean build with 0 warnings. Reference: Notification.Wpf exposes a CloseOnClick request option: https://github.com/G-Stas/Notification.Wpf
8.4 KiB
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 for the memory-map conventions.
| File | Purpose |
|---|---|
BottomBar.xaml(.cs) |
Footer (Row 3): intentionally empty, height-preserving spacer; stream readouts and resolution picker moved to the top-left cell in the 2026-10-26 surface redesign |
HealthBanner.xaml(.cs) |
Row 1 status banner + the 7 value→visibility converters live in the app theme, not here |
TopBar.xaml(.cs) |
Row 0 session surface: brand + gear menu (left), explicit EDIT toggle beside the REC |
PreviewPane.xaml(.cs) |
Row 2 Col 1 center: 1920×1080 OverlayCanvas/SelectionOverlay/BrandFlashLayer/SocialBarElement, static TextOverlay preview image from the shared cached text raster, text drawer, outgoing-picture transition overlay, right-edge Stream Info/YPP/Socials/TEST tabs and drawers, and live controls (TRAX / game / mic meters + sliders + speakers). The list-selected draggable layer is temporarily raised in preview z-order and custom hit-testing; deselection restores the scene's unchanged order (SelectedLayerPreviewHitTestingTests). TASK 57: dragging/resizing a webcam in the vertical tier routes through MainViewModel.MoveWebcamInVerticalTier / ResizeWebcamInVerticalTier for the six-spot snap or Chat camera's two sizes. |
SceneThumbnailStrip.xaml(.cs) |
Top row over the preview column: five fixed-size scene cards with scene name, hotkey, staged PROGRAM badge, orange staged/live border |
LeftPanel.xaml(.cs) |
Row 2 Col 0 left panel: explicit EditingActive toggle on → Scene Layers+properties; off → YouTube chat, independent of stream status. Source add/edit/remove, list drag-to-reorder, snapshot revert-accept; TextOverlay properties edit multiline content, font type/family, size, color via the shared palette, emphasis, and horizontal/vertical alignment with a static-text notice; sub-options are indented under their headings. TASK 51: the webcam properties carry the Background section — RemoveBackgroundPill / BlurBackgroundPill (PillToggle, radio-exclusive via WebcamSceneConfig.RemoveBackground/BlurBackground) + BackgroundStrengthSlider (0..1, 5% snaps, shown only while an effect is on). TASK 57: the webcam properties carry a Geometry section (X/Y/W/H) — manual placement of the camera, bound to the live placement (the one the current tier shows), gated on IsWebcamSelected (not SelectedElement.IsWebcam — the TASK 51 test finds that panel by binding text and would match twice); the Shape button's Visibility is bound to CanToggleClipShape, so the circle option is NOT OFFERED in the vertical Chat layout. TASK 58: every drop-down, check box and field in the properties panel carries the theme's style (YtComboBox/YtCheckBox/YtTextBoxCompact) and paints no colour of its own — the guard is ytLive.Tests/PropertiesStyleTests.cs. TASK 59: the webcam's properties are a HIERARCHY — a Shape title owns the mirror switch and the shape choice (Rect |
OverlayHost.xaml(.cs) |
Root-grid overlays: App Settings / Report Bug / Feature Request / About; About includes the output-quality conversion copy and an explicit llamacasty.com link, while the licensing/legal panel remains in-app. App Settings groups recording, slide-out alert placement/stickiness, brand-logo, preview, transition, and staged/all-scene layout-default controls. BackToAbout_Click lives here |
SceneThumbnailStrip.xaml.cs |
Code-behind for the strip above |
LeftPanel.xaml.cs |
Code-behind for the panel above |
The Add Layer menu exposes one standalone recent-alert Ticker per scene. The
properties panel edits its independent Ticker settings; Stream Alerts retains
its separate current-event ticker controls. Outside edit mode, the left panel
offers Live Chat first and Logs second; Logs wraps and colors typed entries,
pins explicit Class 1/2 errors above the timeline until acknowledged, then
returns them to the timeline; it excludes earlier file contents.
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) AFTERInitializeComponent, before any method touches it (null-ref guard). _previewPane.IsClickInsidePreview/Drawer+_leftPanel.IsClickInside→ deselection guard inWindow_PreviewMouseLeftButtonDown._previewPane.UpdateSelectionOverlay(bool)+_leftPanel.OnSelectionChanged(vm)→ selection/edit-mode routing from the VM'sSelectedElementchange._topBar.RefreshAvatar()onIsConnected/AccountAvatarUrlchange.- 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 fromMainWindow.ResourcesintoThemes/Controls.xaml(seeThemes/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 withMC2000: Value cannot be null (key). Declarexmlns:models/xmlns:Helperson each control's root (LeftPanel hit this). - Name-looked-up host stubs must stay in the window, not a control:
WebViewHostPanel(ctorInitWebView2(WebViewHostPanel)) andToastArea(NotificationService.InArea("ToastArea")) are resolved by name from the window root — soOverlayHostdeliberately leaves them as thin window-root children (Commit 18). Since the 2026-09-14 composition-capture redesign onlyToastArearemains —WebViewHostPanelis deleted (web sources no longer host a multiline XAML control;InitWebView2()runs fromMainWindow_Loadedwith 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 (MainViewModel the
controls bind to), Themes/index.md (styles/converters),
Helpers/index.md (ViewModelBase, converters), the
MainWindow.xaml shell at repo root that hosts these.