Detailed changes
@@ -1,5 +1,6 @@
use gpui::{
- App, Application, Context, Menu, MenuItem, Window, WindowOptions, actions, div, prelude::*, rgb,
+ App, Application, Context, Menu, MenuItem, SystemMenuType, Window, WindowOptions, actions, div,
+ prelude::*, rgb,
};
struct SetMenus;
@@ -27,7 +28,11 @@ fn main() {
// Add menu items
cx.set_menus(vec![Menu {
name: "set_menus".into(),
- items: vec![MenuItem::action("Quit", Quit)],
+ items: vec![
+ MenuItem::os_submenu("Services", SystemMenuType::Services),
+ MenuItem::separator(),
+ MenuItem::action("Quit", Quit),
+ ],
}]);
cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_| SetMenus {}))
.unwrap();
@@ -20,6 +20,34 @@ impl Menu {
}
}
+/// OS menus are menus that are recognized by the operating system
+/// This allows the operating system to provide specialized items for
+/// these menus
+pub struct OsMenu {
+ /// The name of the menu
+ pub name: SharedString,
+
+ /// The type of menu
+ pub menu_type: SystemMenuType,
+}
+
+impl OsMenu {
+ /// Create an OwnedOsMenu from this OsMenu
+ pub fn owned(self) -> OwnedOsMenu {
+ OwnedOsMenu {
+ name: self.name.to_string().into(),
+ menu_type: self.menu_type,
+ }
+ }
+}
+
+/// The type of system menu
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub enum SystemMenuType {
+ /// The 'Services' menu in the Application menu on macOS
+ Services,
+}
+
/// The different kinds of items that can be in a menu
pub enum MenuItem {
/// A separator between items
@@ -28,6 +56,9 @@ pub enum MenuItem {
/// A submenu
Submenu(Menu),
+ /// A menu, managed by the system (for example, the Services menu on macOS)
+ SystemMenu(OsMenu),
+
/// An action that can be performed
Action {
/// The name of this menu item
@@ -53,6 +84,14 @@ impl MenuItem {
Self::Submenu(menu)
}
+ /// Creates a new submenu that is populated by the OS
+ pub fn os_submenu(name: impl Into<SharedString>, menu_type: SystemMenuType) -> Self {
+ Self::SystemMenu(OsMenu {
+ name: name.into(),
+ menu_type,
+ })
+ }
+
/// Creates a new menu item that invokes an action
pub fn action(name: impl Into<SharedString>, action: impl Action) -> Self {
Self::Action {
@@ -89,10 +128,23 @@ impl MenuItem {
action,
os_action,
},
+ MenuItem::SystemMenu(os_menu) => OwnedMenuItem::SystemMenu(os_menu.owned()),
}
}
}
+/// OS menus are menus that are recognized by the operating system
+/// This allows the operating system to provide specialized items for
+/// these menus
+#[derive(Clone)]
+pub struct OwnedOsMenu {
+ /// The name of the menu
+ pub name: SharedString,
+
+ /// The type of menu
+ pub menu_type: SystemMenuType,
+}
+
/// A menu of the application, either a main menu or a submenu
#[derive(Clone)]
pub struct OwnedMenu {
@@ -111,6 +163,9 @@ pub enum OwnedMenuItem {
/// A submenu
Submenu(OwnedMenu),
+ /// A menu, managed by the system (for example, the Services menu on macOS)
+ SystemMenu(OwnedOsMenu),
+
/// An action that can be performed
Action {
/// The name of this menu item
@@ -139,6 +194,7 @@ impl Clone for OwnedMenuItem {
action: action.boxed_clone(),
os_action: *os_action,
},
+ OwnedMenuItem::SystemMenu(os_menu) => OwnedMenuItem::SystemMenu(os_menu.clone()),
}
}
}
@@ -7,9 +7,9 @@ use super::{
use crate::{
Action, AnyWindowHandle, BackgroundExecutor, ClipboardEntry, ClipboardItem, ClipboardString,
CursorStyle, ForegroundExecutor, Image, ImageFormat, KeyContext, Keymap, MacDispatcher,
- MacDisplay, MacWindow, Menu, MenuItem, OwnedMenu, PathPromptOptions, Platform, PlatformDisplay,
- PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task,
- WindowAppearance, WindowParams, hash,
+ MacDisplay, MacWindow, Menu, MenuItem, OsMenu, OwnedMenu, PathPromptOptions, Platform,
+ PlatformDisplay, PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow, Result,
+ SemanticVersion, SystemMenuType, Task, WindowAppearance, WindowParams, hash,
};
use anyhow::{Context as _, anyhow};
use block::ConcreteBlock;
@@ -413,9 +413,20 @@ impl MacPlatform {
}
item.setSubmenu_(submenu);
item.setTitle_(ns_string(&name));
- if name == "Services" {
- let app: id = msg_send![APP_CLASS, sharedApplication];
- app.setServicesMenu_(item);
+ item
+ }
+ MenuItem::SystemMenu(OsMenu { name, menu_type }) => {
+ let item = NSMenuItem::new(nil).autorelease();
+ let submenu = NSMenu::new(nil).autorelease();
+ submenu.setDelegate_(delegate);
+ item.setSubmenu_(submenu);
+ item.setTitle_(ns_string(&name));
+
+ match menu_type {
+ SystemMenuType::Services => {
+ let app: id = msg_send![APP_CLASS, sharedApplication];
+ app.setServicesMenu_(item);
+ }
}
item
@@ -121,8 +121,16 @@ impl ApplicationMenu {
menu.action(name, action)
}
OwnedMenuItem::Submenu(_) => menu,
+ OwnedMenuItem::SystemMenu(_) => {
+ // A system menu doesn't make sense in this context, so ignore it
+ menu
+ }
})
}
+ OwnedMenuItem::SystemMenu(_) => {
+ // A system menu doesn't make sense in this context, so ignore it
+ menu
+ }
})
})
}
@@ -35,10 +35,7 @@ pub fn app_menus() -> Vec<Menu> {
],
}),
MenuItem::separator(),
- MenuItem::submenu(Menu {
- name: "Services".into(),
- items: vec![],
- }),
+ MenuItem::os_submenu("Services", gpui::SystemMenuType::Services),
MenuItem::separator(),
MenuItem::action("Extensions", zed_actions::Extensions::default()),
MenuItem::action("Install CLI", install_cli::Install),