Files
udpscope/help/C/org.udpscope.app/cursors.page
Martino Ferrari bdf0ff7ddc scope: full-screen figures, second windows, gated measurements, per-configuration events
A batch of view and instrument work, with the probes and docs for each.

- **Full screen per figure row**: a toggle on each row's top-right corner, revealed by
  pointer proximity (`kMaximizeRevealPx` around its placed rect, per row, read from an
  overlay-level motion controller so a revealed button under the pointer cannot flicker);
  the plot menu offers the same as *Full screen this figure* / *Restore the layout*. The
  arrangement is saved and restored through `RowState`, so nothing plotted moves. A layout
  of one figure carries no toggle.
- **Second window**: `open_scope_window()` copies a figure (cells, width shares, signals in
  their colours) or opens an empty one, with its own `RowSet` and `PlotArea`; the main
  window's tick binds, acquires and repaints it, mirroring the time window and pin. Entries:
  the plot menu's *Open this plot in a new window* and the overflow menu's *New scope window*.
- **Layout editor from the plot**: the plot menu gains *Edit the layout...* / *Done editing
  the layout*, flipping the app's one toggle.
- **Measurements gate**: `MeasureOptions::gate` restricts a measurement to the held/pause
  snapshot (live falls back to the live ring), persisted with the session and shown in the
  measure dialog.
- **Crosshair axis badges**: a preference (`crosshair_axis_labels`, on by default) and the
  plotter's badges where the lines meet the axes, in the axes' own formats; the plot menu
  and the probe read them.
- **Event cache per configuration**: `EventStore` is keyed `<source>/<configuration>/`,
  `retarget_event_store()` follows every configuration switch, and a pre-configuration cache
  (loose files) is adopted into the configuration's folder on first start.
- **GTK4 lifecycle**: the main window's handlers move from the non-existent `"destroy"`
  signal to `close-request`; scope windows are closed with it and the application quits
  explicitly (the exit run's hang). The exit probe closes with `gtk_window_close()`.
- Probes: `[maximize-probe]` (single-figure rule and the corner reveal), `[scope-probe]`,
  `[split-probe]` cell crosshair leg, `[prefs-probe]` store keying, `[configs]` store key,
  `[rowmenu]` layout-editor entry; docs, help pages, README and AGENT.md updated.

Also fixes a probe-only use-after-free: `toggle_maximize_figure_row` replaces the figure
object, so the area must be re-pointed (the button's path does; the probe's raw call did not).
2026-11-05 17:58:59 +01:00

101 lines
5.6 KiB
Plaintext

<page xmlns="http://projectmallard.org/1.0/"
type="topic" style="task"
id="cursors">
<info>
<link type="guide" xref="index#analysis"/>
<link type="seealso" xref="measurements"/>
<desc>The shared vertical cursor pair and the per-row horizontal rulers, their four modes,
and how a reading is formatted.</desc>
<credit type="author copyright">
<name>Martino Ferrari</name>
</credit>
</info>
<title>Cursors</title>
<p>Cursors read values off the traces. There are two kinds: one <em>shared vertical
pair</em> that every row carries at the same time, and <em>horizontal rulers</em>, one pair
per row, that belong to that row alone.</p>
<section id="reach">
<title>How to reach it</title>
<steps>
<item>
<p>Press the cursors button in the header bar, or <key>c</key>. It switches the cursors on
and off.</p>
</item>
<item>
<p>The readings are in the dock (a read-only copy) and on the <gui>Cursors</gui> page, which
<gui>Edit</gui> beside <gui>Cursors</gui> opens over the plot and which owns the mode
and a <gui>Reset</gui> button. Drag a marker to move it.</p>
</item>
<item>
<p>The mode dropdown offers four modes: <gui>vertical</gui> (the shared time pair),
<gui>horizontal</gui> (the per-row rulers), <gui>both</gui>, and <gui>mix</gui>.</p>
</item>
</steps>
</section>
<section id="knowing">
<title>Worth knowing</title>
<list>
<item><p>The vertical pair is <em>one</em> time for the whole figure, so moving it on
any row moves it on all of them. That is what makes <code>dt</code> meaningful across
rows.</p></item>
<item><p>A ruler belongs to its row and nothing shares it, so two rows' rulers are
independent. The plots of a split row are one row here: they share their y axis, so the
rulers are at the same values in each.</p></item>
<item><p><gui>mix</gui> is the reading mode: one line per <em>bound</em> trace, with the
trace's own values at each marker and their difference. A slot with a name but no
channel behind it is not a trace and gets no line.</p></item>
<item><p>A value is the <em>nearest stored sample's</em>, never a number between two
samples. Zoom in past the sample spacing and the line between two samples is something
the plot drew, not data — so a reading off it would be the drawing. The hover crosshair
reads the same way.</p></item>
<item><p>Every reading carries a <em>metric prefix</em> so the digits sit in
<code>1..999</code>: <code>10.52 mV</code>, not <code>0.01 V</code>. Two decimals is all
an instrument landing on an <code>f32</code> sample can claim, so without the prefix a
small value would read <code>0.00</code> and the reading would be gone. A stream that
declares no unit gets the prefix anyway, written tight against the number
(<code>585.24m</code>) — the space separates the number from its unit, and a spaced
<code>m</code> with nothing after it would read as metres.</p></item>
<item><p>A marker past the newest sample reads <code>--</code>. With only one marker
over data the row reads <code>partial</code>: a difference between a number and nothing
is not a number.</p></item>
<item><p>The hover crosshair also writes its two coordinates where the eye already
looks: the value where its horizontal line meets the y axis, and the time where its
vertical line meets the x axis — each in a small box among that axis' own tick labels,
in the axis' own format. Each box is centred on the line it names, and its background
covers what is behind it, so a tick label underneath never prints through the number.
The preference's <gui>Crosshair axis labels</gui> turns the pair off.</p></item>
<item><p>In a split row the horizontal line runs across <em>every</em> plot of the row,
not just the one under the pointer: the plots share one y range, so it is one value
along the whole row. It stops at the row's edges — another row has a scale of its
own.</p></item>
<item><p>In <gui>mix</gui> each marker also places a crossing annotation per trace,
which is what lets you see on the plot which sample a number came from. It is <em>on
that sample</em>, so when the marker falls between two it sits a little off its own
line: the line is where you put the marker, the dot is the datum being quoted. The hover
crosshair's mark snaps the same way, and is drawn larger than a trace's sample dots so
it does not read as one more sample.</p></item>
<item><p>The time row exists only in the modes that show the vertical pair.
<gui>horizontal</gui> has no time row, by design.</p></item>
<item><p>A cursor <em>stays where you put it</em> while a live view rolls under it,
which is what a bench scope does: its <code>T0</code> is an age, so a marker left at
<code>-1.5s</code> keeps reading the sample a second and a half old as the stream
advances. A measurement's marks do the opposite — they travel with the sample they
measured — because a cursor is a question you asked and a mark is an answer about one
particular sample.</p></item>
</list>
</section>
<section id="example">
<title>Example</title>
<p>Three traces in <gui>mix</gui> mode with both markers over data: 3 lines and 6 crossing
marks, each line reading like <code>sine 0.31 V -0.84 V -1.15 V</code>. Move the second
marker past the newest sample and the third column reads <code>--</code>.</p>
</section>
</page>