From da23eb1377f87bc4aeacfca03388853555d33616 Mon Sep 17 00:00:00 2001 From: momysnow <131729952+momysnow@users.noreply.github.com> Date: Sat, 31 Oct 2026 06:23:41 +0000 Subject: [PATCH] Sketch dialog follows "Dialog buttons at top" (#9860) (#9884) * claim: #9860 Co-Authored-By: Claude * Sketch panel follows Dialog buttons at top (#9860) The Sketch panel drew Finish Sketch, Look at and Slice in a footer under its options even with Preferences > Interface > Dialog buttons set to Top. Panel::palette now reads the same published preference as the feature cards and draws its footer row under the header, above the options, when it is Top. With the preference off nothing changes. Changes a default: no Co-Authored-By: Claude * Docs: Dialog buttons at top also moves the Sketch panel's Finish row (#9860) Co-Authored-By: Claude --------- Co-authored-by: Claude --- crates/ui-egui/src/dialogs_sketch_tests.rs | 19 +++++++++++++++++++ crates/ui-egui/src/ui_kit/panel.rs | 12 ++++++++++-- docs/book/src/guide/ui-tour.md | 3 ++- docs/control-protocol.md | 2 +- docs/ui-kit.md | 2 ++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/dialogs_sketch_tests.rs b/crates/ui-egui/src/dialogs_sketch_tests.rs index 1fea8292d9..84d82b3023 100644 --- a/crates/ui-egui/src/dialogs_sketch_tests.rs +++ b/crates/ui-egui/src/dialogs_sketch_tests.rs @@ -265,6 +265,25 @@ fn apply_in_the_header_row_commits_once_and_keeps_the_dialog() { assert_eq!(scenario::rect_of("dialog:apply"), Some(apply), "the buttons stay where they were"); } +/// Interface › Dialog buttons › Top (#9860): the sketch panel's Finish Sketch row sits above its +/// options, and below them with the preference off. +#[test] +fn sketch_panel_finish_row_follows_dialog_buttons() { + for (placement, on_top) in [("top", true), ("bottom", false)] { + let mut h = fixture(); + h.call("ui.prefs", json!({"dialog_buttons": placement})); + h.settle(); + let finish = scenario::rect_of("sketch_palette:finish").unwrap_or_else(|| panic!("{placement}: no Finish Sketch")); + let first = scenario::rect_of("sketch_palette:toggle:show_constraints").unwrap_or_else(|| panic!("{placement}: no first option")); + if on_top { + assert!(finish.bottom() <= first.top(), "top: Finish {finish:?} above its first option {first:?}"); + } else { + assert!(first.bottom() <= finish.top(), "bottom: Finish {finish:?} below its first option {first:?}"); + } + assert!(scenario::handle_at("sketch_palette:finish").is_some(), "{placement}: the handle stays"); + } +} + #[test] fn picked_list_removes_one_item_and_field_still_activates_input() { let mut h = fixture(); diff --git a/crates/ui-egui/src/ui_kit/panel.rs b/crates/ui-egui/src/ui_kit/panel.rs index 85870af5d1..d6251fa70c 100644 --- a/crates/ui-egui/src/ui_kit/panel.rs +++ b/crates/ui-egui/src/ui_kit/panel.rs @@ -851,6 +851,8 @@ impl<'a> Panel<'a> { /// Tokens: space_24 and form_footer_height; header/body/footer own their historical padding. pub(crate) fn palette(&self, ui: &mut Ui, bottom: f32, top: f32, mut add: impl FnMut(&mut Ui, Part)) { let t = Tokens::get(); + // Dialog buttons at top (#9860): the footer row moves under the header, above the options. + let at_top = DialogButtons::current(ui.ctx()) == DialogButtons::Top; ui.set_max_height((bottom - ui.cursor().top()).max(0.0)); super::scope(ui, |ui| { ui.spacing_mut().item_spacing.y = 0.0; @@ -858,7 +860,11 @@ impl<'a> Panel<'a> { let short = height < t.space_24 * 6.0; let mut contents = |ui: &mut Ui| { add(ui, Part::Header); - let body_height = (bottom - ui.cursor().top() - t.form_footer_height).max(0.0); + if at_top { + add(ui, Part::Footer); + } + let footer_height = if at_top { 0.0 } else { t.form_footer_height }; + let body_height = (bottom - ui.cursor().top() - footer_height).max(0.0); let mut body = |ui: &mut Ui| { egui::Frame::NONE.inner_margin(egui::Margin::symmetric(t.space_10 as i8, t.space_4 as i8)).show(ui, |ui| add(ui, Part::Body)); }; @@ -878,7 +884,9 @@ impl<'a> Panel<'a> { .inner; scenario::publish_rect("sketch_palette:options", scroll.inner_rect); } - add(ui, Part::Footer); + if !at_top { + add(ui, Part::Footer); + } }; if short { let scroll = egui::ScrollArea::vertical() diff --git a/docs/book/src/guide/ui-tour.md b/docs/book/src/guide/ui-tour.md index de429a9416..6405937743 100644 --- a/docs/book/src/guide/ui-tour.md +++ b/docs/book/src/guide/ui-tour.md @@ -124,7 +124,8 @@ wide as that column; the column comes back as it was when the dialog closes. Interface › **Dialog buttons** › **Top** puts OK, Cancel and Apply in the dialog's title row, right of the title, where they stay while sections open and close; - Cancel then also takes the place of the close button. Enter and Esc work as before. + Cancel then also takes the place of the close button. Enter and Esc work as before. The Sketch + panel's Finish Sketch, Look at and Slice move up under its title, above the options. - **Timeline** (bottom). Every feature in order. Double-click to edit, drag the marker to roll back, and drag features to reorder them. A sketch's icon, here and in the browser, takes the colour of its state: the sketch colour while it is under constrained, the fully constrained diff --git a/docs/control-protocol.md b/docs/control-protocol.md index 1bfd207716..b1122d20b5 100644 --- a/docs/control-protocol.md +++ b/docs/control-protocol.md @@ -433,7 +433,7 @@ One setting per line, sorted by name. A change that adds a setting adds its own - `check_updates` (default true; [updates](book/src/guide/updates.md)) - `color_vision` (`off` (default), `protan`, `deutan`, `tritan`: colour vision modes, #778) - `default_units` -- `dialog_buttons` (`bottom` (default), `top`: a feature dialog's OK, Cancel and Apply sit in its title row instead of under its inputs, #6708; the buttons keep their handles, and `dialog:close` is then Cancel's) +- `dialog_buttons` (`bottom` (default), `top`: a feature dialog's OK, Cancel and Apply sit in its title row instead of under its inputs, #6708; the buttons keep their handles, and `dialog:close` is then Cancel's; the Sketch panel's Finish Sketch row moves above its options, #9860) - `dialog_place` (`edge` (default) or `left_column`: command dialogs open in the left column in place of the browser and the History, #6707) - `extrude_auto_to_object` (default false: in the Extrude dialog, with a profile picked, a face, vertex or parallel plane picked off its plane switches the extent to To Object; it applies to Extrude dialogs opened afterwards) - `frame_rate_details` (default false: with the frame rate, a frame-time graph of the frames drawn with the triangles over it, a frame-time histogram and what the GPU drew; clicking the readout switches it) diff --git a/docs/ui-kit.md b/docs/ui-kit.md index a5b0b1847e..7c989c168f 100644 --- a/docs/ui-kit.md +++ b/docs/ui-kit.md @@ -107,6 +107,8 @@ rules. Its compass is 110 pt; the 48 pt Shots strip clears the docks by 8 pt. puts the actions in the header row through `HeaderActions` instead of a footer: the same buttons, enabled states, keyboard and handles, each as wide as its bold label, with Cancel standing in for the close button and the compute status in the title's place. + `palette` (the Sketch panel) follows the same preference: its footer row moves under the + header, above the options. `body_rows` caps a manager's scrolling body independently of its position; short viewports still keep the header and actions visible. - `Window` composes the shared Panel frame with a 40 pt icon/title/close header,