From dbfe3fdc3803cedf79a0424d3fb2d43bccb49237 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 4 Nov 2026 03:21:05 -0800 Subject: [PATCH] Global Edit: the options dropdowns close when Global Edit starts The Global Edit Options dropdowns in the Control bar and in the Properties panel's Quick Actions turn their button off while Global Edit is on, but a popup that was already open stayed open and still changed the options. Both dropdowns now close their popup while Global Edit is on, and it stays closed after Global Edit stops until the button is clicked again. --- crates/ui-egui/src/chrome/control_bar.rs | 7 ++-- crates/ui-egui/src/chrome/tests.rs | 9 +++-- crates/ui-egui/src/panels/properties.rs | 12 ++++++- crates/ui-egui/src/panels/properties/tests.rs | 34 +++++++++++++++++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/crates/ui-egui/src/chrome/control_bar.rs b/crates/ui-egui/src/chrome/control_bar.rs index a40d88dfe..1df1f08ab 100644 --- a/crates/ui-egui/src/chrome/control_bar.rs +++ b/crates/ui-egui/src/chrome/control_bar.rs @@ -142,9 +142,9 @@ const GLOBAL_EDIT_OPTIONS_WIDTH: f32 = 24.0; /// page places the options in the Control panel as well as in Properties /// (). /// It opens the Properties panel's Global Edit Options, which set the same session options, and is -/// off while Global Edit is on, as that panel's dropdown is. It sits after Recolor Artwork, ahead of -/// the X/Y/W/H fields that the bar leaves out when it runs short of room, and is left out only when -/// no room is left for it. +/// off and closed while Global Edit is on, as that panel's dropdown is. It sits after Recolor +/// Artwork, ahead of the X/Y/W/H fields that the bar leaves out when it runs short of room, and is +/// left out only when no room is left for it. fn global_edit_options(app: &mut VectorcraftApp, ui: &mut Ui) { if !crate::menus::enabled(app, "select.globalEdit") || ui.available_width() < GLOBAL_EDIT_OPTIONS_WIDTH { return; @@ -152,6 +152,7 @@ fn global_edit_options(app: &mut VectorcraftApp, ui: &mut Ui) { let on = app.session.active().is_some_and(|d| d.global_edit.is_some()); let options = widgets::icon_button_enabled(ui, "chevron-down", tl!("Global Edit Options"), false, !on, GLOBAL_EDIT_OPTIONS_WIDTH); egui::Popup::menu(&options) + .open_memory(crate::panels::properties::global_edit_options_open(&options, on)) .close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside) .show(|ui| crate::panels::properties::global_edit_options(app, ui)); } diff --git a/crates/ui-egui/src/chrome/tests.rs b/crates/ui-egui/src/chrome/tests.rs index 00c56ff67..5c49b153d 100644 --- a/crates/ui-egui/src/chrome/tests.rs +++ b/crates/ui-egui/src/chrome/tests.rs @@ -1807,7 +1807,7 @@ fn the_control_bar_shows_the_distribute_buttons_for_two_or_more_objects() { /// The Control bar shows the Global Edit Options dropdown while Start Global Edit can run. Its /// options are the session's, which the Properties panel's dropdown also sets: Match Size from /// the Control bar narrows the Results and the objects Start Global Edit takes. The dropdown is off -/// while Global Edit is on, and absent when the selection cannot start it. +/// and closed while Global Edit is on, and absent when the selection cannot start it. #[test] fn the_control_bar_shows_the_global_edit_options() { crate::i18n::set_current(crate::i18n::Lang::EN); @@ -1860,9 +1860,14 @@ fn the_control_bar_shows_the_global_edit_options() { let nodes = frame(&mut app, vec![]); let button = find(&nodes, "Global Edit Options").unwrap_or_else(|| panic!("Global Edit on: the dropdown still shows")); assert!(button.is_disabled(), "the options are off while Global Edit is on"); + let results = |nodes: &Nodes| nodes.iter().any(|(_, n)| n.label().or(n.value()).is_some_and(|t| t.starts_with("Result"))); + assert!(!results(&nodes), "Global Edit starting closes the open options"); + assert!(!results(&frame(&mut app, vec![]))); + app.run("select.globalEdit", json!({})).unwrap(); + frame(&mut app, vec![]); + assert!(!results(&frame(&mut app, vec![])), "the options stay closed once Global Edit stops"); // At any width the button either fits inside the bar or is left out, and a wider bar never // leaves it out once a narrower one showed it. - app.run("select.globalEdit", json!({})).unwrap(); let mut seen = false; for width in (600..=2400).step_by(10) { let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(width as f32, 300.0)); diff --git a/crates/ui-egui/src/panels/properties.rs b/crates/ui-egui/src/panels/properties.rs index f236c6a73..b775dc05a 100644 --- a/crates/ui-egui/src/panels/properties.rs +++ b/crates/ui-egui/src/panels/properties.rs @@ -240,10 +240,20 @@ fn global_edit_row(app: &mut VectorcraftApp, ui: &mut Ui) { crate::menus::invoke(app, "select.globalEdit", json!({})); } let options = widgets::icon_button_enabled(ui, "chevron-down", tl!("Global Edit Options"), false, !on, 20.0); - egui::Popup::menu(&options).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| global_edit_options(app, ui)); + egui::Popup::menu(&options) + .open_memory(global_edit_options_open(&options, on)) + .close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside) + .show(|ui| global_edit_options(app, ui)); }); } +/// The open state for a Global Edit Options dropdown's popup: a click on the button toggles the +/// popup, and the popup is closed while Global Edit is on, including when Global Edit starts with +/// the popup open. +pub(crate) fn global_edit_options_open(options: &egui::Response, on: bool) -> Option { + if on { Some(egui::SetOpenCommand::Bool(false)) } else { options.clicked().then_some(egui::SetOpenCommand::Toggle) } +} + /// The Global Edit Options: what else similar objects must share (Match), where they may lie (On /// Artboards) and how many objects that takes now. The Control bar's Global Edit Options dropdown /// shows them too. diff --git a/crates/ui-egui/src/panels/properties/tests.rs b/crates/ui-egui/src/panels/properties/tests.rs index f13e98384..1a38e4f70 100644 --- a/crates/ui-egui/src/panels/properties/tests.rs +++ b/crates/ui-egui/src/panels/properties/tests.rs @@ -135,6 +135,40 @@ fn start_global_edit_quick_action_and_escape() { assert!(!tall(&mut app, vec![]).iter().any(|(t, _)| t == "Start Global Edit"), "type has no Global Edit"); } +/// The Quick Actions' Global Edit Options close when Global Edit starts while they are open, and +/// stay closed once it stops. +#[test] +fn global_edit_starting_closes_the_quick_action_options() { + crate::i18n::set_current(crate::i18n::Lang::EN); + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 400, "height": 300})).unwrap(); + let a = app.run("shape.rectangle", json!({"x": 20, "y": 20, "width": 40, "height": 40})).unwrap()["id"].clone(); + app.run("shape.rectangle", json!({"x": 200, "y": 20, "width": 40, "height": 40})).unwrap(); + app.run("select.set", json!({"ids": [a]})).unwrap(); + let ctx = egui::Context::default(); + ctx.enable_accesskit(); + let frame = |app: &mut VectorcraftApp, events: Vec| { + let raw = egui::RawInput { screen_rect: Some(Rect::from_min_size(Pos2::ZERO, vec2(300.0, 700.0))), events, ..Default::default() }; + let mut out = ctx.run_ui(raw, |ui| global_edit_row(app, ui)); + out.textures_delta.clear(); + out.platform_output.accesskit_update.map(|u| u.nodes).unwrap_or_default() + }; + type Nodes = Vec<(egui::accesskit::NodeId, egui::accesskit::Node)>; + let results = |nodes: &Nodes| nodes.iter().any(|(_, n)| n.label().or(n.value()).is_some_and(|t| t.starts_with("Result"))); + let nodes = frame(&mut app, vec![]); + let b = nodes.iter().find(|(_, n)| n.label() == Some("Global Edit Options")).and_then(|(_, n)| n.bounds()).unwrap_or_else(|| panic!("{nodes:?}")); + let at = Pos2::new(((b.x0 + b.x1) / 2.0) as f32, ((b.y0 + b.y1) / 2.0) as f32); + let press = |pressed| Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: Default::default() }; + frame(&mut app, vec![Event::PointerMoved(at), press(true)]); + frame(&mut app, vec![press(false)]); + assert!(results(&frame(&mut app, vec![])), "the options open"); + app.run("select.globalEdit", json!({})).unwrap(); + assert!(!results(&frame(&mut app, vec![])), "Global Edit starting closes the open options"); + app.run("select.globalEdit", json!({})).unwrap(); + frame(&mut app, vec![]); + assert!(!results(&frame(&mut app, vec![])), "the options stay closed once Global Edit stops"); +} + /// The options' Results count is found once per document revision, options and selected /// object, not every frame; another selected object or other options find it again. #[test]