diff --git a/src/ui.cpp b/src/ui.cpp index b9edb7e..03c7ea7 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -2280,36 +2280,30 @@ void rebuild_math_pane(App& app) { } GtkWidget* build_measures_panel(App& app) { - GtkWidget* box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); - gtk_widget_set_margin_top(box, 6); - gtk_widget_set_margin_bottom(box, 6); - gtk_widget_set_margin_start(box, 6); - gtk_widget_set_margin_end(box, 6); + // The panel's frame is a UI file; the readings are not, because each row is one measurement and they are + // rebuilt whenever the set or the mode changes. `make test-ui` checks the file's types against the GIR; + // the ids below are the only thing that keeps the code and the file agreeing on the names. + GtkBuilder* ui = ui_load(app, "/org/udpscope/ui/measures-panel.ui"); + GtkWidget* box = GTK_WIDGET(gtk_builder_get_object(ui, "measures_panel")); + GtkWidget* add = GTK_WIDGET(gtk_builder_get_object(ui, "measures_add")); + app.measures_stats_check = GTK_WIDGET(gtk_builder_get_object(ui, "measures_stats_check")); + app.measures_stats_reset = GTK_WIDGET(gtk_builder_get_object(ui, "measures_stats_reset")); + app.measures_list = GTK_WIDGET(gtk_builder_get_object(ui, "measures_list")); + if (box == nullptr || add == nullptr || app.measures_stats_check == nullptr || + app.measures_stats_reset == nullptr || app.measures_list == nullptr) { + // A missing id is a mistake in the `.ui`. The pane comes up empty rather than taking the window with + // it, and `[udpscope] measures pane:` reads differently instead of the application failing to start. + return box != nullptr ? box : gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + } - GtkWidget* head = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); - GtkWidget* title = gtk_label_new("Measurements"); - gtk_widget_set_halign(title, GTK_ALIGN_START); - gtk_widget_set_hexpand(title, true); - gtk_box_append(GTK_BOX(head), title); - GtkWidget* add = gtk_button_new_from_icon_name("list-add-symbolic"); - gtk_widget_add_css_class(add, "flat"); - gtk_widget_set_tooltip_text(add, "add a measurement"); g_signal_connect(add, "clicked", G_CALLBACK(+[](GtkButton*, gpointer user) { open_measure_dialog(*static_cast(user), -1); }), &app); - gtk_box_append(GTK_BOX(head), add); - gtk_box_append(GTK_BOX(box), head); // One toggle for the whole pane, not one per measurement: statistics are either what the // user is looking at or they are not, and a per-row switch is a column of chrome paid for // on every row to answer a question asked once. - GtkWidget* stats_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); - app.measures_stats_check = gtk_check_button_new_with_label("Statistics"); - gtk_widget_set_hexpand(app.measures_stats_check, true); - gtk_widget_set_tooltip_text(app.measures_stats_check, - "show how each reading has varied: passes, mean, standard " - "deviation and extremes, under the reading itself"); g_signal_connect(app.measures_stats_check, "toggled", G_CALLBACK(+[](GtkCheckButton* b, gpointer user) { App* a = static_cast(user); @@ -2328,13 +2322,7 @@ GtkWidget* build_measures_panel(App& app) { schedule_layout_save(*a); }), &app); - gtk_box_append(GTK_BOX(stats_row), app.measures_stats_check); - app.measures_stats_reset = gtk_button_new_with_label("Reset"); - gtk_widget_add_css_class(app.measures_stats_reset, "flat"); - gtk_widget_set_tooltip_text(app.measures_stats_reset, - "start every measurement's statistics again from the next pass"); - gtk_widget_set_sensitive(app.measures_stats_reset, false); g_signal_connect(app.measures_stats_reset, "clicked", G_CALLBACK(+[](GtkButton*, gpointer user) { App* a = static_cast(user); @@ -2342,11 +2330,6 @@ GtkWidget* build_measures_panel(App& app) { update_measures(*a, /*force=*/true, /*accumulate=*/true); }), &app); - gtk_box_append(GTK_BOX(stats_row), app.measures_stats_reset); - gtk_box_append(GTK_BOX(box), stats_row); - - app.measures_list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); - gtk_box_append(GTK_BOX(box), app.measures_list); return box; } @@ -5775,10 +5758,17 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit { std::lock_guard lock(model.mutex()); std::printf("[udpscope] measures pane: n=%zu revealed=%d marks_shown=%d pane_rows=%zu " - "pane_w=%d pane_h=%d\n", + "pane_w=%d pane_h=%d switch_on=%d reset_sensitive=%d\n", app.measures.size(), app.measures_revealed ? 1 : 0, rows.measure_marks_placed(), app.measure_labels.size(), - app.dbg_measures_pane_w, app.dbg_measures_pane_h); + app.dbg_measures_pane_w, app.dbg_measures_pane_h, + // The two widgets the panel's *frame* is read by: the code drives `measure_stats_on`, + // so the switch and the Reset button are where a `.ui` that lost them -- or lost the + // wiring to them -- shows up. `switch_on` disagreeing with `[stats-probe] on=` is that. + app.measures_stats_check != nullptr && + gtk_check_button_get_active(GTK_CHECK_BUTTON(app.measures_stats_check)) ? 1 : 0, + app.measures_stats_reset != nullptr && + gtk_widget_get_sensitive(app.measures_stats_reset) ? 1 : 0); } // `locked` is re-reported here rather than only in the readouts line: this task added a // whole new cursor pool, and a pool created selectable is exactly the regression that a diff --git a/src/ui/measures-panel.ui b/src/ui/measures-panel.ui new file mode 100644 index 0000000..bd4086d --- /dev/null +++ b/src/ui/measures-panel.ui @@ -0,0 +1,72 @@ + + + + + + + vertical + 4 + 6 + 6 + 6 + 6 + + + horizontal + 8 + + + Measurements + start + 1 + + + + + list-add-symbolic + flat + add a measurement + + + + + + + horizontal + 8 + + + Statistics + 1 + show how each reading has varied: passes, mean, standard deviation and extremes, under the reading itself + + + + + Reset + flat + 0 + start every measurement's statistics again from the next pass + + + + + + + vertical + 2 + + + + diff --git a/src/ui/udpscope-ui.gresource.xml b/src/ui/udpscope-ui.gresource.xml index 1365bd6..e4b06af 100644 --- a/src/ui/udpscope-ui.gresource.xml +++ b/src/ui/udpscope-ui.gresource.xml @@ -16,5 +16,6 @@ source-chooser.ui main-window.ui cursors-panel.ui + measures-panel.ui