feat: a search's hits are marked on every row, and the panel says how many of them it is showing

This commit is contained in:
Martino Ferrari committed 2026-10-08 10:11:50 +02:00
1 parent 8ee4effb70
commit db81ab2700
3 files changed
+140

No files matched your search

+46
View File
@@ -31,6 +31,14 @@ constexpr plot::u32 kRulerBColor = 0x8FA8C8u;
/// mistaken for something the user placed.
constexpr plot::u32 kMeasureColor = 0x9BE8A0u;
constexpr plot::u32 kTriggerColor = 0xFF5B5Bu;
/// A found hit's colour: the trigger marker's claim ("the scope fired here") in a cooler hue,
/// because a hit is the same condition found in *history* and the two are on screen together --
/// the live marker's red must stay the one that means "this is the event the capture is about".
constexpr plot::u32 kSearchColor = 0x7FB2FFu;
/// The *selected* hit's colour: the one the arrows are standing on, and the one a pause was taken
/// around. Sixteen identical lines with no way to tell which of them the readout is quoting is a
/// picture that cannot be read, so the current hit is brighter than its neighbours.
constexpr plot::u32 kSearchCurrentColor = 0xE8D37Fu;
/// The smallest difference a `TickFormat::Time` label can *write* at the magnitude `t` -- what
/// the last digit of a label at that magnitude is worth. `format_time` writes three significant
@@ -202,6 +210,15 @@ void RowSet::install(Row& r, int /*row_index*/, plot::Plot& p) {
r.plot->set_cursor_stroke(r.measure_v[mi], kMeasureStrokeWidth,
plot::Vec2{kMeasureDashOn, kMeasureDashOff});
}
// The search hits' pool, parked, created for the same reason the measurement pool above is:
// `add_cursor()` only appends, so every mark a scan could ever want exists from the start.
for (int m = 0; m < kSearchMarks; ++m) {
const std::size_t mi = static_cast<std::size_t>(m);
r.search_v[mi] = r.plot->add_cursor(kParked, plot::Color::hex(kSearchColor));
r.plot->set_cursor_selectable(r.search_v[mi], false);
r.plot->set_cursor_stroke(r.search_v[mi], kMeasureStrokeWidth,
plot::Vec2{kMeasureDashOn, kMeasureDashOff});
}
r.trigger_marker = r.plot->add_cursor(kParked, plot::Color::hex(kTriggerColor));
// A marker, not an instrument: it must sit exactly on the edge's time, so its
// snap is Free and the controller never rounds it onto a sample.
@@ -544,6 +561,32 @@ void RowSet::set_measure_marks(const std::vector<MeasureMark>& marks) {
}
}
void RowSet::set_search_marks(const std::vector<double>& xs, int current) {
const int n = static_cast<int>(xs.size()) < kSearchMarks ? static_cast<int>(xs.size())
: kSearchMarks;
search_marks_placed_ = n;
for (int i = 0; i < kMaxRows; ++i) {
Row& r = row(i);
if (!r.plot) continue;
// Every row, not the hit's own row: a hit is an *instant on the shared x window*, so the
// mark lines up down the column the way the vertical cursors do. There is no "the hit's
// row" to pick -- the condition was found on the trigger's channel, which may not be
// plotted at all.
for (int m = 0; m < n; ++m) {
const std::size_t mi = static_cast<std::size_t>(m);
r.plot->set_cursor_x(r.search_v[mi], xs[mi], plot::CursorPlacement::Free);
// The colour is re-set every frame rather than only when the selection moves: the pool is
// re-ordered each frame as the view rolls, so slot `m` is not the same hit it was.
r.plot->set_cursor_color(r.search_v[mi],
plot::Color::hex(m == current ? kSearchCurrentColor
: kSearchColor));
}
for (int m = n; m < kSearchMarks; ++m) {
r.plot->set_cursor_x(r.search_v[static_cast<std::size_t>(m)], kParked);
}
}
}
bool RowSet::update_time_labels() {
Row& first = row(0);
if (!first.plot || !figure_) return false;
@@ -697,6 +740,9 @@ bool RowSet::readouts_locked() const noexcept {
for (const plot::u32 id : r.measure_v) {
if (r.plot->cursor(id).selectable) return false;
}
for (const plot::u32 id : r.search_v) {
if (r.plot->cursor(id).selectable) return false;
}
}
return true;
}