feat: the locked trigger pane carries Arm/Disarm, and one function states both arm buttons
The compact line -- the pane's locked, "what is it set to" mode -- showed the arm state in words and gave
no way to change it, so arming from there meant unfolding the panel first, which is the thing the lock
exists to avoid. The control is built into the pane's *heading*, the row that stays on screen when the
body goes, and it is wired to the same `on_arm_clicked` the body's button uses, so both do one thing.
Its label, sensitivity and visibility are stated by `sync_arm_buttons()`, called from the four places any of
its inputs can move: the click, the mode toggle, the curator's per-tick re-read (a single-shot latch disarms
by itself) and `apply_pane_lock()`. The label comes from the producer's `trigger_armed()` atomic, the other
two from the toolbar toggle that owns `enabled`, so no caller needs the model lock. Visibility is
`enabled && locked`: with trigger mode off the line beside it already reads "stream (trigger off)" and there
is no engine to arm, and with the pane open the body's own button is the one on screen. Never both -- two
arm buttons would be two controls for one state.
Verified with the arm probe, which locks the pane the way a user does (the [edit] click):
compact arm with the trigger off: visible=0; with the trigger on and the pane locked:
visible=1 sensitive=1 width=51 label=Disarm
[udpscope] compact arm probe: locked=1 enabled=1 own=1 effective=1 width=51 panel_width=-1 label=Disarm
`panel_width=-1` is the "not both" assertion: the body's button is not on screen while the compact one is.
The width cost a defect to read. The control is created hidden and shown for the first time inside the
handler, so a same-tick `gtk_widget_get_width()` reads 0 while `gtk_widget_is_visible()` already says 1 --
and the body button's *stale* allocation (270 px, from when the body was still up) reads as a live one,
because `gtk_widget_get_visible()` is the widget's own flag and stays true under a hidden parent. The
reading is therefore taken one frame later, by a step of its own, and the control is compared with
`is_visible()`. Both defects are in the invariants, because the next control read this way will meet them.
0 Gtk-CRITICAL; clean build with the documented four warnings (the help resource re-measured); test-ui
14/14. Docs: the rule and both readings in `invariants-trigger-and-pinning.md`, the probe's entry in
`probe-readings.md`, and the user-facing sentence in `src/help/08-trigger.txt`.
This commit is contained in:
1 parent
908286ee89
commit
620473a5be
8 files changed
+160
-17
No files matched your search
+13
-5
@@ -3659,7 +3659,7 @@ gboolean tick(gpointer user) {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(app->trigger_toggle), trigger_on);
|
||||
}
|
||||
const bool armed = model.trigger_armed().load(std::memory_order_relaxed);
|
||||
gtk_button_set_label(GTK_BUTTON(app->arm_button), armed ? "Disarm" : "Arm");
|
||||
sync_arm_buttons(*app);
|
||||
if (new_capture) {
|
||||
// The edge has been accepted, which means the pre window was already full, so the
|
||||
// whole event window can be framed now and the post side filled in view.
|
||||
@@ -4442,6 +4442,9 @@ void apply_pane_lock(App& app) {
|
||||
};
|
||||
edit_button(app.trigger_edit, app.trigger_locked, "the trigger");
|
||||
edit_button(app.signals_edit, app.signals_locked, "the signals");
|
||||
// The lock decides whether the compact line's arm control is the one on screen: with the body shown the
|
||||
// body's own button is there, and two arm buttons at once would be two controls for one state.
|
||||
sync_arm_buttons(app);
|
||||
sync_trigger_summary(app);
|
||||
// The list, because the lock is what decides whether a row carries a tick, a menu and the `+`.
|
||||
if (app.signal_list != nullptr) rebuild_signal_panels(app);
|
||||
@@ -4898,8 +4901,9 @@ 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);
|
||||
// The arm controls are the one part of the trigger pane that needs trigger mode running: both of them,
|
||||
// and the compact line's visibility, are stated from the toggle by `sync_arm_buttons()`.
|
||||
sync_arm_buttons(*app);
|
||||
gtk_widget_set_sensitive(app->cursors_reset, FALSE);
|
||||
|
||||
if (app->side_stack != nullptr) {
|
||||
@@ -6014,10 +6018,14 @@ 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; arm sensitive with the trigger off=%d\n",
|
||||
"pane switches live while their panes are off=%d; arm sensitive with the trigger off=%d; "
|
||||
"compact arm with the trigger off: visible=%d; with the trigger on and the pane "
|
||||
"locked: visible=%d sensitive=%d width=%d label=%s\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_arm_sensitive);
|
||||
app.dbg_panel_switch_live, app.dbg_arm_sensitive, app.dbg_summary_arm_off,
|
||||
app.dbg_summary_arm_visible, app.dbg_summary_arm_sensitive, app.dbg_summary_arm_width,
|
||||
app.dbg_summary_arm_label.c_str());
|
||||
{
|
||||
// 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
|
||||
|
||||
Reference in new issue
Block a user