From 90a17eb18630727142c9f8dc361871981e44ce29 Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Mon, 12 Oct 2026 15:53:35 +0200 Subject: [PATCH] ui: the math panel's frame is a .ui file The first half of this plan row. src/ui/math-panel.ui holds the panel: the box and its margins, the heading row with the title and the add button, and the list the channel rows go in. The rows stay in code -- each is a channel with its name, unit, expression, colour and its own edit and remove buttons -- so the file holds the frame, as in the measurements and signals panels beside it. Read back by a dump line that also reads the file's own text: `math panel: head=2 buttons=1 list=1 title='Math channels'`, where buttons=0 is a lost `+` and title='-' a lost label. `[math-probe]` still drives the add and opens the editor, so the rows landing in the file's list is read as well as the frame. The other half is the node editor's chrome, which is where least of that file is template: the canvas is a GtkDrawingArea that is drawn rather than declared, and the gestures, the parameter fields and the palette are code. That is its own change. --- docs/claude/probe-readings.md | 12 ++++++++ src/ui.cpp | 18 ++++++++++++ src/ui/math-panel.ui | 49 ++++++++++++++++++++++++++++++++ src/ui/udpscope-ui.gresource.xml | 1 + src/ui_math.cpp | 31 ++++++++------------ 5 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 src/ui/math-panel.ui diff --git a/docs/claude/probe-readings.md b/docs/claude/probe-readings.md index 4914dd4..27de394 100644 --- a/docs/claude/probe-readings.md +++ b/docs/claude/probe-readings.md @@ -1190,3 +1190,15 @@ The signals panel's frame read from the window: the heading and the hint the `.u the rows go in. `labels=1` is a `.ui` that lost one of the two, `list=0` one that lost the list -- both of which otherwise show as a panel quietly missing a piece. `panels: … signals=WxH row_h=26` is the other half: the list's own geometry and the height a row really takes, which is what the compact-row CSS rule is for. + +## `math panel:` + +``` +[udpscope] math panel: head=2 buttons=1 list=1 title='Math channels' +``` + +The math panel's frame read from the window: the head row the `.ui` gives it (the title and the add +button), and the list the channel rows go in. `buttons=0` is a `.ui` that lost the `+` -- a panel that +cannot add a channel, with nothing else complaining -- and `title=` is the file's own text read back, so a +`.ui` that lost the label shows as `title='-'`. `[math-probe]` is the other half of the reading: it drives +the add and the editor through the same code a user does. diff --git a/src/ui.cpp b/src/ui.cpp index 938c412..eb525da 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -5423,6 +5423,24 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit } std::printf("[udpscope] signals panel: labels=%d list=%d\n", labels, lists); } + { + // The math panel's frame, read from the window: the head row with the title and the add button, and the + // list. `buttons=0` is a `.ui` that lost the `+` -- otherwise a panel that cannot add anything, with + // nothing else complaining -- and the title is read out to show the file's own text arrived. + GtkWidget* panel = app.math_list != nullptr ? gtk_widget_get_parent(app.math_list) : nullptr; + GtkWidget* head = panel != nullptr ? gtk_widget_get_first_child(panel) : nullptr; + GtkWidget* title = head != nullptr ? gtk_widget_get_first_child(head) : nullptr; + int head_children = 0; + int buttons = 0; + for (GtkWidget* c = head != nullptr ? gtk_widget_get_first_child(head) : nullptr; c != nullptr; + c = gtk_widget_get_next_sibling(c)) { + ++head_children; + if (GTK_IS_BUTTON(c)) ++buttons; + } + std::printf("[udpscope] math panel: head=%d buttons=%d list=%d title='%s'\n", head_children, + buttons, app.math_list != nullptr ? 1 : 0, + GTK_IS_LABEL(title) ? gtk_label_get_text(GTK_LABEL(title)) : "-"); + } std::printf("[udpscope] panels: side=%dx%d (visible=%d) signals=%dx%d row_h=%d " "(revealed=%d) trigger=%dx%d (revealed=%d) toggles=%d cursors=%d window=%.3fs " "window_cap=%.1fs\n", diff --git a/src/ui/math-panel.ui b/src/ui/math-panel.ui new file mode 100644 index 0000000..9738111 --- /dev/null +++ b/src/ui/math-panel.ui @@ -0,0 +1,49 @@ + + + + + + + vertical + 4 + 6 + 6 + 6 + 6 + + + horizontal + 8 + + + Math channels + start + 1 + + + + + list-add-symbolic + flat + add a math channel + + + + + + + vertical + 2 + + + + diff --git a/src/ui/udpscope-ui.gresource.xml b/src/ui/udpscope-ui.gresource.xml index f40c8e0..b55de40 100644 --- a/src/ui/udpscope-ui.gresource.xml +++ b/src/ui/udpscope-ui.gresource.xml @@ -17,6 +17,7 @@ main-window.ui cursors-panel.ui measures-panel.ui + math-panel.ui trigger-panel.ui signals-panel.ui diff --git a/src/ui_math.cpp b/src/ui_math.cpp index 87d9cbb..02e7422 100644 --- a/src/ui_math.cpp +++ b/src/ui_math.cpp @@ -1033,29 +1033,22 @@ GtkWidget* math_row(const char* label, GtkWidget* widget, GtkWidget** row_out) { } GtkWidget* build_math_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); - - GtkWidget* head = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); - GtkWidget* title = gtk_label_new("Math channels"); - 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 math channel"); + // The panel's frame -- the heading row with its add button, and the list -- is a UI file; the rows are + // not, because each one is a channel and the whole list is rebuilt whenever the set changes. `make test-ui` + // checks the file's types against the GIR; the ids below are what keeps the code and the file agreeing. + GtkBuilder* ui = ui_load(app, "/org/udpscope/ui/math-panel.ui"); + GtkWidget* box = GTK_WIDGET(gtk_builder_get_object(ui, "math_panel")); + GtkWidget* add = GTK_WIDGET(gtk_builder_get_object(ui, "math_add")); + app.math_list = GTK_WIDGET(gtk_builder_get_object(ui, "math_list")); + if (box == nullptr || add == nullptr || app.math_list == nullptr) { + // A missing id is a mistake in the `.ui`: the panel comes up empty rather than taking the window with + // it, and `[udpscope] math panel:` reads the difference. + return box != nullptr ? box : gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); + } g_signal_connect(add, "clicked", G_CALLBACK(+[](GtkButton*, gpointer user) { add_math_channel(*static_cast(user)); }), &app); - gtk_box_append(GTK_BOX(head), add); - gtk_box_append(GTK_BOX(box), head); - - app.math_list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); - gtk_box_append(GTK_BOX(box), app.math_list); return box; }