scope windows: the same instruments as the main window

A scope or detached window drew its traces and nothing else: no cursors, no gate band, no trigger
marker, no measurement marks. They are not per-window state to be recreated -- the cursors are one
pair of times, the gate one band, the trigger one instant -- so the main window's row set stays the
**owner** and every window is written from it.

`sync_scope_instruments()` runs in the main tick after the main's own `sync_cursors()`/
`sync_gate()`: a gesture inside a scope window is read back from *that* window's controllers first
(those two calls only adopt while their own plot holds the grab, which is exactly "the drag
happened here"), adopted by the owner, and then every window -- the dragged one included -- is
written from the owner with the same cursor pair, mode and visibility, gate bounds and visibility,
and trigger marker. `RowSet::set_cursors()` is the new pair setter; `trigger_marker_x()`/`_visible()`
are the getters the mirror reads. The measurement marks travel by signal name
(`RowSet::MeasureMark::signal`, filled by the measures pass), so each window finds the row that
plots the signal and draws its ruler or mark there -- or none, when it does not plot it. The search
marks are times on the window and are placed on every window by the same per-frame path, and the
crosshair-axis-labels preference is restated to open windows when it changes.

Probe: `[scope-probe] instruments windows=N cursors=N gate=N trigger=N marks=N (main trigger
on=..)`, after adding a real gated `Top` measurement on `sine` -- the gate is restated visible each
frame by `update_measures()` and marks come only from `show_on_plot` measurements, so the leg uses
the app's own path. Docs: README, the cursors invariant, and the probe readings.
This commit is contained in:
Martino Ferrari committed 2026-11-06 12:21:38 +01:00
1 parent a29f38eaa9
commit 91ca29d181
10 files changed
+211

No files matched your search

+14
View File
@@ -355,6 +355,16 @@ class RowSet {
void place_crossings();
CursorMode cursor_mode() const noexcept { return cursor_mode_; }
/// Writes both vertical cursors at once: the two-time pair is one instrument with one owner
/// (the main window's row set), and a scope window is written from it each frame -- reading it
/// back from that window's own plots is what lets a drag *there* move the same pair.
void set_cursors(double a, double b);
/// The trigger marker's position and whether it is up: what a scope window is written from, the
/// same way it is written from the cursor pair.
double trigger_marker_x() const noexcept { return trigger_marker_x_; }
bool trigger_marker_visible() const noexcept { return trigger_marker_on_; }
/// The two vertical cursors' positions, in the **display** x the axis reads -- not stream
/// seconds, which they coincided with only while `display_epoch()` was always 0. A live view
/// is newest-relative now, so a cursor left at -1.5 s keeps meaning "1.5 s ago" and the data
@@ -399,6 +409,10 @@ class RowSet {
/// its label stays empty.
struct MeasureMark {
int row = 0;
/// The signal the mark belongs to, so a *scope window* can find the row that plots it (its
/// row numbering is its own): a mark that never carried the name could only be placed in a
/// copy by index, which a window's own layout editor invalidates.
std::string signal;
bool level = false;
double value = 0.0;
plot::u32 color = 0;