docs: correct the sidebar spec, and plan its implementation

The spec claimed the math pane and the signal list were two lists of the same
channels; they are one. `append_math_signals_locked()` already puts every math
channel into `signals()` and nothing filters `sig.math`, so the merge is mostly
a deletion -- the list gains a `MATH` divider and a second menu, and the pane
goes. §2, §3 and §6 say so now.

The plan is seven tasks, each ending in a build, `make test-ui` and one short
headless probe read.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-10-14 10:48:21 +02:00
1 parent a519e13b67
commit e10a92fc52
2 files changed
+1470 -13

No files matched your search

@@ -78,6 +78,10 @@ that they are appended to a page's box instead of to a revealer.
The two deleted toggles leave `src/ui/header-bar.ui` with their handlers, so the bar is
declared with five controls and no id resolves to a button nothing listens to.
`update_side_visibility()` (`ui.cpp:2830-2850`) keeps its job — the whole sidebar is still the
split view's to show, still gated on a CONFIG having named a signal — minus the line that showed
`signal_toggle`, which no longer exists.
Everything else those handlers do is untouched: `tc.armed`, the epoch rule and the
`set_time_window()` call in `on_trigger_toggled()`, `sync_events_availability()`, the
Arm/Disarm label, and `rows->set_cursors_visible()` with its `reset_cursors()`.
@@ -88,28 +92,48 @@ quotes.
## 3. One channel list
`rebuild_signal_list()` (`ui_signals.cpp:139-301`) gains a tail: a `MATH` divider row carrying
the `+`, then one row per math channel, built with the same widgets as a signal row — tick,
colour chip, name, unit, menu.
**The duplication is literal, and the merge is mostly a deletion.**
`rebuild_signal_list()` is handed `model.signals()`, and `append_math_signals_locked()`
(`model.cpp:252-269`) has already appended one synthetic entry per math channel — *"they go
through the ordinary signal path on purpose: a derived channel is a channel"*, says its own
comment. Nothing in the list filters `sig.math`, so **every math channel is already a row in
the signal list**, with its tick, chip, name, unit and menu. The math pane listed the same
channels a second time.
What the list gains:
- a non-selectable `MATH` divider row before the first math row, carrying the `+` that opens
the node editor on a new channel;
- two items in a math row's menu.
| | signal row | math row |
| --- | --- | --- |
| tick | acquire (hidden in a file view) | enabled |
| colour chip | clickable while plotted | clickable while plotted |
| colour chip | clickable while plotted | unchanged |
| unit | from CONFIG | the channel's own |
| menu | Information, Calibration | Edit graph…, Delete |
| menu | Information, Default colour, Calibrate… | **Edit graph…**, Default colour, **Delete** |
No rename item: the node editor owns a math channel's name and unit already
(`src/ui/math-editor.ui:37-43`), so *Edit graph…* is where both are changed, and a second place
to change them would be a second answer to the same question.
Information and Calibrate… are a *streamed* signal's — calibration is applied at ingest — so a
math row carries neither. No rename item either: the node editor owns a math channel's name and
unit (`src/ui/math-editor.ui:37-43`), so *Edit graph…* is where both are changed, and a second
place to change them would be a second answer to the same question.
The colour chip and the tick need no special case: `ScopeModel::set_math_channels()` already
appends a synthetic `SignalInfo` with `math=true`, which is why measurements, export and the
plots never knew the difference.
Two things already work and must not be rebuilt: the tick is already the right switch —
`enable_locked()` (`model.cpp:626-639`) mirrors it into the `MathChannel`'s own `enabled` flag —
and the row's `GtkDragSource` already lets a math channel be dropped onto a plot row, which is
how `math1` reached row 0 in the session this work started from.
One defect to fix while merging: `App::math` is the copy the session file is written from, so a
tick on a math row must mirror `enabled` into it before `schedule_layout_save()`. Today the pane's
checkbox did that (`ui.cpp:2276`) and the list's tick did not, so the two controls disagreed
about what gets saved.
Deleted: `build_math_panel()` (`ui_math.cpp:1044`), `rebuild_math_pane()` (`ui.cpp:2251-2319`),
`src/ui/math-panel.ui` and its GResource entry, `App::math_revealer`, `App::math_toggle`. The
node editor dialog itself is untouched — only the button that opens it moves.
`src/ui/math-panel.ui` and its entry in `src/ui/udpscope-ui.gresource.xml`, `App::math_revealer`,
`App::math_list`, `App::math_toggle`, `App::measures_toggle`, `App::signal_revealer`, and
`App::signal_toggle` with `on_signals_toggled()` (`ui_signals.cpp:303-307`) and its overflow-menu
row (`ui.cpp:4452-4455`) — the list is a page now, so "show or hide the signal list" has nothing
to hide. The node editor dialog itself is untouched; only the button that opens it moves.
## 4. Persistence
@@ -148,6 +172,7 @@ Readings that change, and what each must say instead:
| `dbg_signal_revealed` (`ui_probe.cpp:90`) | the signal revealer's child is revealed | the `signals` page is current **and** its list is non-empty |
| `dbg_trigger_revealed` (`ui_probe.cpp:92`, `:2190`) | the trigger revealer's child is revealed | the `signals` page is current **and** the trigger body is sensitive |
| `dbg_measures_pane_w/h` | the pane's size while revealed | the pane's size while its page is current; 0 otherwise, and read as such |
| the panel-toggle reading (`ui_probe.cpp:104`) | both menu toggles are visible | the switcher is visible, which is the one control those two became |
One new probe, `UDPSCOPE_PROBE_SIDE_TAB_MS`: select the page that is not current, then dump the
page name, the measurement row count and the accumulator's sample count. The count must have