From 4aa837e933656f3ab860fc252b6fc83da75b48b7 Mon Sep 17 00:00:00 2001 From: grovebit <337789029+grovebit@users.noreply.github.com> Date: Sat, 10 Oct 2026 13:38:23 +0100 Subject: [PATCH] Make Cmd+H heal and Cmd+Alt+H copy to send on macOS On macOS, Heal Separation (Cmd+H) and Copy to Send (Cmd+Alt+H) never ran (#140). The system menu bar used muda's predefined Hide and Hide Others items, which carry those keys, and macOS offers a key press to the menu bar's key equivalents before the window, so egui never saw them: Cmd+H hid SoundCraft and Cmd+Alt+H hid the other apps. Pro Tools uses the same keys for Heal Separation and Copy Automation to Send, so the keys stay and Hide loses its shortcut instead. Hide SoundCraft and Hide Others are now plain menu items that call NSApplication's hide: and hideOtherApplications:. These are safe objc2-app-kit calls, and both crates were already in the build through muda. Show All keeps muda's item, which has no shortcut. Windows and Linux have no such menu, so nothing changes there. --- Cargo.lock | 2 ++ apps/soundcraft/Cargo.toml | 2 ++ apps/soundcraft/src/native_menu.rs | 16 ++++++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b26758..2ea1d8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3409,6 +3409,8 @@ dependencies = [ "egui", "log", "muda", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", "rfd", "serde_json", "soundcraft-engine", diff --git a/apps/soundcraft/Cargo.toml b/apps/soundcraft/Cargo.toml index cb9dab1..783c7b5 100644 --- a/apps/soundcraft/Cargo.toml +++ b/apps/soundcraft/Cargo.toml @@ -28,3 +28,5 @@ winresource = "0.1" [target.'cfg(target_os = "macos")'.dependencies] muda = { version = "0.19.2", default-features = false } +objc2 = "0.6" +objc2-app-kit = { version = "0.3", default-features = false, features = ["NSApplication", "NSResponder"] } diff --git a/apps/soundcraft/src/native_menu.rs b/apps/soundcraft/src/native_menu.rs index 9cc72de..4bd085a 100644 --- a/apps/soundcraft/src/native_menu.rs +++ b/apps/soundcraft/src/native_menu.rs @@ -1,5 +1,7 @@ //! macOS system menu bar. All actions use the same menu dispatch as the egui host. -use muda::{CheckMenuItem, Menu, MenuEvent, PredefinedMenuItem, Submenu}; +use muda::{CheckMenuItem, Menu, MenuEvent, MenuItem, PredefinedMenuItem, Submenu}; +use objc2::MainThreadMarker; +use objc2_app_kit::NSApplication; use soundcraft_ui_egui::{SoundApp, menus}; use std::sync::mpsc::{Receiver, channel}; @@ -36,8 +38,9 @@ impl NativeMenu { app_menu.append(&PredefinedMenuItem::separator())?; app_menu.append(&PredefinedMenuItem::services(None))?; app_menu.append(&PredefinedMenuItem::separator())?; - app_menu.append(&PredefinedMenuItem::hide(Some("Hide SoundCraft")))?; - app_menu.append(&PredefinedMenuItem::hide_others(None))?; + // muda's own Hide items take ⌘H and ⌥⌘H, which are Heal Separation and Copy to Send. + app_menu.append(&MenuItem::with_id("hide", "Hide SoundCraft", true, None))?; + app_menu.append(&MenuItem::with_id("hide_others", "Hide Others", true, None))?; app_menu.append(&PredefinedMenuItem::show_all(None))?; app_menu.append(&PredefinedMenuItem::separator())?; native.add_item(app, &app_menu, "Quit SoundCraft", "", Some("app.quit".into()))?; @@ -82,7 +85,12 @@ impl NativeMenu { while let Ok(event) = self.events.try_recv() { // A click changes state (and Cocoa flips the item's check mark): sync on the next pass. self.last_sync = None; - if let Some(entry) = self.entries.iter().find(|e| e.item.id() == &event.id) + if (event.id == "hide" || event.id == "hide_others") + && let Some(mtm) = MainThreadMarker::new() + { + let ns = NSApplication::sharedApplication(mtm); + if event.id == "hide" { ns.hide(None) } else { ns.hideOtherApplications(None) } + } else if let Some(entry) = self.entries.iter().find(|e| e.item.id() == &event.id) && let Some(command) = &entry.command && menus::item_state(app, &entry.path, Some(command)).0 {