TASK 60: the border-colour drop-down names every colour

The creator: "under Live->Layers->webcam->Properties->border color: in the drop
down box, the #hex values are good, but add a corresponding color name/label next
to the value."

Each row now reads "#e94560  Crimson" - the hex stays exactly as it was, the
colour's name goes next to it (dimmed #a0a0b0 so the hex reads first), and the
tooltip becomes "Crimson (#e94560)".

The 14 colours were a hard-coded string[] inside the click handler - a list of
bare strings cannot carry a name, and a list hidden in UI code cannot be asserted
on. They now live in Models/BorderColorPalette.cs: NamedColor(Hex, Name) +
BorderColorPalette.All (the single source of truth) + NameOf(hex), which answers
case-insensitively and falls back to the hex for a colour outside the palette.
The picker builds its menu from the catalogue and no longer knows any colour by
heart.

The names are chosen not to collide - the palette has two greens, two blues and
two warm colours: #00FF00 is Lime and #22c55e Emerald, #0000FF is Blue and
#3b82f6 Azure, #FF8800 is Orange and #f59e0b Amber.

Guard: ytLive.Tests/BorderColorPaletteTests.cs - every entry valid + named, no
hex or name repeated, the 14 legacy colours all still offered, NameOf answers,
and the old list is gone from the handler. 433/433 green, 0 warnings.
This commit is contained in:
2026-10-21 20:03:19 -07:00
parent 89f6aa3a67
commit 6271bf34c9
10 changed files with 231 additions and 16 deletions
+29
View File
@@ -2142,3 +2142,32 @@ holds the truth.
**Bonus:** the same "title owns its content, content is indented one level" rule applies
to any dense settings panel — one indent level per ownership level, two when a title is
itself nested (Strength under Blur under Background).
## 2026-10-21 — A LIST THE CREATOR MAY WANT TO EXTEND BELONGS IN A TESTABLE CATALOGUE (RECIPE — TASK 60)
The creator asked for one thing ("add a colour name next to the hex") and the code could not
even express it: the drop-down's colours were a `string[]` **inside the click handler**. A
list of bare strings cannot carry a name, and a list hidden in UI code cannot be asserted on
without reading the UI code.
**The recipe (catalogue → UI consumer):**
1. **Move the list out of the handler into a plain data type** — `NamedColor(Hex, Name)` +
`static IReadOnlyList<NamedColor> All` in `Models/`. Models = "plain data types, no logic
beyond what a property can carry" (see `Models/index.md`).
2. **Give it a lookup** (`NameOf(hex)`, case-insensitive) that answers for a value the user
typed, not only for the palette's own entries — the fallback returns the value itself, so
the UI never shows an empty name.
3. **The UI becomes a consumer**: the handler iterates the catalogue and builds the rows. The
handler must not know any colour by heart.
4. **Guard it in the test**: every entry valid + named, no hex or name repeated, the colours
that used to be offered are still offered (a name is added, never a colour dropped), the
lookup answers, and **the old list is gone from the handler** (a source-level assertion:
`Assert.False(source.Contains("\"#FFFFFF\""))`).
5. **Name the colours so they do not collide** — the palette had two greens, two blues and two
warm colours, so `#00FF00` is **Lime** and `#22c55e` **Emerald**, `#0000FF` **Blue** and
`#3b82f6` **Azure**, `#FF8800` **Orange** and `#f59e0b` **Amber**. A name that repeats
another's meaning is worse than no name.
**Why this matters beyond names:** the next request ("add a dark mode", "let me type a
custom colour", "reorder them") is now a change in ONE file instead of a change inside a
click handler.