diff --git a/ROADMAP.md b/ROADMAP.md index d60a6bf49..5df96b8e3 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -347,6 +347,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m Newest first. +- 2026-10-15: Double-clicking the status bar's preflight count or its light shows the Preflight panel, as in InDesign. - **2026-10-14** · The Type tool draws a new text frame over a locked empty frame, as InDesign does, instead of turning the locked frame into a text frame. No readiness percentage change. - **2026-10-14** · 100% black shows as pure black on screen by default, as InDesign's Appearance of Black does out of the box (Display All Blacks as Rich Black); Preferences › Appearance of Black › Display All Blacks Accurately still shows it as the dark grey it prints, and a saved choice is kept. No readiness percentage change. - **2026-10-14** · Pathfinder keeps the frontmost object, as InDesign does: a photo placed over shapes and intersected with them shows through the shapes (it was dropped, and the shapes kept their black fill); Add, Exclude Overlap and Minus Back take the front object's fill too, Subtract still keeps the back one. No readiness percentage change. diff --git a/crates/ui-egui/src/chrome.rs b/crates/ui-egui/src/chrome.rs index 5855b3f9a..9b59f1834 100644 --- a/crates/ui-egui/src/chrome.rs +++ b/crates/ui-egui/src/chrome.rs @@ -703,7 +703,7 @@ pub fn status_bar(app: &mut DesignApp, ui: &mut egui::Ui) { ui.add_space(8.0); // Preflight: overset text is an error. let errors = panels::preflight_errors(app); - let (r, _) = ui.allocate_exact_size(vec2(9.0, 9.0), Sense::hover()); + let (r, dot) = ui.allocate_exact_size(vec2(9.0, 9.0), Sense::click()); ui.painter().circle_filled( r.center(), 4.0, @@ -718,12 +718,17 @@ pub fn status_bar(app: &mut DesignApp, ui: &mut egui::Ui) { ) }; // As in InDesign, the count opens a menu that leads to the Preflight panel. - crate::menus::menu_button(ui, egui::RichText::new(count).font(small.clone()), |ui| { + let menu = crate::menus::menu_button(ui, egui::RichText::new(count).font(small.clone()), |ui| { if ui.button(crate::i18n::tr(&app.ui.language, "Preflight Panel…")).clicked() { let _ = app.run("window.panel", json!({"panel": "preflight"})); ui.close(); } }); + // Double-clicking the light or the count shows the panel straight away (the + // second click has closed the menu the first one opened). + if dot.double_clicked() || menu.response.double_clicked() { + let _ = app.run("window.panel", json!({"panel": "preflight"})); + } ui.add_space(12.0); ui.label(egui::RichText::new(&app.ui.status).font(small).color(t.text_dim)); }); @@ -914,6 +919,22 @@ mod tests { h.get_by_label("Preflight"); } + /// As in InDesign, double-clicking the preflight count (or its light) shows the panel. + #[test] + fn double_clicking_the_status_bar_error_count_opens_the_preflight_panel() { + let mut app = app(); + app.run("file.new", serde_json::json!({})).unwrap(); + app.ui.dock_tab = "properties".into(); + let mut h = test_window::open_stepped(app, vec2(1200.0, 700.0), 1.0, 1.0 / 60.0); + let count = h.get_by_label("No errors ▾"); + count.click(); + count.click(); + h.run_steps(3); + assert_eq!(h.state().app.ui.open_panel.as_deref(), Some("preflight")); + h.get_by_label("Preflight"); + assert!(h.query_by_label("Preflight Panel…").is_none(), "the menu isn't left open"); + } + /// A very long document name made its tab grow past the window, hiding the close button /// and the tabs after it (#603). #[test] diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 59c7ebb59..30dfd78a0 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -2300,22 +2300,29 @@ pub(crate) mod test_window { } /// The window at a size in points and a scale, with a new document open. - pub fn open_scaled(mut app: crate::DesignApp, size: egui::Vec2, pixels_per_point: f32) -> Harness<'static, Window> { + pub fn open_scaled(app: crate::DesignApp, size: egui::Vec2, pixels_per_point: f32) -> Harness<'static, Window> { + open_stepped(app, size, pixels_per_point, 0.25) + } + + /// As `open_scaled`, with `step_dt` seconds between frames (a double-click needs its two + /// clicks closer together than the default quarter second). + pub fn open_stepped(mut app: crate::DesignApp, size: egui::Vec2, pixels_per_point: f32, step_dt: f32) -> Harness<'static, Window> { if app.session.active().is_none() { app.run("file.new", serde_json::json!({})).unwrap(); } - let mut h = Harness::builder().with_size(size).with_pixels_per_point(pixels_per_point).with_max_steps(1000).build_ui_state( - |ui, w: &mut Window| { - // The builder runs a frame before the texture size can be raised: skip it. - if !w.ready { - return; - } - let ctx = ui.ctx().clone(); - w.app.logic(&ctx); - w.app.ui(ui); - }, - Window { app, ready: false }, - ); + let mut h = + Harness::builder().with_size(size).with_pixels_per_point(pixels_per_point).with_max_steps(1000).with_step_dt(step_dt).build_ui_state( + |ui, w: &mut Window| { + // The builder runs a frame before the texture size can be raised: skip it. + if !w.ready { + return; + } + let ctx = ui.ctx().clone(); + w.app.logic(&ctx); + w.app.ui(ui); + }, + Window { app, ready: false }, + ); h.input_mut().max_texture_side = Some(8192); h.state_mut().ready = true; h.run_steps(6);