From 61cd4ed6e6ad51145c9d8266140b1334d29071ea Mon Sep 17 00:00:00 2001 From: theironchef <6278036+theironchef@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:38:09 -0700 Subject: [PATCH] Dialogs: the button row is as wide as the fields, not the window The OK/Cancel row was laid out right to left at the full available width, and a dialog without a maximum width may use the whole window: the row, and the window with it, spanned the window (Clean Up, Blend Options, every generic parameter dialog). The row now spans the fields above it, at least the dialog's minimum width, so a dialog is sized to its content. Fixes #238. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/dialogs/mod.rs | 7 ++++--- crates/ui-egui/src/dialogs/tests.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/src/dialogs/mod.rs b/crates/ui-egui/src/dialogs/mod.rs index 94c5e53d..468c40ce 100644 --- a/crates/ui-egui/src/dialogs/mod.rs +++ b/crates/ui-egui/src/dialogs/mod.rs @@ -328,9 +328,10 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { ui.add_space(12.0); cancel = (spec.body)(app, ui, &mut d); ui.add_space(16.0); - // The button row is as tall as the buttons: a right-to-left layout would otherwise take - // all the height left in the window, so the window could never shrink to its content. - let row = egui::vec2(ui.available_width(), ui.spacing().interact_size.y); + // The button row is as wide as the fields above it and as tall as the buttons: a + // right-to-left layout would otherwise take all the room left in the window, so the + // window could never shrink to its content. + let row = egui::vec2(ui.min_rect().width(), ui.spacing().interact_size.y); ui.allocate_ui_with_layout(row, egui::Layout::right_to_left(egui::Align::Center), |ui| { if let Some(label) = spec.ok.map(|ok| spec.ok_label.map_or(ok, |f| f(app))) && widgets::primary_button(ui, label).clicked() diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs index ecffbffb..94c89b45 100644 --- a/crates/ui-egui/src/dialogs/tests.rs +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -246,3 +246,30 @@ fn dialogs_do_not_stretch_to_the_screen() { } assert!(checked >= 10, "only {checked} dialogs drew in the shared frame"); } + +/// The dialog window of `app`'s open dialog after a few frames in a `w` × 900 window. +fn dialog_rect(app: &mut VectorcraftApp, w: f32) -> egui::Rect { + let ctx = egui::Context::default(); + theme::install_fonts(&ctx); + theme::apply(&ctx, Default::default()); + let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(w, 900.0)); + let kind = app.ui.dialog.as_ref().unwrap().kind.clone(); + for _ in 0..3 { + let input = egui::RawInput { screen_rect: Some(screen), ..Default::default() }; + ctx.run_ui(input, |ui| show(app, ui.ctx())).textures_delta.clear(); + } + ctx.memory(|m| m.area_rect(egui::Id::new(("dialog", kind.as_str())))).unwrap() +} + +#[test] +fn a_form_dialog_is_as_wide_as_its_fields_not_the_window() { + let mut app = app(); + // Clean Up: three check boxes in the generic parameter dialog. + let params = json!({"emptyTextPaths": true, "strayPoints": true, "unpaintedObjects": true}); + app.run("ui.paramDialog", json!({"command": "object.path.cleanUp", "label": "Clean Up", "params": params})).unwrap(); + let wide = dialog_rect(&mut app, 1600.0); + assert!(wide.width() < 500.0, "Clean Up is {:.0} wide in a 1600-point window", wide.width()); + // The same dialog in a narrow window still fits it. + let narrow = dialog_rect(&mut app, 360.0); + assert!(narrow.width() <= 360.0, "{:.0} wide in a 360-point window", narrow.width()); +}