Files
LlamaCasty/TASKS/task-63-border-colour-app-dropdown.md
T

5.3 KiB
Raw Blame History

TASK 63 — The border-colour picker is the app's own drop-down

Status: ✅ Done — shipped 2026-10-21.

What it is

The creator (2026-10-21), on Live → Layers → webcam → Properties → Border Color:

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

and, with the reason:

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

So the border colour is picked with the app's own drop-down — the YtComboBox every other picker wears — instead of a ... button that built a ContextMenu in code.

Why it was missed (the root cause, recorded so it cannot recur)

The TASK 58 sweep looked for controls by type (ComboBox / CheckBox / TextBox) that carried no theme style. The border picker was none of those — it was a Button whose Click handler built a ContextMenu of swatches in code. So it was never in the sweep's scope, and ytLive.Tests/PropertiesStyleTests.cs could not see it either: the guard asserts control types, not the design system. A style sweep must ask "is this the app's widget?", not "is this control type themed?".

How it is done

  • The picker — a ComboBox with Style="{StaticResource YtComboBox}", bound to SelectedElement.BorderColorOptions (the palette) and SelectedItem to SelectedElement.BorderColorEntry (TwoWay). SelectedItem, not SelectedValue: WPF matches SelectedValue ordinally (case-sensitively) and the palette mixes #FFFFFF and #e94560; the model matches by VALUE, so the hex the creator typed in any case still lands on the right row.
  • Clicking a popup row stays inside LeftPanel (2026-10-22 fix): a ComboBox's open PART_Popup lives in its own PopupRoot visual tree (VisualTreeHelper.GetParent(popupRoot) is null), connected back to the Popup and ComboBox only via PopupRoot's logical parent (LogicalTreeHelper.GetParent(popupRoot)). LeftPanel.IsDescendantOf previously used child is Visual ? VisualTreeHelper.GetParent(child) : LogicalTreeHelper.GetParent(child), which returned null at PopupRoot because PopupRoot is a Visual. That caused MainWindow.Window_PreviewMouseLeftButtonDown to see IsClickInside == false on any click inside the open dropdown and clear SelectedElement = null — exiting the properties panel before the ComboBoxItem click could select the colour. IsDescendantOf now falls back to LogicalTreeHelper.GetParent and TemplatedParent whenever VisualTreeHelper.GetParent returns null.
  • 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 the template by the ContentPresenter's templated parent (the ComboBox for the closed box, the ComboBoxItem for a row).
  • 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 for the current colour moved out of a TextBlock (TASK 62's BorderColorName) and into the drop-down's closed box.
  • The swatch is painted by the theme, not by code: Helpers/HexToBrushConverter.cs (registered in Themes/Controls.xaml as HexToBrush) turns the hex into a brush, and the templates use the theme's border colour #2a3a5e so the black swatch is visible on the dark popup. The hand-built menu, its hand-painted SolidColorBrushes and WebcamBorderColorPicker_Click are gone.

The model

  • SceneElement.BorderColorName → BorderColorEntry (the palette entry for the current hex, with its name; setting it writes the hex back) and BorderColorOptions (the palette plus the custom entry). Both are raised when BorderColor changes.
  • BorderColorPalette.EntryFor(hex) (case-insensitive; a nameless entry for a colour outside the palette) and OptionsFor(hex) (the palette + that entry). NameOf now delegates to EntryFor.

Tests

ytLive.Tests/BorderColorPickerTests.cs — the ONE test for this change, in three parts: the model (the palette entry, the custom hex, the raises), the panel (the picker IS a YtComboBox over the palette, the hex field survives, the hand-rolled menu is gone from both the XAML and the code-behind) and the real widget — with the app's theme style and the panel's real templates, the closed box shows the name and the rows show the hex AND the name. ytLive.Tests/BorderColorPaletteTests.cs (TASK 60) was updated: its "the picker builds its menu from the palette" step 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).

Follow-ups (not done — out of scope)

  • The theme has no implicit Button style, so every plain <Button> in the app still renders with the light WPF system template (LeftPanel.xaml ×7, CameraPickerDialog.xaml ×2). The border picker's ... button was one of them; the rest are still there.
  • The palette's hexes keep their mixed casing (#FFFFFF vs #e94560).