gpui: Allow selection of "Services" menu independent of menu title (#34115)
Release Notes: - N/A --- In the same vein as #29538, the "Services" menu on macOS depended on the text being exactly "Services", not allowing for i18n of the menu name. This PR introduces a new menu type called `OsMenu` that defines a special menu that can be populated by the system. Currently, it takes one enum value, `ServicesMenu` that tells the system to populate its contents with the items it would usually populate the "Services" menu with. An example of this being used has been implemented in the `set_menus` example: `cargo run -p gpui --example set_menus` --- Point to consider: In `mac/platform.rs:414` the existing code for setting the "Services" menu remains for backwards compatibility. Should this remain now that this new method exists to set the menu, or should it be removed? --------- Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
This commit is contained in:
parent
094e878ccf
commit
fa3d0aaed4
5 changed files with 89 additions and 12 deletions
|
@ -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),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue