diff --git a/crates/gpui/examples/set_menus.rs b/crates/gpui/examples/set_menus.rs index 8a97a8d8a2..e0e360b53c 100644 --- a/crates/gpui/examples/set_menus.rs +++ b/crates/gpui/examples/set_menus.rs @@ -26,14 +26,20 @@ fn main() { // Register the `quit` function so it can be referenced by the `MenuItem::action` in the menu bar cx.on_action(quit); // Add menu items - cx.set_menus(vec![Menu { - name: "set_menus".into(), - items: vec![ - MenuItem::os_submenu("Services", SystemMenuType::Services), - MenuItem::separator(), - MenuItem::action("Quit", Quit), - ], - }]); + cx.set_menus(vec![ + Menu { + name: "set_menus".into(), + items: vec![ + MenuItem::os_submenu("Services", SystemMenuType::Services), + MenuItem::separator(), + MenuItem::action("Quit", Quit), + ], + }, + Menu { + name: "Window".into(), + items: vec![MenuItem::os_submenu("", SystemMenuType::Window)], + }, + ]); cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| SetMenus {})) .unwrap(); }); diff --git a/crates/gpui/src/platform/app_menu.rs b/crates/gpui/src/platform/app_menu.rs index 4069fee726..b6c3af5c6a 100644 --- a/crates/gpui/src/platform/app_menu.rs +++ b/crates/gpui/src/platform/app_menu.rs @@ -46,6 +46,15 @@ impl OsMenu { pub enum SystemMenuType { /// The 'Services' menu in the Application menu on macOS Services, + + /// The 'Window' menu on macOS. + /// + /// This item is a marker and will not display as a menu item. + /// If a menu begins with this item, it will be treated as the Window menu. + /// + /// macOS will add window management items to this menu, such as + /// 'Fill', 'Center', 'Move to [Display]', and a list of windows. + Window, } /// The different kinds of items that can be in a menu diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 832550dc46..0599538fa1 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -243,24 +243,29 @@ impl MacPlatform { menu.setTitle_(menu_title); menu.setDelegate_(delegate); + // Check the first item to see if it is window menu + if let Some(i) = menu_config.items.first() + && matches!( + i, + MenuItem::SystemMenu(OsMenu { + menu_type: SystemMenuType::Window, + .. + }) + ) + { + let app: id = msg_send![APP_CLASS, sharedApplication]; + app.setWindowsMenu_(menu); + }; + for item_config in &menu_config.items { - menu.addItem_(Self::create_menu_item( - item_config, - delegate, - actions, - keymap, - )); + Self::create_menu_item(item_config, delegate, actions, keymap) + .map(|item| menu.addItem_(item)); } let menu_item = NSMenuItem::new(nil).autorelease(); menu_item.setTitle_(menu_title); menu_item.setSubmenu_(menu); application_menu.addItem_(menu_item); - - if menu_config.name == "Window" { - let app: id = msg_send![APP_CLASS, sharedApplication]; - app.setWindowsMenu_(menu); - } } application_menu @@ -278,12 +283,8 @@ impl MacPlatform { let dock_menu = NSMenu::new(nil); dock_menu.setDelegate_(delegate); for item_config in menu_items { - dock_menu.addItem_(Self::create_menu_item( - &item_config, - delegate, - actions, - keymap, - )); + Self::create_menu_item(&item_config, delegate, actions, keymap) + .map(|item| dock_menu.addItem_(item)); } dock_menu @@ -295,12 +296,12 @@ impl MacPlatform { delegate: id, actions: &mut Vec>, keymap: &Keymap, - ) -> id { + ) -> Option { static DEFAULT_CONTEXT: OnceLock> = OnceLock::new(); unsafe { match item { - MenuItem::Separator => NSMenuItem::separatorItem(nil), + MenuItem::Separator => Some(NSMenuItem::separatorItem(nil)), MenuItem::Action { name, action, @@ -402,18 +403,19 @@ impl MacPlatform { let tag = actions.len() as NSInteger; let _: () = msg_send![item, setTag: tag]; actions.push(action.boxed_clone()); - item + Some(item) } MenuItem::Submenu(Menu { name, items }) => { let item = NSMenuItem::new(nil).autorelease(); let submenu = NSMenu::new(nil).autorelease(); submenu.setDelegate_(delegate); for item in items { - submenu.addItem_(Self::create_menu_item(item, delegate, actions, keymap)); + Self::create_menu_item(item, delegate, actions, keymap) + .map(|item| submenu.addItem_(item)); } item.setSubmenu_(submenu); item.setTitle_(ns_string(name)); - item + Some(item) } MenuItem::SystemMenu(OsMenu { name, menu_type }) => { let item = NSMenuItem::new(nil).autorelease(); @@ -427,9 +429,12 @@ impl MacPlatform { let app: id = msg_send![APP_CLASS, sharedApplication]; app.setServicesMenu_(item); } + SystemMenuType::Window => { + return None; + } } - item + Some(item) } } } diff --git a/crates/zed/src/zed/app_menus.rs b/crates/zed/src/zed/app_menus.rs index 6c7ab0b374..3ea60150d6 100644 --- a/crates/zed/src/zed/app_menus.rs +++ b/crates/zed/src/zed/app_menus.rs @@ -236,6 +236,9 @@ pub fn app_menus() -> Vec { Menu { name: "Window".into(), items: vec![ + #[cfg(target_os = "macos")] + MenuItem::os_submenu("", gpui::SystemMenuType::Window), + MenuItem::separator(), MenuItem::action("Minimize", super::Minimize), MenuItem::action("Zoom", super::Zoom), MenuItem::separator(),