fix(plot,render,glfw): a hue-neutral crosshair, an em-dash placeholder, one layout per figure event, and a usable readout switch

This commit is contained in:
Martino Ferrari committed 2026-09-28 19:38:30 +02:00
1 parent 10d5129fbf
commit a14f333dbe
10 files changed
+175 -70

No files matched your search

+19 -4
View File
@@ -130,8 +130,11 @@ struct CrosshairRow {
f64 y = 0.0;
/// The trace's name, or the positional placeholder of an unnamed one.
std::string label;
/// The formatted value, written in the trace's y axis' own tick format (`Axis::format_value()`,
/// so a readout reads exactly like the labels beside it), and empty when `!valid`.
/// What the row's value column shows. For a `valid` row it is the formatted number, written in
/// the trace's y axis' own tick format (`Axis::format_value()`, so a readout reads exactly like
/// the labels beside it); for a row whose trace has **no sample at the crosshair's x** it is
/// `kCrosshairNoValue`, the em dash. The absence is therefore *legible* -- a blank cell reads as
/// "still loading" rather than as "nothing here" -- and nothing numeric is fabricated for it.
std::string value;
/// The trace's intersection with the crosshair, in framebuffer pixels: the pen the
/// renderer draws the marker at. Placed through the *trace's own* axis pair, so the
@@ -143,8 +146,10 @@ struct CrosshairRow {
/// The label's line box: its top-left is the pen the run is staged at and its height one
/// text line. Empty when `!listed`.
Rect label_box{};
/// The value's line box, one gutter (the legend's padding) after the label's. Empty when
/// `!listed` or `!valid`: a row with no value stages no run.
/// The value's line box, right-aligned on the panel's inner edge and one gutter's worth of
/// measurement away from the label's own box. Empty when `!listed`: a row that is not in the panel
/// stages neither run, while a listed row always stages something -- the number, or
/// `kCrosshairNoValue`.
Rect value_box{};
};
@@ -185,6 +190,16 @@ struct CrosshairLayout {
bool empty() const noexcept { return !active; }
};
/// What a listed readout row shows in its value column when its trace has **no sample at the
/// crosshair's x**: an em dash, not a blank.
///
/// The row is listed either way -- the trace *is* under the pointer, and a panel that grew and
/// shrank with the pointer would be worse to read than one that says "nothing here" -- so the
/// absence has to be *said*: a blank value cell reads as a value that has not arrived yet. The em
/// dash is in the embedded font's coverage (spec §2 covers U+2013/U+2014), it is not a number, and
/// it cannot be mistaken for one.
inline constexpr const char* kCrosshairNoValue = "\u2014";
/// The label an **unnamed** trace's readout row carries: `#` and the trace's position, e.g.
/// `#2`.
///
+6
View File
@@ -567,6 +567,12 @@ class Plot {
/// calls this when the pointer leaves the axes.
void clear_crosshair() noexcept { crosshair_.active = false; }
/// Switches the readout panel off -- or back on -- without moving the crosshair: the lines and
/// the per-trace markers stay, the panel is not measured and no row is listed. A host that wants
/// the pointer's column marked but not a panel over its data sets this once; it is deliberately
/// **not** part of `set_crosshair()`, so placing a hover cannot silently change it.
void set_crosshair_readout(bool on) noexcept { crosshair_.show_readout = on; }
/// The crosshair's state: where it is, which lines it draws and whether the readout is
/// on. Read-only -- `set_crosshair()` and `clear_crosshair()` are how it moves -- so the
/// coordinates it holds and the geometry `layout()` places from them cannot disagree.