signal page: the header names it, numbered; no duplicate title or back button

The slide-over's header already prints the open page's title beside its Close button, so the
signal page's body carried the name twice and a "Back to signals" button that only repeated
navigation the header owns. Both are gone.

The header's title is now the **numbered** name -- `<n>. <name>`, `measure_channel_number()`'s
number, the same one the signal badges and the measurements pane use -- so the panel says
*which acquired channel* it is and not only what it is called. `slideover_set_title()`
retitles a page in place (the open one included): the number follows the stream's channel
numbering, and the page is refilled whenever the model's signal list changes.

Probe: `[signalpage]` reads `title='1. sine' title_ok=1` (the header text against the numbered
name) and `no_dup=1` (no "Back to signals" button and no `title-3` label in the body); the old
`back_to_list` reading is gone with the button. Help, README and the probe doc follow.
This commit is contained in:
Martino Ferrari committed 2026-11-05 18:08:50 +01:00
1 parent bdf0ff7ddc
commit 706d247b58
7 files changed
+76 -22

No files matched your search

+3
View File
@@ -2161,6 +2161,9 @@ gboolean dock_session_probe(gpointer user); // UDPSCOPE_PROBE_DOCK_SESSION_MS
/// per setup surface, each page in a scroller of its own.
void build_slideover(App& app); // once, after the plot_overlay exists
bool slideover_add_page(App& app, const std::string& id, const std::string& title, GtkWidget* content);
/// Retitles a page in place (`slideover_add_page`'s title, for a page whose subject changed: a signal
/// page carries its channel's number, and the stream can renumber). False when the id is unknown.
bool slideover_set_title(App& app, const std::string& id, const std::string& title);
bool slideover_has_page(const App& app, const std::string& id);
/// The inverse of `slideover_add_page`: false when the id is unknown or is the open page.
bool slideover_remove_page(App& app, const std::string& id);
+33 -17
View File
@@ -194,20 +194,14 @@ void fill(App& app, const std::string& name, GtkWidget* page) {
const bool enabled = app.model->signal_enabled(index);
const bool plotted = signal_plotted(app, sig.name);
// Back to the list: a page reached from a list row has no other way home.
GtkWidget* back = text_button("Back to signals", page);
gtk_widget_set_halign(back, GTK_ALIGN_START);
gtk_widget_add_css_class(back, "flat");
g_signal_connect(back, "clicked", G_CALLBACK(+[](GtkButton*, gpointer user) {
slideover_open(*static_cast<App*>(user), "signals");
}),
&app);
GtkWidget* title = gtk_label_new(sig.name.c_str());
gtk_widget_add_css_class(title, "title-3");
gtk_widget_set_halign(title, GTK_ALIGN_START);
gtk_label_set_ellipsize(GTK_LABEL(title), PANGO_ELLIPSIZE_END);
gtk_box_append(GTK_BOX(page), title);
// The page's name is the panel header's, not the body's: the slide-over already prints it above the
// content (with its **Close** button), so a second copy in the body is the same word twice. The
// header's title carries the channel's number -- the same one the badge and the measurements pane
// use -- so the panel says which acquired channel this is, not just what it is called.
const int channel_number = measure_channel_number(app, sig.name);
slideover_set_title(app, page_id(name),
channel_number > 0 ? std::to_string(channel_number) + ". " + sig.name
: sig.name);
if (sig.math) {
if (!sig.unit.empty()) {
GtkWidget* unit = gtk_label_new(sig.unit.c_str());
@@ -489,8 +483,29 @@ gboolean signal_page_probe(gpointer user) {
: find_descendant(page_it->second, [](GtkWidget* w) { return is_button_labelled(w, "Remove from plots"); });
if (off != nullptr) g_signal_emit_by_name(off, "clicked");
}
// The page's *header* names it -- numbered like the badge -- and the body carries no second copy of
// the name and no "Back to signals" button: the header's Close is the way out, the badge bar and the
// list the ways in. Read before switching to the list.
const int channel_number = measure_channel_number(a, name);
const std::string want_title =
channel_number > 0 ? std::to_string(channel_number) + ". " + name : name;
// Copied: the label's text is replaced when the probe switches pages below, and a `const char*` into
// it would be read after the free (it printed as an empty title).
const std::string shown_title =
a.slideover.title != nullptr ? gtk_label_get_text(GTK_LABEL(a.slideover.title)) : "";
const int title_ok = a.slideover.page == ("signal:" + name) && want_title == shown_title ? 1 : 0;
// ...and the body really has lost the two duplicates: no "Back to signals" button (the header's
// Close is the way out) and no second copy of the name (a `title-3` label; the header carries it).
GtkWidget* body = page_it == a.signal_pages.end() ? nullptr : page_it->second;
const int no_dup =
body != nullptr &&
find_descendant(body, [](GtkWidget* w) { return is_button_labelled(w, "Back to signals"); }) ==
nullptr &&
find_descendant(body, [](GtkWidget* w) { return gtk_widget_has_css_class(w, "title-3"); }) ==
nullptr
? 1
: 0;
slideover_open(a, "signals");
const int back_to_list = slideover_page(a) == "signals" ? 1 : 0;
// Activating the signal's row in the list opens its page (the path a click takes). The list proposes what
// can be added, so the signal has a row only while it is not acquired: switch it off for the leg and back
@@ -660,9 +675,10 @@ gboolean signal_page_probe(gpointer user) {
std::fprintf(stderr,
"[signalpage] name=%s open=%d pages=%d enable_visible=%d plot_buttons=%d plot_on_row0=%d "
"plot_on_row0_disabled=%d "
"removed=%d btn_row0=%d back_to_list=%d row_opens=%d pruned=%d other_page_kept=%d rows=%d\n",
"removed=%d btn_row0=%d title='%s' title_ok=%d no_dup=%d row_opens=%d pruned=%d "
"other_page_kept=%d rows=%d\n",
name.c_str(), open, pages, enable_visible, plot_buttons, plot_on_row0, plot_on_row0_disabled,
removed, btn_row0, back_to_list, row_opens, pruned,
removed, btn_row0, shown_title.c_str(), title_ok, no_dup, row_opens, pruned,
other_page_kept, a.rows->row_count());
return G_SOURCE_REMOVE;
}
+17
View File
@@ -118,6 +118,23 @@ bool slideover_add_page(App& app, const std::string& id, const std::string& titl
return true;
}
bool slideover_set_title(App& app, const std::string& id, const std::string& title) {
SlideOver& s = app.slideover;
if (s.stack == nullptr || title.empty()) return false;
GtkWidget* child = gtk_stack_get_child_by_name(GTK_STACK(s.stack), id.c_str());
if (child == nullptr) return false;
GtkStackPage* page = gtk_stack_get_page(GTK_STACK(s.stack), child);
gtk_stack_page_set_title(page, title.c_str());
if (is_source_page(id)) {
AdwToggle* t = adw_toggle_group_get_toggle_by_name(ADW_TOGGLE_GROUP(s.source_switch), id.c_str());
if (t != nullptr) adw_toggle_set_label(t, title.c_str());
}
// The header shows the *open* page's title, so a retitle of the page on show is visible at once --
// a signal page's channel number can change under it when the stream renumbers its channels.
if (s.page == id) gtk_label_set_text(GTK_LABEL(s.title), title.c_str());
return true;
}
bool slideover_has_page(const App& app, const std::string& id) {
return app.slideover.stack != nullptr &&
gtk_stack_get_child_by_name(GTK_STACK(app.slideover.stack), id.c_str()) != nullptr;