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

83 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `SolidColorBrush`es 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`).