fix(macos): a menu-bar command runs at once instead of on the next input (#784)

This commit is contained in:
aiongg
2026-10-13 14:51:52 +00:00
parent e862e0a613
commit 368958ee2f
3 changed files with 15 additions and 4 deletions
+1
View File
@@ -129,6 +129,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m
Newest first.
- **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** · Orderly native exit writes the latest recovery state after finalizing current quit approvals; incomplete or stale plans keep their documents, and Undo to the saved state clears stale recovery. Unavailable or failed recovery is reported. Cancelable macOS Dock Quit remains open (#544). No readiness percentage change.
- **2026-10-12** · Recovered documents keep a checkpoint until saved, closed or reverted, including a second crash before autosave. Restarted document IDs and explicit recovery folders preserve other snapshots; failed republishing retains the original. No readiness percentage change.
+1 -1
View File
@@ -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);
+13 -3
View File
@@ -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<String, (String, Value, Handle)>,
events: Receiver<MenuEvent>,
last_refresh: f64,
}
@@ -31,7 +33,15 @@ fn accel(sc: &str) -> Option<Accelerator> {
}
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(), &params.clone());
}