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:
1 parent
8ee4effb70
commit
db81ab2700
3 files changed
+140
No files matched your search
+74
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -165,6 +166,9 @@ struct App {
|
||||
GtkWidget* search_cancel_button = nullptr;
|
||||
GtkWidget* search_status_label = nullptr;
|
||||
GtkWidget* search_pos_label = nullptr;
|
||||
/// "16 of 38 marks shown" -- the pool is smaller than a scan's result, and a display that is
|
||||
/// showing a sixth of what it found has to say so.
|
||||
GtkWidget* search_marks_label = nullptr;
|
||||
GtkWidget* search_prev_button = nullptr;
|
||||
GtkWidget* search_next_button = nullptr;
|
||||
GtkWidget* menu_button = nullptr;
|
||||
@@ -3341,6 +3345,62 @@ void place_measure_marks(App& app) {
|
||||
app.rows->set_measure_marks(app.measure_marks_display);
|
||||
}
|
||||
|
||||
/// Puts the search hits nearest the view on the plots, converting each from the stream seconds
|
||||
/// `BufferSearch` records to the x the axis is reading this frame -- the same conversion
|
||||
/// `place_measure_marks()` makes, and for the same reason. Called every frame: that is what
|
||||
/// makes a mark read-only and what keeps it on its own sample as a live view rolls.
|
||||
///
|
||||
/// Only the hits **inside the visible window** are candidates, nearest the view's centre first, so
|
||||
/// that a zoomed-out view with more hits than marks shows the middle of the cluster rather than an
|
||||
/// arbitrary end of it. The selected hit is placed first regardless, so stepping to a hit never
|
||||
/// leaves it unmarked, and it is the one drawn in the current colour.
|
||||
void place_search_marks(App& app) {
|
||||
std::vector<double> xs;
|
||||
int current = -1;
|
||||
std::size_t in_view = 0;
|
||||
const std::vector<double>& hits = app.search.hits();
|
||||
if (!hits.empty()) {
|
||||
const double s0 = app.model->stream_at_x0();
|
||||
const plot::Range view = app.rows->view_range();
|
||||
const double centre = 0.5 * (view.min + view.max);
|
||||
const int sel = app.search.cursor();
|
||||
|
||||
// Candidates: the display x of every hit in view, paired with its distance from the centre.
|
||||
std::vector<std::pair<double, double>> cand;
|
||||
for (std::size_t i = 0; i < hits.size(); ++i) {
|
||||
const double x = hits[i] - s0;
|
||||
if (x < view.min || x > view.max) continue;
|
||||
++in_view;
|
||||
if (sel >= 0 && i == static_cast<std::size_t>(sel)) continue; // placed first, below
|
||||
cand.push_back({std::fabs(x - centre), x});
|
||||
}
|
||||
std::sort(cand.begin(), cand.end());
|
||||
|
||||
const std::size_t cap = static_cast<std::size_t>(RowSet::kSearchMarks);
|
||||
xs.reserve(cap);
|
||||
if (sel >= 0 && sel < static_cast<int>(hits.size())) {
|
||||
current = 0;
|
||||
xs.push_back(hits[static_cast<std::size_t>(sel)] - s0);
|
||||
}
|
||||
for (std::size_t i = 0; i < cand.size() && xs.size() < cap; ++i) xs.push_back(cand[i].second);
|
||||
}
|
||||
if (app.search_marks_label != nullptr) {
|
||||
char text[128];
|
||||
if (hits.empty()) {
|
||||
std::snprintf(text, sizeof(text), " ");
|
||||
} else if (in_view > xs.size()) {
|
||||
// A truncated display says so: "16 of 38" with no qualifier reads as "there are 38 and you
|
||||
// are seeing 16 of them *everywhere*", which is not the claim.
|
||||
std::snprintf(text, sizeof(text), "%zu of %zu marks shown \xE2\x80\x94 zoom in to see the rest",
|
||||
xs.size(), in_view);
|
||||
} else {
|
||||
std::snprintf(text, sizeof(text), "%zu of %zu in view", xs.size(), hits.size());
|
||||
}
|
||||
gtk_label_set_text(GTK_LABEL(app.search_marks_label), text);
|
||||
}
|
||||
app.rows->set_search_marks(xs, current);
|
||||
}
|
||||
|
||||
/// Recomputes every measurement over the visible window and writes the results into the pane,
|
||||
/// collecting the marks the ones asking to be shown need.
|
||||
///
|
||||
@@ -6128,6 +6188,9 @@ gboolean tick(gpointer user) {
|
||||
app->rows->sync_cursors(cursor_drag_row);
|
||||
update_cursors_pane(*app);
|
||||
update_measures(*app, false);
|
||||
// Every frame, whether or not a scan is running: the marks are in stream seconds and the axis
|
||||
// is newest-relative, so a live window that has rolled since the last frame needs them moved.
|
||||
place_search_marks(*app);
|
||||
|
||||
// The first CONFIG names the signals, which is what the persisted layout is
|
||||
// written against: apply it once, here, and never again (a later CONFIG only
|
||||
@@ -7026,6 +7089,11 @@ GtkWidget* build_trigger_panel(App& app) {
|
||||
gtk_box_append(GTK_BOX(snav), app.search_next_button);
|
||||
gtk_box_append(GTK_BOX(sbox), snav);
|
||||
|
||||
app.search_marks_label = gtk_label_new(" ");
|
||||
gtk_widget_set_halign(app.search_marks_label, GTK_ALIGN_START);
|
||||
gtk_widget_add_css_class(app.search_marks_label, "dim-label");
|
||||
gtk_box_append(GTK_BOX(sbox), app.search_marks_label);
|
||||
|
||||
gtk_expander_set_child(GTK_EXPANDER(search_exp), sbox);
|
||||
gtk_box_append(GTK_BOX(box), search_exp);
|
||||
|
||||
@@ -10967,6 +11035,12 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
|
||||
rows.measure_marks_placed(), app.measure_labels.size(),
|
||||
app.dbg_measures_pane_w, app.dbg_measures_pane_h);
|
||||
}
|
||||
// `locked` is re-reported here rather than only in the readouts line: this task added a
|
||||
// whole new cursor pool, and a pool created selectable is exactly the regression that a
|
||||
// readout-vs-instrument check is for.
|
||||
std::printf("[udpscope] search marks: placed=%d hits=%zu locked=%d\n",
|
||||
rows.search_marks_placed(), app.search.hits().size(),
|
||||
rows.readouts_locked() ? 1 : 0);
|
||||
std::printf("[udpscope] header: window_button_w=%d window_label_w=%d (bar_w=%d)\n",
|
||||
app.dbg_win_btn_w, app.dbg_win_label_w, app.dbg_bar_w);
|
||||
std::printf("[udpscope] layout: rows=%d window_s=%.6g", rows.row_count(),
|
||||
|
||||
Reference in new issue
Block a user