Files
LlamaCasty/Controls/index.md
T
gramps 987fddac24 TASK 63: the border-colour picker IS the app's own drop-down
The creator (2026-10-21), on Live -> Layers -> webcam -> Properties:

  "why does the drop down under the web cam properties not show the same style for
   the border color drop down list box?"

  "one of the first things we did was define branding. We established look-and-feel
   standards. And yet you keep ignoring these and I'm wondering why"

WHY IT WAS MISSED - the answer the creator is owed.
The TASK 58 sweep had already walked the whole app for un-themed controls and the
border picker STILL was not themed, because the sweep looked for control TYPES
(ComboBox / CheckBox / TextBox) carrying no theme style - and the border picker was
NONE of those. It was a "..." Button whose Click handler built a ContextMenu of
swatches in code. It was never in the sweep's scope, and PropertiesStyleTests.cs
(a type-based guard) could not see it either. A picker that LOOKS like a drop-down
is invisible to a type-based audit; the guard has to ask "is this the app's widget?".

WHAT CHANGED
- The picker is now the app's YtComboBox - the widget the Display and Font pickers
  wear - bound to the palette (SelectedElement.BorderColorOptions) with its selection
  bound by VALUE (SelectedItem -> BorderColorEntry), because WPF's SelectedValue
  matching is ordinal and the palette mixes #FFFFFF and #e94560.
- The closed box shows the swatch + the colour's NAME; the rows show swatch + hex +
  name (the rows the creator approved in TASK 60). A ComboBox cannot do that with one
  ItemTemplate - WPF copies it into the read-only SelectionBoxItemTemplate - so
  Helpers/ClosedBoxTemplateSelector.cs picks by the ContentPresenter's templated
  parent: the ComboBox for the closed box, the ComboBoxItem for a row. Verified
  against WPF with a throwaway probe, not assumed.
- The swatch is painted by the THEME (Helpers/HexToBrushConverter.cs, registered as
  HexToBrush) - the hand-painted SolidColorBrush(Color.FromRgb(...)) in the old
  click handler was the exact anti-pattern the theme forbids. The swatch also carries
  a 1px #2a3a5e border now, so black and dark swatches are visible on the dark menu
  (the follow-up TASK 60 left open).
- The hex field stays (60px, editable): a colour outside the palette can still be
  typed, and the drop-down then carries that hex as a NAMELESS entry ("no name" is the
  honest answer). The palette's name moved out of TASK 62's TextBlock into the
  drop-down's closed box.
- The hand-built menu, its ContextMenu/MenuItem/SolidColorBrush code and
  WebcamBorderColorPicker_Click are gone.

MODEL: SceneElement.BorderColorName -> BorderColorEntry (the palette entry for the
current hex, with its name; setting it writes the hex back) + BorderColorOptions (the
palette plus the custom entry), both raised when BorderColor changes.
BorderColorPalette gains EntryFor(hex) and OptionsFor(hex); NameOf delegates.

Guards: ytLive.Tests/BorderColorPickerTests.cs (the ONE test for this change - the
model, the panel, and the REAL widget: with the app's theme style and the panel's own
templates the closed box names the colour and the rows show the hex AND the name).
The TASK 60 guard (BorderColorPaletteTests) was updated because the mechanism it
asserted - a code-built menu - no longer exists; it now guards the new wiring (the
drop-down is bound to the palette and neither the panel nor the code-behind carries a
second list of colours).

Full suite: 435/435 passed, 0 warnings.
2026-10-21 23:09:11 -07:00

7.0 KiB
Raw Blame History

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): stream stats (bitrate/fps/dropped/duration/health), resolution dropdown (gear menu moved up to TopBar 2026-09-22)
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 (2026-09-22 redesign): brand + gear menu (left), one segmented REC
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). TASK 57: dragging a webcam under the vertical tier routes through MainViewModel.MoveWebcamInVerticalTier (the six-spot snap inside the cropped band) instead of free-floating over the master, and a resize re-snaps the tile onto its spot (TASK 57 Unit 2: the resize routes through MainViewModel.ResizeWebcamInVerticalTier — the Chat camera's two sizes, then the snap)
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. 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 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).
  • 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 (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.