ui: the layout editor's buttons keep their look in one place, closing that plan row

The row asked for these buttons in a .ui file and that is not possible: there is one per
row and one more than that per boundary, and a GtkBuilder instantiates a file's objects
once, so the file can hold neither the count nor a prototype for the code to copy. The
plan's note ("only the buttons are template") is wrong for the same reason, and the plan
document now says so.

What was real in the row was the duplication: the look was written out three times -- the
icon, the two CSS classes that make a small round on-screen-display control, the size,
the overlay append and the bookkeeping -- and is now one function,
`layout_overlay_button()`. Each loop keeps only what makes it different: its alignment,
its sensitivity, the data it carries and, for the resize handle, its drag gesture.

The layout probe is the check and its readings are byte-identical before and after, all
seven lines: `buttons=5 done=1 done_box=[680,4,72x42] close=[4] add=[0,832] resize=[(1:-)]`
and the row geometry through add-above, remove, add-at-end and Editor off.
This commit is contained in:
Martino Ferrari committed 2026-10-12 16:30:00 +02:00
1 parent 148d1168d6
commit c94bc4316c
3 files changed
+45 -25

No files matched your search

+31 -25
View File
@@ -699,6 +699,28 @@ void on_layout_drag_end(GtkGestureDrag*, double, double, gpointer user) {
/// Builds one `×` per row and one `+` per boundary, or takes them all off again when the
/// editor is switched off.
/// One of the layout editor's overlay buttons: the look they all share is *here*, once. It was written out
/// three times -- the icon, the two CSS classes that make a small round on-screen-display control, and the
/// size -- and the fourth copy would have been a button added next to them.
///
/// These buttons cannot be a `.ui` file, and the reason is worth stating because the plan expected them to
/// be: their *number* is the row count, one per row and one more than that for the boundaries, and a
/// `GtkBuilder` instantiates a file's objects once. This function reappears whenever a `.ui` is asked to hold
/// something whose count is data.
///
/// What each button does with the look is the caller's: they differ in alignment, sensitivity, the data they
/// carry and the gesture they take.
GtkWidget* layout_overlay_button(App& app, const char* icon, const char* tooltip) {
GtkWidget* b = gtk_button_new_from_icon_name(icon);
gtk_widget_set_tooltip_text(b, tooltip);
gtk_widget_add_css_class(b, "circular");
gtk_widget_add_css_class(b, "osd");
gtk_widget_set_size_request(b, kLayoutButtonPx, kLayoutButtonPx);
gtk_overlay_add_overlay(GTK_OVERLAY(app.area_overlay), b);
app.layout_buttons.push_back(b);
return b;
}
void rebuild_layout_editor(App& app) {
clear_layout_editor(app);
if (!app.layout_editing || app.area_overlay == nullptr) return;
@@ -706,48 +728,34 @@ void rebuild_layout_editor(App& app) {
app.layout_buttons_rows = rows;
for (int i = 0; i < rows; ++i) {
GtkWidget* b = gtk_button_new_from_icon_name("window-close-symbolic");
gtk_widget_set_tooltip_text(b, "remove this row");
gtk_widget_add_css_class(b, "circular");
gtk_widget_add_css_class(b, "osd");
gtk_widget_set_size_request(b, kLayoutButtonPx, kLayoutButtonPx);
GtkWidget* b = layout_overlay_button(app, "window-close-symbolic", "remove this row");
gtk_widget_set_halign(b, GTK_ALIGN_START);
gtk_widget_set_valign(b, GTK_ALIGN_START);
gtk_widget_set_sensitive(b, rows > 1);
set_row_key(b, i);
g_signal_connect(b, "clicked", G_CALLBACK(on_layout_row_remove), &app);
gtk_overlay_add_overlay(GTK_OVERLAY(app.area_overlay), b);
app.layout_buttons.push_back(b);
}
for (int b_i = 0; b_i <= rows; ++b_i) {
GtkWidget* b = gtk_button_new_from_icon_name("list-add-symbolic");
gtk_widget_set_tooltip_text(b, b_i == 0 ? "add a row above"
: b_i == rows ? "add a row below"
: "add a row here");
gtk_widget_add_css_class(b, "circular");
gtk_widget_add_css_class(b, "osd");
gtk_widget_set_size_request(b, kLayoutButtonPx, kLayoutButtonPx);
GtkWidget* b = layout_overlay_button(app, "list-add-symbolic",
b_i == 0 ? "add a row above"
: b_i == rows ? "add a row below"
: "add a row here");
gtk_widget_set_halign(b, GTK_ALIGN_CENTER);
gtk_widget_set_valign(b, GTK_ALIGN_START);
gtk_widget_set_sensitive(b, rows < RowSet::kMaxRows);
set_row_key(b, b_i);
g_object_set_data(G_OBJECT(b), kLayoutKindKey, b);
g_signal_connect(b, "clicked", G_CALLBACK(on_layout_row_insert), &app);
gtk_overlay_add_overlay(GTK_OVERLAY(app.area_overlay), b);
app.layout_buttons.push_back(b);
}
// ... and one arrow per boundary whose *only* job is resizing the two rows it sits between. A widget
// of its own rather than a second gesture on the `+`, so each handle does one thing -- and a handle
// that cannot be clicked cannot be clicked by accident. The figure's own edges are not boundaries
// (there is no pair of rows to share between them), so the first and last are inert.
for (int b_i = 0; b_i <= rows; ++b_i) {
GtkWidget* b = gtk_button_new_from_icon_name("udpscope-resize-rows-symbolic");
gtk_widget_set_tooltip_text(b, b_i == 0 ? "drag to resize the first row"
: b_i == rows ? "drag to resize the last row"
: "drag to resize the two rows here");
gtk_widget_add_css_class(b, "circular");
gtk_widget_add_css_class(b, "osd");
gtk_widget_set_size_request(b, kLayoutButtonPx, kLayoutButtonPx);
GtkWidget* b = layout_overlay_button(app, "udpscope-resize-rows-symbolic",
b_i == 0 ? "drag to resize the first row"
: b_i == rows ? "drag to resize the last row"
: "drag to resize the two rows here");
gtk_widget_set_halign(b, GTK_ALIGN_CENTER);
gtk_widget_set_valign(b, GTK_ALIGN_START);
// Hidden, not disabled, where it has no pair to share between: the figure's own edges. A greyed
@@ -766,8 +774,6 @@ void rebuild_layout_editor(App& app) {
g_signal_connect(drag, "drag-update", G_CALLBACK(on_layout_drag_update), &app);
g_signal_connect(drag, "drag-end", G_CALLBACK(on_layout_drag_end), &app);
gtk_widget_add_controller(b, GTK_EVENT_CONTROLLER(drag));
gtk_overlay_add_overlay(GTK_OVERLAY(app.area_overlay), b);
app.layout_buttons.push_back(b);
}
// The editor's way out, in the area's top-right corner: the toolbar's toggle is in the