gpui: improve the window menu on macOS

This commit is contained in:
Chuqiao Feng 2025-08-23 22:16:34 +08:00
parent 70575d1115
commit b724431256
No known key found for this signature in database
GPG key ID: 817C351E0A1EC12E
4 changed files with 54 additions and 31 deletions

View file

@ -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();
});

View file

@ -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

View file

@ -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<Box<dyn Action>>,
keymap: &Keymap,
) -> id {
) -> Option<id> {
static DEFAULT_CONTEXT: OnceLock<Vec<KeyContext>> = 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)
}
}
}

View file

@ -236,6 +236,9 @@ pub fn app_menus() -> Vec<Menu> {
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(),