ui: Arm is disabled with trigger mode off, and the redundant Go-live button is gone

**`Arm` is now disabled while trigger mode is off**, and labelled `Arm` again: it is the one control in
that pane that is *runtime* rather than setting, so with no engine running there is nothing to arm.
It is also the only thing in the body that stays disabled — the settings themselves stay editable, which
is the point of the previous commit. `arm sensitive with the trigger off=0` is the reading.

**`Go live (trigger off)` is deleted.** Its handler was `trigger_toggle → FALSE`, i.e. exactly the
toolbar's trigger switch and the pane's own switch, which is what it was for: "the way out of trigger
mode, from inside the panel that froze the view: it is the toolbar's trigger switch, so there is exactly
one piece of state." With that switch now in the pane's own heading, the button was the same control
drawn a third time. The status line that told users about it says "or switch the trigger off to stream"
instead. The events pane's own `events_live` button is a different button on a different path — the file
view restore — and is untouched.

**And one silent regression this work uncovered.** The `trigger panel:` frame reading had been walking the
*wrapper* box the app puts around the `.ui`'s panel (heading row + locked summary + panel) ever since the
heading moved out of the file in the lock commit, so it reported `boxes=2 … search_inside=0/4` against a
panel that was entirely correct. It reads `trigger_body` — the file's own panel — again, and now says so in
its comment, because a reading that can silently start describing the wrong widget is the failure it exists
to catch.

Verified: `trigger panel: boxes=15 controls_inside=15/15 search_widgets=4/4 search_inside=4/4` (the
documented shape, restored); `arm sensitive with the trigger off=0`; `trigger panel sensitive …=1`;
`panes:`/`panel switches:`/`pane edits:` unchanged; `make test-ui` 14 files; zero `Gtk-CRITICAL`.
This commit is contained in:
Martino Ferrari committed 2026-10-14 14:50:04 +02:00
1 parent 796dda4ad5
commit d8705a369f
6 files changed
+40 -36

No files matched your search

+25 -21
View File
@@ -4082,7 +4082,7 @@ gboolean tick(gpointer user) {
if (app->capturing) {
mode = "trigger: CAPTURING (post-trigger window filling)";
} else if (app->event_held) {
mode = "trigger: EVENT HELD (Arm to re-arm, or 'Go live' for streaming)";
mode = "trigger: EVENT HELD (Arm to re-arm, or switch the trigger off to stream)";
} else if (armed && have_trigger_value) {
static char waiting[192];
std::snprintf(waiting, sizeof(waiting),
@@ -4857,6 +4857,8 @@ void on_activate(GtkApplication* application, gpointer user) {
// switch that turns it back on. The trigger's body is deliberately *not* disabled: a trigger is
// configured before it is switched on, so its settings have to stay editable while it is off.
gtk_widget_set_sensitive(app->cursors_body, FALSE);
// The trigger's Arm button is the one control in its pane that needs trigger mode running.
if (app->arm_button != nullptr) gtk_widget_set_sensitive(app->arm_button, FALSE);
gtk_widget_set_sensitive(app->cursors_reset, FALSE);
if (app->side_stack != nullptr) {
@@ -5968,10 +5970,10 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
app.dbg_badge_frames[3]);
std::printf("[udpscope] trigger panel sensitive before the probe toggled it: %d; "
"signal list items=%d selected=%d; column visible after sidebar toggle off=%d; "
"pane switches live while their panes are off=%d\n",
"pane switches live while their panes are off=%d; arm sensitive with the trigger off=%d\n",
app.dbg_trigger_sensitive_default, app.dbg_trigger_items,
app.dbg_trigger_selected, app.dbg_side_column_after_hide,
app.dbg_panel_switch_live);
app.dbg_panel_switch_live, app.dbg_arm_sensitive);
{
// The panel's *frame* read back: the row boxes the `.ui` holds, and that every control the code built
// ended up inside the panel rather than in a row that is not there. A control is created in code and
@@ -5985,8 +5987,10 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
}
return 0;
};
// The *file's* panel (`trigger_body`), not the wrapper this app puts it in: the heading row and the
// locked summary are the code's arrangement, and what these counts read is what the `.ui` holds.
int boxes = 0;
for (GtkWidget* c = gtk_widget_get_first_child(app.trigger_panel); c != nullptr;
for (GtkWidget* c = gtk_widget_get_first_child(app.trigger_body); c != nullptr;
c = gtk_widget_get_next_sibling(c)) {
if (GTK_IS_BOX(c)) ++boxes;
}
@@ -5998,8 +6002,8 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
// a throwaway program, all three ways of setting it). So the walk the search widgets are read with has
// to start at the expander's child and not at the panel -- walking to the panel reads 0/4 on a panel
// that is entirely correct, which is what this line did first.
GtkWidget* search_root = app.trigger_panel;
for (GtkWidget* c = gtk_widget_get_first_child(app.trigger_panel); c != nullptr;
GtkWidget* search_root = app.trigger_body;
for (GtkWidget* c = gtk_widget_get_first_child(app.trigger_body); c != nullptr;
c = gtk_widget_get_next_sibling(c)) {
if (GTK_IS_EXPANDER(c)) {
if (GtkWidget* inner = gtk_expander_get_child(GTK_EXPANDER(c))) search_root = inner;
@@ -6013,21 +6017,21 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
"[udpscope] trigger panel: boxes=%d controls_inside=%d/15 search_widgets=%d/4 "
"search_inside=%d/4\n",
boxes,
inside(app.trigger_signal_drop, app.trigger_panel) +
inside(app.trigger_type_drop, app.trigger_panel) +
inside(app.trigger_edge_drop, app.trigger_panel) +
inside(app.trigger_cmp_drop, app.trigger_panel) +
inside(app.trigger_mode_drop, app.trigger_panel) +
inside(app.trigger_width_spin, app.trigger_panel) +
inside(app.trigger_width2_spin, app.trigger_panel) +
inside(app.trigger_level2_spin, app.trigger_panel) +
inside(app.trigger_timeout_spin, app.trigger_panel) +
inside(app.trigger_level_spin, app.trigger_panel) +
inside(app.trigger_pre_spin, app.trigger_panel) +
inside(app.trigger_holdoff_spin, app.trigger_panel) +
inside(app.trigger_band_spin, app.trigger_panel) +
inside(app.trigger_hf_spin, app.trigger_panel) +
inside(app.events_record_switch, app.trigger_panel),
inside(app.trigger_signal_drop, app.trigger_body) +
inside(app.trigger_type_drop, app.trigger_body) +
inside(app.trigger_edge_drop, app.trigger_body) +
inside(app.trigger_cmp_drop, app.trigger_body) +
inside(app.trigger_mode_drop, app.trigger_body) +
inside(app.trigger_width_spin, app.trigger_body) +
inside(app.trigger_width2_spin, app.trigger_body) +
inside(app.trigger_level2_spin, app.trigger_body) +
inside(app.trigger_timeout_spin, app.trigger_body) +
inside(app.trigger_level_spin, app.trigger_body) +
inside(app.trigger_pre_spin, app.trigger_body) +
inside(app.trigger_holdoff_spin, app.trigger_body) +
inside(app.trigger_band_spin, app.trigger_body) +
inside(app.trigger_hf_spin, app.trigger_body) +
inside(app.events_record_switch, app.trigger_body),
search_present,
inside(app.search_run_button, search_root) + inside(app.search_cancel_button, search_root) +
inside(app.search_prev_button, search_root) + inside(app.search_next_button, search_root));