scope windows: mirror a pin as a range, and restore the tick rebuild

**The blank scope window in trigger mode.** A held event pins the main window to
`[-pre, +post]`, and the tick mirrors that pin onto every scope window. It passed
`pinned_range().min/max` to `pin_event_window()`, which takes **spans** (`pre, post`): `min` is
signed, so `pre` came out negative and the group range became `[+pre, +post]` — an inverted x
window. The plotter then had nothing to draw: no trace, no x-axis labels, an empty plot that
looked like a broken window. `RowSet::pin_display_range(x0, x1)` states the pin as the range a
mirroring view actually has, and the tick uses it; live mode was never affected, which is why the
defect only showed under the trigger.

Also restores the scope-window `rebuild_if_dirty()` in the tick, which a reproduction experiment
had left commented out (`REPRO-TEMP`) and a later commit carried: a window opened empty and filled
by a drop kept the figure it was built with.

Probe: `[scope-probe] pin_mirror=1 main=[..] scope=[..] x=[..]` pins the main window, reads the
scope's pin and plot x range a frame later, and reads `0` for an inverted or mismatched pin.
Docs: probe readings and the trigger/pinning invariant (range vs spans).
This commit is contained in:
Martino Ferrari committed 2026-11-06 10:39:10 +01:00
1 parent 2279e2140a
commit ecdbf4f0e1
6 files changed
+75 -9

No files matched your search

@@ -629,3 +629,11 @@ samples. Three rules keep that honest:
its ink is not in the widget's paint tree, so `gtk_snapshot_free_to_node()` on a widget paintable of
it returns NULL and the "screenshot" would be an empty file. Drawing it in the store also keeps that
class GTK-free, which is what makes the cache testable without a window.
- **A pin is mirrored as a *range*, never as a pair of numbers that might be spans.** `Range`
(`pinned_range()`: `{-pre, +post}`) and the pin's *spans* (`pin_event_window(pre, post)`) are two
different things that both look like a pair, and mixing them inverts the window: a second view
that handed `pinned_range().min` to `pin_event_window()` pinned to `[+pre, +post]`, and the plot
drew nothing — no trace, no axis labels — because the x range was degenerate. Views that mirror a
pin use `pin_display_range(x0, x1)`, the range's own form. `[scope-probe] pin_mirror=1` reads the
two windows' pins and the scope's x range, and an inverted pin reads `0`.