diff --git a/crates/ui-egui/src/autosave.rs b/crates/ui-egui/src/autosave.rs index e63bf1cbf..d062f69e9 100644 --- a/crates/ui-egui/src/autosave.rs +++ b/crates/ui-egui/src/autosave.rs @@ -150,7 +150,7 @@ pub fn show(app: &mut CadApp, ctx: &egui::Context) { let mut open = None; let mut discard = None; let mut later = false; - egui::Modal::new(egui::Id::unique("cc_drawing_recovery")).show(ctx, |ui| { + crate::modal::instant(ctx, egui::Id::unique("cc_drawing_recovery")).show(ctx, |ui| { ui.set_width(460.0); ui.label(RichText::new("Drawing Recovery").strong()); ui.add_space(4.0); diff --git a/crates/ui-egui/src/closing.rs b/crates/ui-egui/src/closing.rs index 9e4f35ed8..04d526fa2 100644 --- a/crates/ui-egui/src/closing.rs +++ b/crates/ui-egui/src/closing.rs @@ -204,7 +204,7 @@ pub fn prompt(app: &mut CadApp, ctx: &egui::Context) { }; let t = Tokens::get(); let mut choice = None; - egui::Modal::new(egui::Id::unique("cc_close_prompt")).show(ctx, |ui| { + crate::modal::instant(ctx, egui::Id::unique("cc_close_prompt")).show(ctx, |ui| { ui.set_width(360.0); ui.label(RichText::new(crate::tl!("CADCraft")).strong()); ui.add_space(4.0); diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index d3799dc36..a157a05aa 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -39,6 +39,7 @@ pub mod icons; pub mod layers; pub mod managers; pub mod menus; +pub mod modal; pub mod opening; pub mod options; pub mod palettes; diff --git a/crates/ui-egui/src/modal.rs b/crates/ui-egui/src/modal.rs new file mode 100644 index 000000000..10c2ea562 --- /dev/null +++ b/crates/ui-egui/src/modal.rs @@ -0,0 +1,14 @@ +//! Alerts that appear in the frame that asks for them. +//! +//! An `egui::Modal` normally shows a frame late and then fades in over `animation_time` +//! (0.2 s): its area's first frame only measures it and draws nothing, and the dim behind it +//! fades with it. An alert that answers a key press or menu choice should be there at once. + +/// A modal with no fade-in. On its first show it asks egui to run the pass again with the +/// measured size, so the first frame presented already draws it. +pub fn instant(ctx: &egui::Context, id: egui::Id) -> egui::Modal { + if egui::AreaState::load(ctx, id).is_none() { + ctx.request_discard("an instant modal's first show only measures it"); + } + egui::Modal::new(id).area(egui::Modal::default_area(id).fade_in(false)) +} diff --git a/crates/ui-egui/tests/it/instant_modals.rs b/crates/ui-egui/tests/it/instant_modals.rs new file mode 100644 index 000000000..7a7797803 --- /dev/null +++ b/crates/ui-egui/tests/it/instant_modals.rs @@ -0,0 +1,41 @@ +//! "Save changes?" is fully drawn, dim included, in the frame of the Close that asks it. + +use cadcraft_engine::Session; +use cadcraft_ui_egui::{CadApp, Services, menus}; +use egui::epaint::Shape; +use egui::vec2; +use egui_kittest::Harness; +use serde_json::json; + +/// The opacity of each painted text containing `needle`. +fn text_opacity(shapes: &[egui::epaint::ClippedShape], needle: &str) -> Vec { + fn walk(shape: &Shape, needle: &str, out: &mut Vec) { + match shape { + Shape::Vec(shapes) => shapes.iter().for_each(|s| walk(s, needle, out)), + Shape::Text(text) if text.galley.text().contains(needle) => out.push(text.opacity_factor), + _ => {} + } + } + let mut out = Vec::new(); + shapes.iter().for_each(|s| walk(&s.shape, needle, &mut out)); + out +} + +#[test] +fn close_prompt_is_fully_drawn_in_the_frame_of_the_menu_choice() { + let mut app = CadApp::new(Session::default(), Services::default()); + app.ui.start_tab = false; + let mut h = Harness::builder().with_size(vec2(1280.0, 800.0)).build_ui_state( + |ui, app: &mut CadApp| { + app.logic(&ui.ctx().clone()); + app.ui(ui); + }, + app, + ); + h.run_steps(5); + h.state_mut().run("line", json!({"points": [[0, 0], [10, 0]]})).unwrap(); + h.run_steps(2); + menus::activate(h.state_mut(), "close"); + h.step(); + assert_eq!(text_opacity(&h.output().shapes, "Save changes to"), [1.0]); +} diff --git a/crates/ui-egui/tests/it/main.rs b/crates/ui-egui/tests/it/main.rs index 2e2451579..5faf3135b 100644 --- a/crates/ui-egui/tests/it/main.rs +++ b/crates/ui-egui/tests/it/main.rs @@ -12,6 +12,7 @@ mod drafting_spacing; mod file_tab_titles; mod gpu_viewport_stencil; mod hidden_command_line; +mod instant_modals; mod lisp_files; mod measure_distance_snapping; mod menubar_hover;