ui: signal pages honour the signal lock and disable the plot a signal is already on
The per-signal page bypassed the signals lock for the acquire tick and the math Edit/Delete buttons, and offered "Plot on r" for a plot the signal was already on. A removed page also let another signal's page show during the close animation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
baf4a1f20f
commit
3b1cda50e5
2 files changed
+60
-6
No files matched your search
@@ -2637,12 +2637,15 @@ left the caller's `false` alone would read `dock_shown=0 default=1 ok=0`. The pr
|
|||||||
## `[signalpage]`
|
## `[signalpage]`
|
||||||
|
|
||||||
```
|
```
|
||||||
[signalpage] name=sine open=1 pages=1 enable_visible=1 plot_buttons=1 plot_on_row0=1 removed=1 btn_row0=1 back_to_list=1 row_opens=1 pruned=1 rows=1
|
[signalpage] name=sine open=1 pages=1 enable_visible=1 plot_buttons=1 plot_on_row0=1 plot_on_row0_disabled=1 locked_enable_sensitive=0 removed=1 btn_row0=1 back_to_list=1 row_opens=1 pruned=1 rows=1
|
||||||
```
|
```
|
||||||
|
|
||||||
`UDPSCOPE_PROBE_SIGNAL_PAGES_MS` builds and drives the page of the first streamed signal. `open=1` (the slide-over shows `signal:<name>`), `pages=1`
|
`UDPSCOPE_PROBE_SIGNAL_PAGES_MS` builds and drives the page of the first streamed signal. `open=1` (the slide-over shows `signal:<name>`), `pages=1`
|
||||||
(one page in `App::signal_pages`), `enable_visible=1` (the acquire tick is shown in live mode; `0` in a file view), `plot_buttons=` equal to
|
(one page in `App::signal_pages`), `enable_visible=1` (the acquire tick is shown in live mode; `0` in a file view), `plot_buttons=` equal to
|
||||||
`rows=` (one "Plot on" button per figure row), `plot_on_row0=1` (`assign_signal_to_row()` returned true and the signal is plotted), `removed=1`
|
`rows=` (one "Plot on" button per figure row), `plot_on_row0=1` (`assign_signal_to_row()` returned true and the signal is plotted, from a state where the probe first took it off every plot), `plot_on_row0_disabled=1`
|
||||||
|
(after the page refilled with the signal on plot 1, that plot's "Plot on" button is insensitive, "already on this plot"; `0` is a button that would put the signal in a second slot of
|
||||||
|
the same plot), `locked_enable_sensitive=0` (with `signals_locked` set and `signal_pages_sync()` run, the page's Acquire/Compute tick is insensitive; `1` is a page that bypasses
|
||||||
|
the signal lock -- the same goes for a math channel's Edit graph and Delete, which the probe does not read; the probe restores the lock), `removed=1`
|
||||||
(the page's "Remove from plots" took it off), `btn_row0=1` (the "Plot on 1" button, the user's path, put it back), `back_to_list=1` ("All signals"
|
(the page's "Remove from plots" took it off), `btn_row0=1` (the "Plot on 1" button, the user's path, put it back), `back_to_list=1` ("All signals"
|
||||||
target), `row_opens=1` (activating the list row opens the page), and `pruned=1`: a math channel's page was opened, its **Delete** button clicked, and
|
target), `row_opens=1` (activating the list row opens the page), and `pruned=1`: a math channel's page was opened, its **Delete** button clicked, and
|
||||||
the slide-over closed and the page was dropped by `signal_pages_sync()`. `pruned=-1` means no math channel could be added; `0` is a stale page.
|
the slide-over closed and the page was dropped by `signal_pages_sync()`. `pruned=-1` means no math channel could be added; `0` is a stale page.
|
||||||
|
|||||||
+55
-4
@@ -96,6 +96,8 @@ void fill(App& app, const std::string& name, GtkWidget* page) {
|
|||||||
g_object_set_data(G_OBJECT(enable), "udpscope-signal", GSIZE_TO_POINTER(index));
|
g_object_set_data(G_OBJECT(enable), "udpscope-signal", GSIZE_TO_POINTER(index));
|
||||||
g_signal_connect(enable, "notify::active", G_CALLBACK(on_signal_switch), &app);
|
g_signal_connect(enable, "notify::active", G_CALLBACK(on_signal_switch), &app);
|
||||||
gtk_widget_set_visible(enable, sig.math || app.waveform_name.empty());
|
gtk_widget_set_visible(enable, sig.math || app.waveform_name.empty());
|
||||||
|
// The signal lock is the list's lock too: the page may not do what the locked list refuses.
|
||||||
|
gtk_widget_set_sensitive(enable, unlocked);
|
||||||
gtk_box_append(GTK_BOX(page), enable);
|
gtk_box_append(GTK_BOX(page), enable);
|
||||||
|
|
||||||
// Colour.
|
// Colour.
|
||||||
@@ -137,7 +139,13 @@ void fill(App& app, const std::string& name, GtkWidget* page) {
|
|||||||
GtkWidget* b = text_button(std::to_string(r + 1).c_str(), rows_box);
|
GtkWidget* b = text_button(std::to_string(r + 1).c_str(), rows_box);
|
||||||
tag(b, index);
|
tag(b, index);
|
||||||
g_object_set_data(G_OBJECT(b), "udpscope-row", GINT_TO_POINTER(r));
|
g_object_set_data(G_OBJECT(b), "udpscope-row", GINT_TO_POINTER(r));
|
||||||
gtk_widget_set_tooltip_text(b, "draw this signal on this plot");
|
// A button for a plot the signal is already on would add it to a second slot there.
|
||||||
|
bool on_this_row = false;
|
||||||
|
for (int slot = 0; slot < RowSet::kSlotsPerRow; ++slot) {
|
||||||
|
if (app.rows->row(r).signal_name[static_cast<std::size_t>(slot)] == sig.name) on_this_row = true;
|
||||||
|
}
|
||||||
|
gtk_widget_set_sensitive(b, !on_this_row);
|
||||||
|
gtk_widget_set_tooltip_text(b, on_this_row ? "already on this plot" : "draw this signal on this plot");
|
||||||
g_signal_connect(b, "clicked", G_CALLBACK(+[](GtkButton* b, gpointer user) {
|
g_signal_connect(b, "clicked", G_CALLBACK(+[](GtkButton* b, gpointer user) {
|
||||||
outlive_the_rebuild(GTK_WIDGET(b));
|
outlive_the_rebuild(GTK_WIDGET(b));
|
||||||
assign_signal_to_row(*static_cast<App*>(user), tagged(b),
|
assign_signal_to_row(*static_cast<App*>(user), tagged(b),
|
||||||
@@ -176,10 +184,12 @@ void fill(App& app, const std::string& name, GtkWidget* page) {
|
|||||||
GtkWidget* more = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
GtkWidget* more = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||||
if (sig.math) {
|
if (sig.math) {
|
||||||
GtkWidget* edit = text_button("Edit graph...", more);
|
GtkWidget* edit = text_button("Edit graph...", more);
|
||||||
|
gtk_widget_set_sensitive(edit, unlocked);
|
||||||
tag(edit, index);
|
tag(edit, index);
|
||||||
g_signal_connect(edit, "clicked", G_CALLBACK(on_math_edit_clicked), &app);
|
g_signal_connect(edit, "clicked", G_CALLBACK(on_math_edit_clicked), &app);
|
||||||
GtkWidget* del = text_button("Delete", more);
|
GtkWidget* del = text_button("Delete", more);
|
||||||
gtk_widget_add_css_class(del, "destructive-action");
|
gtk_widget_add_css_class(del, "destructive-action");
|
||||||
|
gtk_widget_set_sensitive(del, unlocked);
|
||||||
tag(del, index);
|
tag(del, index);
|
||||||
g_signal_connect(del, "clicked", G_CALLBACK(on_math_delete_clicked), &app);
|
g_signal_connect(del, "clicked", G_CALLBACK(on_math_delete_clicked), &app);
|
||||||
} else {
|
} else {
|
||||||
@@ -221,6 +231,11 @@ void signal_pages_sync(App& app) {
|
|||||||
for (auto it = app.signal_pages.begin(); it != app.signal_pages.end();) {
|
for (auto it = app.signal_pages.begin(); it != app.signal_pages.end();) {
|
||||||
if (signal_index(app, it->first) == std::string::npos) {
|
if (signal_index(app, it->first) == std::string::npos) {
|
||||||
if (slideover_page(app) == page_id(it->first)) slideover_close(app);
|
if (slideover_page(app) == page_id(it->first)) slideover_close(app);
|
||||||
|
// The stack keeps showing the dying page through the close animation; park it on the list so that
|
||||||
|
// removing the page does not let another signal's page take its place for those frames.
|
||||||
|
if (slideover_has_page(app, "signals")) {
|
||||||
|
gtk_stack_set_visible_child_name(GTK_STACK(app.slideover.stack), "signals");
|
||||||
|
}
|
||||||
slideover_remove_page(app, page_id(it->first));
|
slideover_remove_page(app, page_id(it->first));
|
||||||
it = app.signal_pages.erase(it);
|
it = app.signal_pages.erase(it);
|
||||||
} else {
|
} else {
|
||||||
@@ -295,12 +310,46 @@ gboolean signal_page_probe(gpointer user) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start from a signal that is on no plot (the layout may have it somewhere), through the page's own button.
|
||||||
|
auto page_it = a.signal_pages.find(name);
|
||||||
|
if (signal_plotted(a, name) && page_it != a.signal_pages.end()) {
|
||||||
|
GtkWidget* off0 = find_descendant(page_it->second, [](GtkWidget* w) {
|
||||||
|
return is_button_labelled(w, "Remove from plots");
|
||||||
|
});
|
||||||
|
if (off0 != nullptr) g_signal_emit_by_name(off0, "clicked");
|
||||||
|
}
|
||||||
|
|
||||||
// Plot on row 0 through the function the "Plot on" buttons call, then take it off through the page's own
|
// Plot on row 0 through the function the "Plot on" buttons call, then take it off through the page's own
|
||||||
// button (outlive_the_rebuild + remove_signal_from_plots + the page's refill).
|
// button (outlive_the_rebuild + remove_signal_from_plots + the page's refill).
|
||||||
const bool assigned = assign_signal_to_row(a, index, 0);
|
const bool assigned = assign_signal_to_row(a, index, 0);
|
||||||
const int plot_on_row0 = assigned && signal_plotted(a, name) ? 1 : 0;
|
const int plot_on_row0 = assigned && signal_plotted(a, name) ? 1 : 0;
|
||||||
|
|
||||||
|
// The refilled page disables the button of the plot the signal is now on.
|
||||||
|
int plot_on_row0_disabled = -1;
|
||||||
|
page_it = a.signal_pages.find(name);
|
||||||
|
GtkWidget* rows_box_on = page_it == a.signal_pages.end() ? nullptr : find_descendant(page_it->second, [](GtkWidget* w) {
|
||||||
|
return std::string(gtk_widget_get_name(w)) == "udpscope-plot-on";
|
||||||
|
});
|
||||||
|
if (rows_box_on != nullptr && gtk_widget_get_first_child(rows_box_on) != nullptr) {
|
||||||
|
plot_on_row0_disabled = gtk_widget_get_sensitive(gtk_widget_get_first_child(rows_box_on)) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The signal lock: with it on, the page's acquire tick is insensitive after the sync (the lock change
|
||||||
|
// re-runs it through rebuild_signal_panels); restored afterwards.
|
||||||
|
int locked_enable_sensitive = -1;
|
||||||
|
const bool was_locked = a.signals_locked;
|
||||||
|
a.signals_locked = true;
|
||||||
|
signal_pages_sync(a);
|
||||||
|
page_it = a.signal_pages.find(name);
|
||||||
|
if (page_it != a.signal_pages.end()) {
|
||||||
|
GtkWidget* enable_now = find_descendant(page_it->second, [](GtkWidget* w) { return GTK_IS_CHECK_BUTTON(w); });
|
||||||
|
if (enable_now != nullptr) locked_enable_sensitive = gtk_widget_get_sensitive(enable_now) ? 1 : 0;
|
||||||
|
}
|
||||||
|
a.signals_locked = was_locked;
|
||||||
|
signal_pages_sync(a);
|
||||||
|
|
||||||
int removed = -1;
|
int removed = -1;
|
||||||
auto page_it = a.signal_pages.find(name);
|
page_it = a.signal_pages.find(name);
|
||||||
GtkWidget* off = page_it == a.signal_pages.end()
|
GtkWidget* off = page_it == a.signal_pages.end()
|
||||||
? nullptr
|
? nullptr
|
||||||
: find_descendant(page_it->second, [](GtkWidget* w) {
|
: find_descendant(page_it->second, [](GtkWidget* w) {
|
||||||
@@ -370,9 +419,11 @@ gboolean signal_page_probe(gpointer user) {
|
|||||||
slideover_close(a);
|
slideover_close(a);
|
||||||
std::fprintf(stderr,
|
std::fprintf(stderr,
|
||||||
"[signalpage] name=%s open=%d pages=%d enable_visible=%d plot_buttons=%d plot_on_row0=%d "
|
"[signalpage] name=%s open=%d pages=%d enable_visible=%d plot_buttons=%d plot_on_row0=%d "
|
||||||
|
"plot_on_row0_disabled=%d locked_enable_sensitive=%d "
|
||||||
"removed=%d btn_row0=%d back_to_list=%d row_opens=%d pruned=%d rows=%d\n",
|
"removed=%d btn_row0=%d back_to_list=%d row_opens=%d pruned=%d rows=%d\n",
|
||||||
name.c_str(), open, pages, enable_visible, plot_buttons, plot_on_row0, removed, btn_row0,
|
name.c_str(), open, pages, enable_visible, plot_buttons, plot_on_row0, plot_on_row0_disabled,
|
||||||
back_to_list, row_opens, pruned, a.rows->row_count());
|
locked_enable_sensitive, removed, btn_row0, back_to_list, row_opens, pruned,
|
||||||
|
a.rows->row_count());
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user