feat: switching a signal off takes its traces off every plot

The switch in the signal list means "do not acquire this", and until now a trace of it
stayed on its plot drawing nothing: a slot that holds one of the four a plot has, reads
`--`, and blocks the drop that would fill it with something live.

`on_signal_switch()` now clears every slot that holds that signal, by the **signal name**
the slot remembers -- never the channel index, which is what the enable change renumbers --
and says what it did: `'sine' is off: 1 trace left the plots`. The follow-ups (a relayout,
the panels' chips, the layout save) happen once rather than per slot.

Re-enabling deliberately does not put the traces back: the arrangement is the user's, and
guessing where they were is worse than leaving them off. Say the word if you want that
direction too.

[enable-all-probe] reads it end to end -- it enables every signal, assigns the channels
across the rows, then switches one off *through its own acquire tick*:

    [enable-all-probe] switched 'sine' off: traces_left=0 (want 0) notice=''sine' is off: 1 trace left the plots'

which is also why the notice shares the status line's warning styling from the previous
commit: the trace leaving the plots is something the user should see happen.
This commit is contained in:
Martino Ferrari committed 2026-10-12 22:12:24 +02:00
1 parent aaf544be1e
commit 8b3240cacb
6 files changed
+102 -1

No files matched your search

+40
View File
@@ -1864,6 +1864,46 @@ gboolean enable_all_probe(gpointer user) {
std::fprintf(stderr, "[enable-all-probe] %zu channel(s) enabled and assigned to plots\n",
a->model->channel_count());
std::fflush(stderr);
// Then switch one off through its own switch in the signal list: a disabled signal must leave the plots,
// not sit there drawing nothing in a slot a live one could take.
g_timeout_add(250, +[](gpointer u) -> gboolean {
App* app = static_cast<App*>(u);
if (app->model->channels().empty() || app->signal_list == nullptr) return G_SOURCE_REMOVE;
const std::size_t signal = app->model->channels().front().signal;
const std::string name = app->model->channels().front().name;
GtkWidget* tick = nullptr;
for (GtkWidget* row_widget = gtk_widget_get_first_child(app->signal_list);
row_widget != nullptr && tick == nullptr;
row_widget = gtk_widget_get_next_sibling(row_widget)) {
// The tick sits a box inside the row, which is the shape `signal_tick_visible()` walks too.
for (GtkWidget* box = gtk_widget_get_first_child(row_widget); box != nullptr && tick == nullptr;
box = gtk_widget_get_next_sibling(box)) {
for (GtkWidget* w = gtk_widget_get_first_child(box); w != nullptr;
w = gtk_widget_get_next_sibling(w)) {
if (!GTK_IS_CHECK_BUTTON(w)) continue;
if (GPOINTER_TO_SIZE(g_object_get_data(G_OBJECT(w), "udpscope-signal")) != signal) continue;
tick = w;
break;
}
}
}
if (tick == nullptr) {
std::fprintf(stderr, "[enable-all-probe] no acquire tick found for signal %zu\n", signal);
std::fflush(stderr);
return G_SOURCE_REMOVE;
}
gtk_check_button_set_active(GTK_CHECK_BUTTON(tick), FALSE);
int left = 0;
for (int r = 0; r < app->rows->row_count(); ++r) {
for (int s = 0; s < RowSet::kSlotsPerRow; ++s) {
if (app->rows->row(r).signal_name[static_cast<std::size_t>(s)] == name) ++left;
}
}
std::fprintf(stderr, "[enable-all-probe] switched '%s' off: traces_left=%d (want 0) notice='%s'\n",
name.c_str(), left, app->notice.c_str());
std::fflush(stderr);
return G_SOURCE_REMOVE;
}, a);
return G_SOURCE_REMOVE;
}