feat: a signal's chip is clickable, so a trace's colour is the user's to choose

Clicking the coloured chip in the signal list opens a GtkColorDialog and the
colour it returns overrides that signal's palette entry. Held by name, like
every other identity here, and persisted as a session `[[color]]` table.

RowSet::signal_color() becomes the one resolver: install() builds the trace
through it and all three chip sites read it, because a chip exists to say which
trace a row is about and an override is exactly the state in which two routes to
a colour would disagree. A grey chip names no trace and so is not clickable, and
the way back is `Default colour` in the signal's menu -- a setting with no undo
is a trap.

The gesture sits on the existing label rather than in a GtkButton: padding on
this row is what made the signal list twice as tall once. It claims its sequence
so a click on the chip cannot become the start of a row drag.

UDPSCOPE_PROBE_COLOR_MS reads the result out of plot::Plot::trace() and out of
the chip's own markup, with an override in no palette slot, so neither leg can
pass by asking the resolver what it already said.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-10-07 08:22:32 +02:00
1 parent 8bc1fe8b5c
commit 4003084648
11 files changed
+406 -24

No files matched your search

+11
View File
@@ -104,3 +104,14 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
figure rebuild — which `assign()` already forces. A signal on no plot gets
`RowSet::unplotted_color()` (grey) rather than no chip, so the panel's rows stay the
same height.
- **`RowSet::signal_color()` is the only route to a trace's colour.** The user can override the
palette entry per signal (clicking its chip; `set_signal_color()` / `clear_signal_color()`,
keyed by **name**, because a palette index is positional and a CONFIG renumbers), and that
override is exactly the state in which two routes to a colour disagree. So `install()` builds
the trace through this call and all three chip sites — the signal panel, the measures pane and
the cursors pane — read it too; none of them may index `palette_color()` itself. An override
marks every row holding the signal dirty (`mark_signal_dirty()`, not `assign()`: what the slot
*holds* did not change) and the rebuild happens on the frame tick via `relayout_pending`,
because `relayout_areas()` is the one place that builds a figure and re-points the area at it.
`UDPSCOPE_PROBE_COLOR_MS` reads the result back out of `plot::Plot::trace()` and out of the
chip's own markup, since asking the resolver twice would assert nothing.