fix: the [edit] buttons' icons, and the locked pane says four lines not one

Two things the lock shipped wrong.

**The "done" icon did not exist.** `emblem-ok-symbolic` is a breeze icon; Adwaita — which is what this
app runs under — does not have it, so the button that leaves configuration mode drew a missing-image
placeholder, i.e. nothing. Both names are now Adwaita's own: `document-edit-symbolic` (the pencil: edit)
and `object-select-symbolic` (the check: done). This is exactly the silent failure `toolbar icons:`
exists for, so the two `[edit]` buttons get the same treatment: `pane edits: locked=…(ok) config=…(ok)`
is read by clicking into configuration mode and back, so one line carries both names and
`gtk_icon_theme_has_icon()`'s verdict for each. The invariants now carry the rule, including the trap
that a name found under `breeze` is not an answer.

**The locked pane was too compact.** One line could not hold what a user opens the pane for, so it is now
a small definition list: `signal`, `condition`, `level`, `mode` on four lines, and after that only the
figures that are *not* at their defaults — `noise band`, `HF reject`, `pre-trigger`, `hold-off` — because
a line reading "band 0" is a line that says nothing. The channel is resolved from the acquired channels
and the condition quotes the figures its own shape reads (a width and its comparison, two levels, or a
timeout), never one it ignores. The pane is ~192 px tall locked against 580 in configuration mode.

One self-inflicted critical on the way: the probe read the summary with `gtk_label_get_text()`, and the
container is a grid now — `Gtk-CRITICAL: assertion 'GTK_IS_LABEL (self)' failed`. The text comes from the
sync that writes it instead.

Verified: `pane edits: locked=document-edit-symbolic(ok) config=object-select-symbolic(ok)`;
`panes: trigger=locked signals=locked body_visible=0 summary='signal: square | condition: window, falling
| level: 0.3 .. 0.6 | mode: single | noise band: 0.2 | pre-trigger: 20 % | hold-off: 1000 ms'`;
`make test-ui` 14 files; zero `Gtk-CRITICAL` and no missing icon.
This commit is contained in:
Martino Ferrari committed 2026-10-14 14:37:41 +02:00
1 parent 121abbefae
commit 4f4851f4e3
7 files changed
+142 -41

No files matched your search

+82 -29
View File
@@ -152,6 +152,27 @@ void write_trigger_widgets(App& app) {
/// rebuilt when a session loads.
void sync_trigger_summary(App& app) {
if (app.trigger_summary == nullptr) return;
int row = 0;
std::string reading; // the same content as one line, for the dump
const auto put = [&](const char* key, const std::string& value) {
if (row >= App::kTriggerSummaryRows) return;
gtk_label_set_text(GTK_LABEL(app.trigger_summary_keys[row]), key);
gtk_label_set_text(GTK_LABEL(app.trigger_summary_values[row]), value.c_str());
gtk_widget_set_visible(app.trigger_summary_keys[row], TRUE);
gtk_widget_set_visible(app.trigger_summary_values[row], TRUE);
reading += std::string(reading.empty() ? "" : " | ") + key + ": " + value;
++row;
};
const auto ms = [](double seconds) {
char b[64];
std::snprintf(b, sizeof(b), "%.6g ms", seconds * 1000.0);
return std::string(b);
};
const auto number = [](double v) {
char b[64];
std::snprintf(b, sizeof(b), "%.6g", v);
return std::string(b);
};
TriggerConfig tc;
std::string channel;
{
@@ -162,42 +183,52 @@ void sync_trigger_summary(App& app) {
channel = channels[static_cast<std::size_t>(tc.channel)].name;
}
}
char buf[192];
std::string text = trigger_type_text(tc.condition.type);
text += " · ";
text += channel.empty() ? "no channel" : channel;
put("signal", channel.empty() ? std::string("none acquired") : channel);
// The condition: the shape, and whatever qualifies it. A pulse width carries its comparison and its
// width, which is what the shape *is*; a window carries its two levels on the line below.
std::string condition = trigger_type_text(tc.condition.type);
switch (tc.condition.type) {
case TriggerType::PulseWidth:
std::snprintf(buf, sizeof(buf), " · %s %.6g ms", width_cmp_text(tc.condition.cmp),
tc.condition.width_s * 1000.0);
text += buf;
break;
case TriggerType::Edge:
case TriggerType::Window:
std::snprintf(buf, sizeof(buf), " · %.6g…%.6g", tc.condition.level, tc.condition.level2);
text += buf;
condition += std::string(", ") + trigger_edge_text(tc.condition.edge);
break;
case TriggerType::PulseWidth:
condition += std::string(", ") + width_cmp_text(tc.condition.cmp) + " " + ms(tc.condition.width_s);
if (tc.condition.cmp == WidthCmp::Within || tc.condition.cmp == WidthCmp::Outside) {
condition += " .. " + ms(tc.condition.width2_s);
}
break;
case TriggerType::Dropout:
std::snprintf(buf, sizeof(buf), " · %.6g ms", tc.condition.timeout_s * 1000.0);
text += buf;
break;
case TriggerType::Edge:
std::snprintf(buf, sizeof(buf), " · level %.6g", tc.condition.level);
text += buf;
condition += std::string(", ") + ms(tc.condition.timeout_s);
break;
}
if (!(tc.condition.noise_band > 0.0) || tc.condition.type == TriggerType::Dropout) {
// A band is not offered for a dropout, so it is not quoted for one either.
} else {
std::snprintf(buf, sizeof(buf), " · band %.6g", tc.condition.noise_band);
text += buf;
put("condition", condition);
switch (tc.condition.type) {
case TriggerType::Window:
put("level", number(tc.condition.level) + " .. " + number(tc.condition.level2));
break;
case TriggerType::Dropout:
put("timeout", ms(tc.condition.timeout_s));
break;
default:
put("level", number(tc.condition.level));
break;
}
if (tc.condition.hf_fc > 0.0) {
std::snprintf(buf, sizeof(buf), " · HF %.6g Hz", tc.condition.hf_fc);
text += buf;
put("mode", trigger_mode_text(tc.mode));
// Everything else only when it is not at its default: a line that says "band 0" is a line that says
// nothing, and the four lines above are the ones a user reads.
if (tc.condition.noise_band > 0.0 && tc.condition.type != TriggerType::Dropout) {
put("noise band", number(tc.condition.noise_band));
}
std::snprintf(buf, sizeof(buf), " · %.6g %% pre", tc.pretrigger_pct);
text += buf;
gtk_label_set_text(GTK_LABEL(app.trigger_summary), text.c_str());
if (tc.condition.hf_fc > 0.0) put("HF reject", number(tc.condition.hf_fc) + " Hz");
if (std::fabs(tc.pretrigger_pct - 50.0) > 1.0e-9) put("pre-trigger", number(tc.pretrigger_pct) + " %");
if (std::fabs(tc.holdoff_s - 0.010) > 1.0e-12) put("hold-off", ms(tc.holdoff_s));
for (int i = row; i < App::kTriggerSummaryRows; ++i) {
gtk_widget_set_visible(app.trigger_summary_keys[i], FALSE);
gtk_widget_set_visible(app.trigger_summary_values[i], FALSE);
}
app.dbg_trigger_summary = reading;
gtk_widget_set_visible(app.trigger_summary, app.trigger_locked);
}
TriggerType trigger_type_at(guint i) {
@@ -616,7 +647,7 @@ GtkWidget* build_trigger_panel(App& app) {
gtk_widget_set_hexpand(app.trigger_summary, TRUE);
gtk_widget_set_visible(app.trigger_summary, FALSE);
gtk_box_append(GTK_BOX(head), app.trigger_summary);
app.trigger_edit = gtk_button_new_from_icon_name("emblem-system-symbolic");
app.trigger_edit = gtk_button_new_from_icon_name("document-edit-symbolic");
gtk_widget_add_css_class(app.trigger_edit, "flat");
gtk_widget_set_valign(app.trigger_edit, GTK_ALIGN_CENTER);
gtk_widget_set_tooltip_text(app.trigger_edit, "configure the trigger");
@@ -629,8 +660,30 @@ GtkWidget* build_trigger_panel(App& app) {
g_signal_connect(app.trigger_switch, "notify::active", G_CALLBACK(on_panel_switch_toggled), &app);
gtk_box_append(GTK_BOX(head), app.trigger_switch);
// The locked pane's content: the settings as a definition list, so the answer to "what is this set to"
// is four short lines rather than a row of controls.
app.trigger_summary = gtk_grid_new();
gtk_grid_set_row_spacing(GTK_GRID(app.trigger_summary), 2);
gtk_grid_set_column_spacing(GTK_GRID(app.trigger_summary), 12);
gtk_widget_set_margin_top(app.trigger_summary, 6);
gtk_widget_set_margin_bottom(app.trigger_summary, 6);
gtk_widget_set_margin_start(app.trigger_summary, 8);
gtk_widget_set_margin_end(app.trigger_summary, 8);
for (int i = 0; i < App::kTriggerSummaryRows; ++i) {
app.trigger_summary_keys[i] = gtk_label_new("");
gtk_widget_add_css_class(app.trigger_summary_keys[i], "dim-label");
gtk_widget_set_halign(app.trigger_summary_keys[i], GTK_ALIGN_START);
app.trigger_summary_values[i] = gtk_label_new("");
gtk_widget_set_halign(app.trigger_summary_values[i], GTK_ALIGN_START);
gtk_label_set_ellipsize(GTK_LABEL(app.trigger_summary_values[i]), PANGO_ELLIPSIZE_END);
gtk_grid_attach(GTK_GRID(app.trigger_summary), app.trigger_summary_keys[i], 0, i, 1, 1);
gtk_grid_attach(GTK_GRID(app.trigger_summary), app.trigger_summary_values[i], 1, i, 1, 1);
}
gtk_widget_set_visible(app.trigger_summary, FALSE);
GtkWidget* outer = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_append(GTK_BOX(outer), head);
gtk_box_append(GTK_BOX(outer), app.trigger_summary);
gtk_box_append(GTK_BOX(outer), box);
return outer;
}