From 71d8ead31820e197f39c7304ad1db0402bc00077 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 22 Feb 2023 09:02:31 -0800 Subject: [PATCH 1/2] Introduce an OSAction that can be associated with menu items for mac platform compatibility. Co-authored-by: Antonio --- crates/gpui/src/app/menu.rs | 37 ++ crates/gpui/src/platform/mac/platform.rs | 3 +- crates/live_kit_client/examples/test_app.rs | 1 + crates/zed/src/menus.rs | 420 +++++--------------- 4 files changed, 148 insertions(+), 313 deletions(-) diff --git a/crates/gpui/src/app/menu.rs b/crates/gpui/src/app/menu.rs index 2234bfa391..5fe307a7c1 100644 --- a/crates/gpui/src/app/menu.rs +++ b/crates/gpui/src/app/menu.rs @@ -11,9 +11,46 @@ pub enum MenuItem<'a> { Action { name: &'a str, action: Box, + os_action: Option, }, } +impl<'a> MenuItem<'a> { + pub fn separator() -> Self { + Self::Separator + } + + pub fn submenu(menu: Menu<'a>) -> Self { + Self::Submenu(menu) + } + + pub fn action(name: &'a str, action: impl Action) -> Self { + Self::Action { + name, + action: Box::new(action), + os_action: None, + } + } + + pub fn os_action(name: &'a str, action: impl Action, os_action: OsAction) -> Self { + Self::Action { + name, + action: Box::new(action), + os_action: Some(os_action), + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum OsAction { + Cut, + Copy, + Paste, + SelectAll, + Undo, + Redo, +} + impl MutableAppContext { pub fn set_menus(&mut self, menus: Vec) { self.foreground_platform diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 7e3f92a04c..93a1c39a33 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -193,7 +193,8 @@ impl MacForegroundPlatform { ) -> id { match item { MenuItem::Separator => NSMenuItem::separatorItem(nil), - MenuItem::Action { name, action } => { + MenuItem::Action { name, action, .. } => { + // TODO let keystrokes = keystroke_matcher .bindings_for_action_type(action.as_any().type_id()) .find(|binding| binding.action().eq(action.as_ref())) diff --git a/crates/live_kit_client/examples/test_app.rs b/crates/live_kit_client/examples/test_app.rs index a92b106d33..14332b3e06 100644 --- a/crates/live_kit_client/examples/test_app.rs +++ b/crates/live_kit_client/examples/test_app.rs @@ -20,6 +20,7 @@ fn main() { items: vec![MenuItem::Action { name: "Quit", action: Box::new(Quit), + os_action: None, }], }]); diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index 52ca7d2324..bb519c7a95 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -1,4 +1,4 @@ -use gpui::{Menu, MenuItem}; +use gpui::{Menu, MenuItem, OsAction}; #[cfg(target_os = "macos")] pub fn menus() -> Vec> { @@ -6,363 +6,159 @@ pub fn menus() -> Vec> { Menu { name: "Zed", items: vec![ - MenuItem::Action { - name: "About Zed…", - action: Box::new(super::About), - }, - MenuItem::Action { - name: "Check for Updates", - action: Box::new(auto_update::Check), - }, - MenuItem::Separator, - MenuItem::Submenu(Menu { + MenuItem::action("About Zed…", super::About), + MenuItem::action("Check for Updates", auto_update::Check), + MenuItem::separator(), + MenuItem::submenu(Menu { name: "Preferences", items: vec![ - MenuItem::Action { - name: "Open Settings", - action: Box::new(super::OpenSettings), - }, - MenuItem::Action { - name: "Open Key Bindings", - action: Box::new(super::OpenKeymap), - }, - MenuItem::Action { - name: "Open Default Settings", - action: Box::new(super::OpenDefaultSettings), - }, - MenuItem::Action { - name: "Open Default Key Bindings", - action: Box::new(super::OpenDefaultKeymap), - }, - MenuItem::Action { - name: "Select Theme", - action: Box::new(theme_selector::Toggle), - }, + MenuItem::action("Open Settings", super::OpenSettings), + MenuItem::action("Open Key Bindings", super::OpenKeymap), + MenuItem::action("Open Default Settings", super::OpenDefaultSettings), + MenuItem::action("Open Default Key Bindings", super::OpenDefaultKeymap), + MenuItem::action("Select Theme", theme_selector::Toggle), ], }), - MenuItem::Action { - name: "Install CLI", - action: Box::new(super::InstallCommandLineInterface), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Hide Zed", - action: Box::new(super::Hide), - }, - MenuItem::Action { - name: "Hide Others", - action: Box::new(super::HideOthers), - }, - MenuItem::Action { - name: "Show All", - action: Box::new(super::ShowAll), - }, - MenuItem::Action { - name: "Quit", - action: Box::new(super::Quit), - }, + MenuItem::action("Install CLI", super::InstallCommandLineInterface), + MenuItem::separator(), + MenuItem::action("Hide Zed", super::Hide), + MenuItem::action("Hide Others", super::HideOthers), + MenuItem::action("Show All", super::ShowAll), + MenuItem::action("Quit", super::Quit), ], }, Menu { name: "File", items: vec![ - MenuItem::Action { - name: "New", - action: Box::new(workspace::NewFile), - }, - MenuItem::Action { - name: "New Window", - action: Box::new(workspace::NewWindow), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Open…", - action: Box::new(workspace::Open), - }, - MenuItem::Action { - name: "Open Recent...", - action: Box::new(recent_projects::OpenRecent), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Add Folder to Project…", - action: Box::new(workspace::AddFolderToProject), - }, - MenuItem::Action { - name: "Save", - action: Box::new(workspace::Save), - }, - MenuItem::Action { - name: "Save As…", - action: Box::new(workspace::SaveAs), - }, - MenuItem::Action { - name: "Save All", - action: Box::new(workspace::SaveAll), - }, - MenuItem::Action { - name: "Close Editor", - action: Box::new(workspace::CloseActiveItem), - }, - MenuItem::Action { - name: "Close Window", - action: Box::new(workspace::CloseWindow), - }, + MenuItem::action("New", workspace::NewFile), + MenuItem::action("New Window", workspace::NewWindow), + MenuItem::separator(), + MenuItem::action("Open…", workspace::Open), + MenuItem::action("Open Recent...", recent_projects::OpenRecent), + MenuItem::separator(), + MenuItem::action("Add Folder to Project…", workspace::AddFolderToProject), + MenuItem::action("Save", workspace::Save), + MenuItem::action("Save As…", workspace::SaveAs), + MenuItem::action("Save All", workspace::SaveAll), + MenuItem::action("Close Editor", workspace::CloseActiveItem), + MenuItem::action("Close Window", workspace::CloseWindow), ], }, Menu { name: "Edit", items: vec![ - MenuItem::Action { - name: "Undo", - action: Box::new(editor::Undo), - }, - MenuItem::Action { - name: "Redo", - action: Box::new(editor::Redo), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Cut", - action: Box::new(editor::Cut), - }, - MenuItem::Action { - name: "Copy", - action: Box::new(editor::Copy), - }, - MenuItem::Action { - name: "Paste", - action: Box::new(editor::Paste), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Find", - action: Box::new(search::buffer_search::Deploy { focus: true }), - }, - MenuItem::Action { - name: "Find In Project", - action: Box::new(workspace::NewSearch), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Toggle Line Comment", - action: Box::new(editor::ToggleComments::default()), - }, - MenuItem::Action { - name: "Emoji & Symbols", - action: Box::new(editor::ShowCharacterPalette), - }, + MenuItem::os_action("Undo", editor::Undo, OsAction::Undo), + MenuItem::os_action("Redo", editor::Redo, OsAction::Redo), + MenuItem::separator(), + MenuItem::os_action("Cut", editor::Cut, OsAction::Cut), + MenuItem::os_action("Copy", editor::Copy, OsAction::Copy), + MenuItem::os_action("Paste", editor::Paste, OsAction::Paste), + MenuItem::separator(), + MenuItem::action("Find", search::buffer_search::Deploy { focus: true }), + MenuItem::action("Find In Project", workspace::NewSearch), + MenuItem::separator(), + MenuItem::action("Toggle Line Comment", editor::ToggleComments::default()), + MenuItem::action("Emoji & Symbols", editor::ShowCharacterPalette), ], }, Menu { name: "Selection", items: vec![ - MenuItem::Action { - name: "Select All", - action: Box::new(editor::SelectAll), - }, - MenuItem::Action { - name: "Expand Selection", - action: Box::new(editor::SelectLargerSyntaxNode), - }, - MenuItem::Action { - name: "Shrink Selection", - action: Box::new(editor::SelectSmallerSyntaxNode), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Add Cursor Above", - action: Box::new(editor::AddSelectionAbove), - }, - MenuItem::Action { - name: "Add Cursor Below", - action: Box::new(editor::AddSelectionBelow), - }, - MenuItem::Action { - name: "Select Next Occurrence", - action: Box::new(editor::SelectNext { + MenuItem::os_action("Select All", editor::SelectAll, OsAction::SelectAll), + MenuItem::action("Expand Selection", editor::SelectLargerSyntaxNode), + MenuItem::action("Shrink Selection", editor::SelectSmallerSyntaxNode), + MenuItem::separator(), + MenuItem::action("Add Cursor Above", editor::AddSelectionAbove), + MenuItem::action("Add Cursor Below", editor::AddSelectionBelow), + MenuItem::action( + "Select Next Occurrence", + editor::SelectNext { replace_newest: false, - }), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Move Line Up", - action: Box::new(editor::MoveLineUp), - }, - MenuItem::Action { - name: "Move Line Down", - action: Box::new(editor::MoveLineDown), - }, - MenuItem::Action { - name: "Duplicate Selection", - action: Box::new(editor::DuplicateLine), - }, + }, + ), + MenuItem::separator(), + MenuItem::action("Move Line Up", editor::MoveLineUp), + MenuItem::action("Move Line Down", editor::MoveLineDown), + MenuItem::action("Duplicate Selection", editor::DuplicateLine), ], }, Menu { name: "View", items: vec![ - MenuItem::Action { - name: "Zoom In", - action: Box::new(super::IncreaseBufferFontSize), - }, - MenuItem::Action { - name: "Zoom Out", - action: Box::new(super::DecreaseBufferFontSize), - }, - MenuItem::Action { - name: "Reset Zoom", - action: Box::new(super::ResetBufferFontSize), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Toggle Left Sidebar", - action: Box::new(workspace::ToggleLeftSidebar), - }, - MenuItem::Submenu(Menu { + MenuItem::action("Zoom In", super::IncreaseBufferFontSize), + MenuItem::action("Zoom Out", super::DecreaseBufferFontSize), + MenuItem::action("Reset Zoom", super::ResetBufferFontSize), + MenuItem::separator(), + MenuItem::action("Toggle Left Sidebar", workspace::ToggleLeftSidebar), + MenuItem::submenu(Menu { name: "Editor Layout", items: vec![ - MenuItem::Action { - name: "Split Up", - action: Box::new(workspace::SplitUp), - }, - MenuItem::Action { - name: "Split Down", - action: Box::new(workspace::SplitDown), - }, - MenuItem::Action { - name: "Split Left", - action: Box::new(workspace::SplitLeft), - }, - MenuItem::Action { - name: "Split Right", - action: Box::new(workspace::SplitRight), - }, + MenuItem::action("Split Up", workspace::SplitUp), + MenuItem::action("Split Down", workspace::SplitDown), + MenuItem::action("Split Left", workspace::SplitLeft), + MenuItem::action("Split Right", workspace::SplitRight), ], }), - MenuItem::Separator, - MenuItem::Action { - name: "Project Panel", - action: Box::new(project_panel::ToggleFocus), - }, - MenuItem::Action { - name: "Command Palette", - action: Box::new(command_palette::Toggle), - }, - MenuItem::Action { - name: "Diagnostics", - action: Box::new(diagnostics::Deploy), - }, - MenuItem::Separator, + MenuItem::separator(), + MenuItem::action("Project Panel", project_panel::ToggleFocus), + MenuItem::action("Command Palette", command_palette::Toggle), + MenuItem::action("Diagnostics", diagnostics::Deploy), + MenuItem::separator(), ], }, Menu { name: "Go", items: vec![ - MenuItem::Action { - name: "Back", - action: Box::new(workspace::GoBack { pane: None }), - }, - MenuItem::Action { - name: "Forward", - action: Box::new(workspace::GoForward { pane: None }), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Go to File", - action: Box::new(file_finder::Toggle), - }, - MenuItem::Action { - name: "Go to Symbol in Project", - action: Box::new(project_symbols::Toggle), - }, - MenuItem::Action { - name: "Go to Symbol in Editor", - action: Box::new(outline::Toggle), - }, - MenuItem::Action { - name: "Go to Definition", - action: Box::new(editor::GoToDefinition), - }, - MenuItem::Action { - name: "Go to Type Definition", - action: Box::new(editor::GoToTypeDefinition), - }, - MenuItem::Action { - name: "Find All References", - action: Box::new(editor::FindAllReferences), - }, - MenuItem::Action { - name: "Go to Line/Column", - action: Box::new(go_to_line::Toggle), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Next Problem", - action: Box::new(editor::GoToDiagnostic), - }, - MenuItem::Action { - name: "Previous Problem", - action: Box::new(editor::GoToPrevDiagnostic), - }, + MenuItem::action("Back", workspace::GoBack { pane: None }), + MenuItem::action("Forward", workspace::GoForward { pane: None }), + MenuItem::separator(), + MenuItem::action("Go to File", file_finder::Toggle), + MenuItem::action("Go to Symbol in Project", project_symbols::Toggle), + MenuItem::action("Go to Symbol in Editor", outline::Toggle), + MenuItem::action("Go to Definition", editor::GoToDefinition), + MenuItem::action("Go to Type Definition", editor::GoToTypeDefinition), + MenuItem::action("Find All References", editor::FindAllReferences), + MenuItem::action("Go to Line/Column", go_to_line::Toggle), + MenuItem::separator(), + MenuItem::action("Next Problem", editor::GoToDiagnostic), + MenuItem::action("Previous Problem", editor::GoToPrevDiagnostic), ], }, Menu { name: "Window", items: vec![ - MenuItem::Action { - name: "Minimize", - action: Box::new(super::Minimize), - }, - MenuItem::Action { - name: "Zoom", - action: Box::new(super::Zoom), - }, - MenuItem::Separator, + MenuItem::action("Minimize", super::Minimize), + MenuItem::action("Zoom", super::Zoom), + MenuItem::separator(), ], }, Menu { name: "Help", items: vec![ - MenuItem::Action { - name: "Command Palette", - action: Box::new(command_palette::Toggle), - }, - MenuItem::Separator, - MenuItem::Action { - name: "View Telemetry Log", - action: Box::new(crate::OpenTelemetryLog), - }, - MenuItem::Action { - name: "View Dependency Licenses", - action: Box::new(crate::OpenLicenses), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Copy System Specs Into Clipboard", - action: Box::new(feedback::CopySystemSpecsIntoClipboard), - }, - MenuItem::Action { - name: "File Bug Report", - action: Box::new(feedback::FileBugReport), - }, - MenuItem::Action { - name: "Request Feature", - action: Box::new(feedback::RequestFeature), - }, - MenuItem::Separator, - MenuItem::Action { - name: "Documentation", - action: Box::new(crate::OpenBrowser { + MenuItem::action("Command Palette", command_palette::Toggle), + MenuItem::separator(), + MenuItem::action("View Telemetry Log", crate::OpenTelemetryLog), + MenuItem::action("View Dependency Licenses", crate::OpenLicenses), + MenuItem::separator(), + MenuItem::action( + "Copy System Specs Into Clipboard", + feedback::CopySystemSpecsIntoClipboard, + ), + MenuItem::action("File Bug Report", feedback::FileBugReport), + MenuItem::action("Request Feature", feedback::RequestFeature), + MenuItem::separator(), + MenuItem::action( + "Documentation", + crate::OpenBrowser { url: "https://zed.dev/docs".into(), - }), - }, - MenuItem::Action { - name: "Zed Twitter", - action: Box::new(crate::OpenBrowser { + }, + ), + MenuItem::action( + "Zed Twitter", + crate::OpenBrowser { url: "https://twitter.com/zeddotdev".into(), - }), - }, + }, + ), ], }, ] From 4798b72cb89842c62dc3054b4def167fc3fa31e4 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 22 Feb 2023 09:10:16 -0800 Subject: [PATCH 2/2] Fixed keyboard shortcuts in mac os native panels co-authored-by: Antonio --- crates/gpui/src/platform/mac/platform.rs | 46 +++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 93a1c39a33..aeec02c8ca 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -98,6 +98,31 @@ unsafe fn build_classes() { sel!(handleGPUIMenuItem:), handle_menu_item as extern "C" fn(&mut Object, Sel, id), ); + // Add menu item handlers so that OS save panels have the correct key commands + decl.add_method( + sel!(cut:), + handle_menu_item as extern "C" fn(&mut Object, Sel, id), + ); + decl.add_method( + sel!(copy:), + handle_menu_item as extern "C" fn(&mut Object, Sel, id), + ); + decl.add_method( + sel!(paste:), + handle_menu_item as extern "C" fn(&mut Object, Sel, id), + ); + decl.add_method( + sel!(selectAll:), + handle_menu_item as extern "C" fn(&mut Object, Sel, id), + ); + decl.add_method( + sel!(undo:), + handle_menu_item as extern "C" fn(&mut Object, Sel, id), + ); + decl.add_method( + sel!(redo:), + handle_menu_item as extern "C" fn(&mut Object, Sel, id), + ); decl.add_method( sel!(validateMenuItem:), validate_menu_item as extern "C" fn(&mut Object, Sel, id) -> bool, @@ -193,12 +218,25 @@ impl MacForegroundPlatform { ) -> id { match item { MenuItem::Separator => NSMenuItem::separatorItem(nil), - MenuItem::Action { name, action, .. } => { + MenuItem::Action { + name, + action, + os_action, + } => { // TODO let keystrokes = keystroke_matcher .bindings_for_action_type(action.as_any().type_id()) .find(|binding| binding.action().eq(action.as_ref())) .map(|binding| binding.keystrokes()); + let selector = match os_action { + Some(crate::OsAction::Cut) => selector("cut:"), + Some(crate::OsAction::Copy) => selector("copy:"), + Some(crate::OsAction::Paste) => selector("paste:"), + Some(crate::OsAction::SelectAll) => selector("selectAll:"), + Some(crate::OsAction::Undo) => selector("undo:"), + Some(crate::OsAction::Redo) => selector("redo:"), + None => selector("handleGPUIMenuItem:"), + }; let item; if let Some(keystrokes) = keystrokes { @@ -219,7 +257,7 @@ impl MacForegroundPlatform { item = NSMenuItem::alloc(nil) .initWithTitle_action_keyEquivalent_( ns_string(name), - selector("handleGPUIMenuItem:"), + selector, ns_string(key_to_native(&keystroke.key).as_ref()), ) .autorelease(); @@ -241,7 +279,7 @@ impl MacForegroundPlatform { item = NSMenuItem::alloc(nil) .initWithTitle_action_keyEquivalent_( ns_string(&name), - selector("handleGPUIMenuItem:"), + selector, ns_string(""), ) .autorelease(); @@ -250,7 +288,7 @@ impl MacForegroundPlatform { item = NSMenuItem::alloc(nil) .initWithTitle_action_keyEquivalent_( ns_string(name), - selector("handleGPUIMenuItem:"), + selector, ns_string(""), ) .autorelease();