Merge pull request #2201 from zed-industries/save-shortcuts

Add OS UI Keybindings
This commit is contained in:
Mikayla Maki 2023-02-22 09:16:19 -08:00 committed by GitHub
commit 7b559176f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 189 additions and 316 deletions

View file

@ -11,9 +11,46 @@ pub enum MenuItem<'a> {
Action {
name: &'a str,
action: Box<dyn Action>,
os_action: Option<OsAction>,
},
}
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<Menu>) {
self.foreground_platform

View file

@ -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,11 +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 {
@ -218,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();
@ -240,7 +279,7 @@ impl MacForegroundPlatform {
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(&name),
selector("handleGPUIMenuItem:"),
selector,
ns_string(""),
)
.autorelease();
@ -249,7 +288,7 @@ impl MacForegroundPlatform {
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(name),
selector("handleGPUIMenuItem:"),
selector,
ns_string(""),
)
.autorelease();

View file

@ -20,6 +20,7 @@ fn main() {
items: vec![MenuItem::Action {
name: "Quit",
action: Box::new(Quit),
os_action: None,
}],
}]);

View file

@ -1,4 +1,4 @@
use gpui::{Menu, MenuItem};
use gpui::{Menu, MenuItem, OsAction};
#[cfg(target_os = "macos")]
pub fn menus() -> Vec<Menu<'static>> {
@ -6,363 +6,159 @@ pub fn menus() -> Vec<Menu<'static>> {
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(),
}),
},
},
),
],
},
]