diff --git a/ROADMAP.md b/ROADMAP.md index 89c9a55c..c7298f6f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -169,6 +169,7 @@ Newest first. - **2026-10-13** · An explicit recovery folder (`file.recovery.save` with `dir`) never overwrites or deletes another running app's checkpoints. Document uids start at 1 in every process, so two apps saving recovery to one chosen folder replaced or removed each other's `.designcraft`. A run replaces or removes only the entries it wrote or recovered there, refuses an occupied uid, and refuses a folder whose run lock another app holds. No readiness percentage change. - **2026-10-13** · Path edits (`path.setAnchor`, `path.moveAnchors`, `path.appendAnchor`, `path.reverse`, `path.close`, anchor add/delete/convert, Erase, Smooth and Scissors) refuse a path that is locked, sits in a locked group or on a locked layer (#735). Properties links to the Stroke, Character, Paragraph, Effects, Text Wrap and Info panels open them in a customized panel layout too (#736). No readiness percentage change. - **2026-10-13** · IDML export keeps parent item overrides: each page lists its overridden parent items and the local copy keeps its link to the parent item, so a reopened IDML file shows only the local copy (#725). No readiness percentage change. +- **2026-10-13** · macOS: a command chosen with the mouse in the menu bar runs at once. It used to wait for the next mouse move or key press over the window, so File › Open, Place and Save As seemed to take 10–20 seconds to open their dialogs. No readiness percentage change. - **2026-10-12** · macOS Dock Quit uses the same unsaved-document prompts as window and menu Quit. Repeated requests keep the current answers; Cancel retains every tab and unsaved recovery checkpoint, and completed Save/Don't Save answers exit through the ordinary event loop. No readiness percentage change. - **2026-10-12** · Text strokes can sit behind the fill: the stroke lies wholly outside the letters at its full weight on screen, in PNG and in PDF, set from the Properties panel or Character Style Options. IDML `StrokeAlignment` on text imports and exports as this setting, so imported text with outside-aligned strokes keeps its letter shapes (#619). No readiness percentage change. - **2026-10-12** · Placed Word documents keep their bullets and numbering (#626): `numbering.xml` levels become bullet or numbered paragraphs with Word's number formats, label text, start numbers, restarts and list indents, also when a paragraph style carries the list. No readiness percentage change. diff --git a/apps/designcraft/src/main.rs b/apps/designcraft/src/main.rs index e7b0b7fa..2f90f648 100644 --- a/apps/designcraft/src/main.rs +++ b/apps/designcraft/src/main.rs @@ -45,7 +45,7 @@ impl eframe::App for App { #[cfg(target_os = "macos")] { if self.menu.is_none() && std::env::var_os("DESIGNCRAFT_NO_NATIVE_MENU").is_none() { - self.menu = Some(native_menu::NativeMenu::install(&mut self.app)); + self.menu = Some(native_menu::NativeMenu::install(&mut self.app, ctx)); } if let Some(m) = &mut self.menu { m.poll(&mut self.app, ctx); diff --git a/apps/designcraft/src/native_menu.rs b/apps/designcraft/src/native_menu.rs index f51db197..f6e47525 100644 --- a/apps/designcraft/src/native_menu.rs +++ b/apps/designcraft/src/native_menu.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::str::FromStr; +use std::sync::mpsc::{Receiver, channel}; use designcraft_ui_egui::DesignApp; use designcraft_ui_egui::menus::{self, Item}; @@ -19,6 +20,7 @@ enum Handle { pub struct NativeMenu { _menu: Menu, items: HashMap, + events: Receiver, last_refresh: f64, } @@ -31,7 +33,15 @@ fn accel(sc: &str) -> Option { } impl NativeMenu { - pub fn install(app: &mut DesignApp) -> Self { + pub fn install(app: &mut DesignApp, ctx: &egui::Context) -> Self { + // A click in the menu bar reaches the app with no input over the window, so nothing would + // draw the frame that runs the command until the pointer or a key comes back: wake egui. + let (tx, events) = channel(); + let repaint = ctx.clone(); + MenuEvent::set_event_handler(Some(move |e: MenuEvent| { + let _ = tx.send(e); + repaint.request_repaint(); + })); let menu = Menu::new(); let mut items = HashMap::new(); let mut counter = 0usize; @@ -72,11 +82,11 @@ impl NativeMenu { (p.is_null() && accel(sc).is_some()).then(|| id.clone()) }) .collect(); - Self { _menu: menu, items, last_refresh: 0.0 } + Self { _menu: menu, items, events, last_refresh: 0.0 } } pub fn poll(&mut self, app: &mut DesignApp, ctx: &egui::Context) { - while let Ok(ev) = MenuEvent::receiver().try_recv() { + while let Ok(ev) = self.events.try_recv() { if let Some((cmd, params, _)) = self.items.get(ev.id.as_ref()) { menus::activate_native(app, ctx, &cmd.clone(), ¶ms.clone()); }