badges: hide the Source badge when the source has no panel of its own

A Source pill that opens nothing (UDPS, Flat UDP, files) was noise. It is now shown only while the slide-over has a source page; the [badges] source line gains visible=.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-11-03 02:38:12 +01:00
1 parent d8f82bd610
commit 616cde5b13
8 files changed
+41 -24

No files matched your search

+1 -1
View File
@@ -160,7 +160,7 @@ struct BadgeBar {
GtkWidget* signals_slot = nullptr; ///< left; empty in step 1, step 2 fills it
GtkWidget* system_slot = nullptr; ///< right: Trigger, Source
Badge trigger;
Badge source; ///< opens the first source page; a read-out when the source has none
Badge source; ///< opens the first source page; hidden when the source has none
GtkWidget* signals_row = nullptr; ///< the box inside the scroller: signal badges, then the "+"
/// One per shown enabled signal; empty when overflowed. `unique_ptr`: each button's "clicked" handler holds
/// a pointer to its `Badge`, which must not move when the vector does.
+17 -5
View File
@@ -173,13 +173,16 @@ void build_badge_bar(App& app) {
bb.signals_slot = app.badge_signals;
bb.system_slot = app.badge_system;
make_badge(app, bb.trigger, bb.system_slot, "Trigger", "trigger");
// The first source page is where the badge goes; a source with none (UDPS) has nothing to open, so its
// badge is a read-out: not a target, not focusable, but still on screen.
// The first source page is where the badge goes; a source with none (UDPS, Flat UDP, a file) has nothing to
// open, so its badge is hidden (`sync_badge_bar` keeps it in step with the slide-over's source pages) and, for
// as long as it is, neither a target nor focusable. Hidden, not insensitive: a dead "Source" pill that does
// nothing was only noise. The bar's height is fixed, so the row loses width and never height.
const std::string first = app.source_panels.empty() ? std::string() : app.source_panels.front().name;
make_badge(app, bb.source, bb.system_slot, "Source", first);
if (first.empty()) {
gtk_widget_set_can_target(bb.source.button, FALSE);
gtk_widget_set_focusable(bb.source.button, FALSE);
gtk_widget_set_visible(bb.source.button, FALSE);
}
// The link state is the status bar's to say; the badge names the source and opens its pages. The empty
// state label is hidden, as the "+" badge's is, rather than leaving a gap in the pill.
@@ -278,6 +281,14 @@ void sync_badge_bar(App& app) {
set_open(sb->button, slideover_page(app) == sb->page);
}
if (bb.source.button == nullptr) return;
// Shown only while there is a source page to open. The slide-over's page list is the truth, so a page that is
// registered later brings the badge back, and its focus and pick with it.
const bool has_source_pages = !app.slideover.source_pages.empty();
if ((gtk_widget_get_visible(bb.source.button) != FALSE) != has_source_pages) {
gtk_widget_set_visible(bb.source.button, has_source_pages);
gtk_widget_set_can_target(bb.source.button, has_source_pages);
gtk_widget_set_focusable(bb.source.button, has_source_pages);
}
// The source's name; the link state is the status bar's. A file has no kind worth naming.
const bool file_mode = app.replaying || !app.model->config().open_waveform.empty();
std::string name = "Source";
@@ -322,7 +333,8 @@ gboolean badges_probe(gpointer user) {
bad, gtk_label_get_text(GTK_LABEL(a->badges.trigger.state)), h_closed, h_after,
h_closed == h_after && h_closed >= 36 ? 1 : 0, marked_open ? 1 : 0, page_open ? 1 : 0,
marked_closed ? 1 : 0);
// The Source badge: its read-out, and the segmented switch of a source with several panels.
// The Source badge: shown only with a source page (`visible=`; hidden, the page-dependent fields read -1 or 0),
// and the segmented switch of a source with several panels.
const std::string first = a->badges.source.page;
bool switch_visible = false;
int second_ok = -1; // -1: there is no second page to select (one source page, or none)
@@ -348,11 +360,11 @@ gboolean badges_probe(gpointer user) {
}
slideover_close(*a);
std::fprintf(stderr,
"[badges] source name=%s pages=%zu switch_visible=%d can_target=%d second_page=%s "
"[badges] source name=%s pages=%zu visible=%d switch_visible=%d can_target=%d second_page=%s "
"second_ok=%d open_marked=%d closed_unmarked=%d panel_min_w=%d\n",
gtk_label_get_text(GTK_LABEL(a->badges.source.name)),
a->slideover.source_pages.size(),
switch_visible ? 1 : 0, gtk_widget_get_can_target(a->badges.source.button) ? 1 : 0,
gtk_widget_get_visible(a->badges.source.button) ? 1 : 0, switch_visible ? 1 : 0, gtk_widget_get_can_target(a->badges.source.button) ? 1 : 0,
second_page.empty() ? "-" : second_page.c_str(), second_ok,
first.empty() ? -1 : marked ? 1 : 0,
gtk_widget_has_css_class(a->badges.source.button, "udpscope-badge-open") ? 0 : 1, widest);
+3 -3
View File
@@ -446,9 +446,9 @@ constexpr TourStep kTourSteps[] = {
&App::area_overlay},
{"badges", nullptr, "The badges",
"Setup is reached by the thing it configures. A badge per signal opens that signal's colour, "
"calibration and plot; the + opens the full list; Trigger and Source open their own forms. Drag a "
"signal badge onto a plot to draw it there, or onto another badge to reorder. Click again, press "
"Esc or click the plot to close the panel.",
"calibration and plot; the + opens the full list; Trigger opens its form, and so does Source when "
"the source has controls of its own. Drag a signal badge onto a plot to draw it there, or onto "
"another badge to reorder. Click again, press Esc or click the plot to close the panel.",
&App::badge_bar},
{"trigger", "trigger", "The trigger",
"Describe an event -- a signal, a condition, a level and the window around it -- and arm it: "