Allow menu items to specify arguments for their commands
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
a0a004737d
commit
3247f49954
8 changed files with 149 additions and 102 deletions
|
@ -69,7 +69,7 @@ pub trait UpdateView {
|
||||||
|
|
||||||
pub struct Menu<'a> {
|
pub struct Menu<'a> {
|
||||||
pub name: &'a str,
|
pub name: &'a str,
|
||||||
pub items: &'a [MenuItem<'a>],
|
pub items: Vec<MenuItem<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MenuItem<'a> {
|
pub enum MenuItem<'a> {
|
||||||
|
@ -77,6 +77,7 @@ pub enum MenuItem<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
keystroke: Option<&'a str>,
|
keystroke: Option<&'a str>,
|
||||||
action: &'a str,
|
action: &'a str,
|
||||||
|
arg: Option<Box<dyn Any>>,
|
||||||
},
|
},
|
||||||
Separator,
|
Separator,
|
||||||
}
|
}
|
||||||
|
@ -171,14 +172,14 @@ impl App {
|
||||||
|
|
||||||
pub fn on_menu_command<F>(self, mut callback: F) -> Self
|
pub fn on_menu_command<F>(self, mut callback: F) -> Self
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(&str, &mut MutableAppContext),
|
F: 'static + FnMut(&str, Option<&dyn Any>, &mut MutableAppContext),
|
||||||
{
|
{
|
||||||
let ctx = self.0.clone();
|
let ctx = self.0.clone();
|
||||||
self.0
|
self.0
|
||||||
.borrow()
|
.borrow()
|
||||||
.platform
|
.platform
|
||||||
.on_menu_command(Box::new(move |command| {
|
.on_menu_command(Box::new(move |command, arg| {
|
||||||
callback(command, &mut *ctx.borrow_mut())
|
callback(command, arg, &mut *ctx.borrow_mut())
|
||||||
}));
|
}));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -197,7 +198,7 @@ impl App {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_menus(&self, menus: &[Menu]) {
|
pub fn set_menus(&self, menus: Vec<Menu>) {
|
||||||
self.0.borrow().platform.set_menus(menus);
|
self.0.borrow().platform.set_menus(menus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,6 +743,7 @@ impl MutableAppContext {
|
||||||
|
|
||||||
fn open_platform_window(&mut self, window_id: usize) {
|
fn open_platform_window(&mut self, window_id: usize) {
|
||||||
match self.platform.open_window(
|
match self.platform.open_window(
|
||||||
|
window_id,
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
bounds: RectF::new(vec2f(0., 0.), vec2f(1024., 768.)),
|
bounds: RectF::new(vec2f(0., 0.), vec2f(1024., 768.)),
|
||||||
title: "Zed".into(),
|
title: "Zed".into(),
|
||||||
|
|
|
@ -20,6 +20,7 @@ use objc::{
|
||||||
};
|
};
|
||||||
use ptr::null_mut;
|
use ptr::null_mut;
|
||||||
use std::{
|
use std::{
|
||||||
|
any::Any,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
ffi::{c_void, CStr},
|
ffi::{c_void, CStr},
|
||||||
os::raw::c_char,
|
os::raw::c_char,
|
||||||
|
@ -76,7 +77,7 @@ pub struct MacPlatform {
|
||||||
dispatcher: Arc<Dispatcher>,
|
dispatcher: Arc<Dispatcher>,
|
||||||
fonts: Arc<FontSystem>,
|
fonts: Arc<FontSystem>,
|
||||||
callbacks: RefCell<Callbacks>,
|
callbacks: RefCell<Callbacks>,
|
||||||
menu_item_actions: RefCell<Vec<String>>,
|
menu_item_actions: RefCell<Vec<(String, Option<Box<dyn Any>>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -84,7 +85,7 @@ struct Callbacks {
|
||||||
become_active: Option<Box<dyn FnMut()>>,
|
become_active: Option<Box<dyn FnMut()>>,
|
||||||
resign_active: Option<Box<dyn FnMut()>>,
|
resign_active: Option<Box<dyn FnMut()>>,
|
||||||
event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
|
event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
|
||||||
menu_command: Option<Box<dyn FnMut(&str)>>,
|
menu_command: Option<Box<dyn FnMut(&str, Option<&dyn Any>)>>,
|
||||||
open_files: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
|
open_files: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
|
||||||
finish_launching: Option<Box<dyn FnOnce() -> ()>>,
|
finish_launching: Option<Box<dyn FnOnce() -> ()>>,
|
||||||
}
|
}
|
||||||
|
@ -99,7 +100,7 @@ impl MacPlatform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn create_menu_bar(&self, menus: &[Menu]) -> id {
|
unsafe fn create_menu_bar(&self, menus: Vec<Menu>) -> id {
|
||||||
let menu_bar = NSMenu::new(nil).autorelease();
|
let menu_bar = NSMenu::new(nil).autorelease();
|
||||||
let mut menu_item_actions = self.menu_item_actions.borrow_mut();
|
let mut menu_item_actions = self.menu_item_actions.borrow_mut();
|
||||||
menu_item_actions.clear();
|
menu_item_actions.clear();
|
||||||
|
@ -107,8 +108,9 @@ impl MacPlatform {
|
||||||
for menu_config in menus {
|
for menu_config in menus {
|
||||||
let menu_bar_item = NSMenuItem::new(nil).autorelease();
|
let menu_bar_item = NSMenuItem::new(nil).autorelease();
|
||||||
let menu = NSMenu::new(nil).autorelease();
|
let menu = NSMenu::new(nil).autorelease();
|
||||||
|
let menu_name = menu_config.name;
|
||||||
|
|
||||||
menu.setTitle_(ns_string(menu_config.name));
|
menu.setTitle_(ns_string(menu_name));
|
||||||
|
|
||||||
for item_config in menu_config.items {
|
for item_config in menu_config.items {
|
||||||
let item;
|
let item;
|
||||||
|
@ -121,12 +123,13 @@ impl MacPlatform {
|
||||||
name,
|
name,
|
||||||
keystroke,
|
keystroke,
|
||||||
action,
|
action,
|
||||||
|
arg,
|
||||||
} => {
|
} => {
|
||||||
if let Some(keystroke) = keystroke {
|
if let Some(keystroke) = keystroke {
|
||||||
let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| {
|
let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| {
|
||||||
panic!(
|
panic!(
|
||||||
"Invalid keystroke for menu item {}:{} - {:?}",
|
"Invalid keystroke for menu item {}:{} - {:?}",
|
||||||
menu_config.name, name, err
|
menu_name, name, err
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -161,7 +164,7 @@ impl MacPlatform {
|
||||||
|
|
||||||
let tag = menu_item_actions.len() as NSInteger;
|
let tag = menu_item_actions.len() as NSInteger;
|
||||||
let _: () = msg_send![item, setTag: tag];
|
let _: () = msg_send![item, setTag: tag];
|
||||||
menu_item_actions.push(action.to_string());
|
menu_item_actions.push((action.to_string(), arg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +192,7 @@ impl platform::Platform for MacPlatform {
|
||||||
self.callbacks.borrow_mut().event = Some(callback);
|
self.callbacks.borrow_mut().event = Some(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&str)>) {
|
fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>) {
|
||||||
self.callbacks.borrow_mut().menu_command = Some(callback);
|
self.callbacks.borrow_mut().menu_command = Some(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,10 +234,15 @@ impl platform::Platform for MacPlatform {
|
||||||
|
|
||||||
fn open_window(
|
fn open_window(
|
||||||
&self,
|
&self,
|
||||||
|
id: usize,
|
||||||
options: platform::WindowOptions,
|
options: platform::WindowOptions,
|
||||||
executor: Rc<executor::Foreground>,
|
executor: Rc<executor::Foreground>,
|
||||||
) -> Result<Box<dyn platform::Window>> {
|
) -> Result<Box<dyn platform::Window>> {
|
||||||
Ok(Box::new(Window::open(options, executor, self.fonts())?))
|
Ok(Box::new(Window::open(id, options, executor, self.fonts())?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_window_id(&self) -> Option<usize> {
|
||||||
|
Window::key_window_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prompt_for_paths(
|
fn prompt_for_paths(
|
||||||
|
@ -292,7 +300,7 @@ impl platform::Platform for MacPlatform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_menus(&self, menus: &[Menu]) {
|
fn set_menus(&self, menus: Vec<Menu>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let app: id = msg_send![APP_CLASS, sharedApplication];
|
let app: id = msg_send![APP_CLASS, sharedApplication];
|
||||||
app.setMainMenu_(self.create_menu_bar(menus));
|
app.setMainMenu_(self.create_menu_bar(menus));
|
||||||
|
@ -375,8 +383,8 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
|
||||||
if let Some(callback) = platform.callbacks.borrow_mut().menu_command.as_mut() {
|
if let Some(callback) = platform.callbacks.borrow_mut().menu_command.as_mut() {
|
||||||
let tag: NSInteger = msg_send![item, tag];
|
let tag: NSInteger = msg_send![item, tag];
|
||||||
let index = tag as usize;
|
let index = tag as usize;
|
||||||
if let Some(action) = platform.menu_item_actions.borrow().get(index) {
|
if let Some((action, arg)) = platform.menu_item_actions.borrow().get(index) {
|
||||||
callback(&action);
|
callback(action, arg.as_ref().map(Box::as_ref));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ use crate::{
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use cocoa::{
|
use cocoa::{
|
||||||
appkit::{
|
appkit::{
|
||||||
NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable, NSViewWidthSizable,
|
NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
|
||||||
NSWindow, NSWindowStyleMask,
|
NSViewWidthSizable, NSWindow, NSWindowStyleMask,
|
||||||
},
|
},
|
||||||
base::{id, nil},
|
base::{id, nil},
|
||||||
foundation::{NSAutoreleasePool, NSInteger, NSSize, NSString},
|
foundation::{NSAutoreleasePool, NSInteger, NSSize, NSString},
|
||||||
|
@ -118,6 +118,7 @@ unsafe fn build_classes() {
|
||||||
pub struct Window(Rc<RefCell<WindowState>>);
|
pub struct Window(Rc<RefCell<WindowState>>);
|
||||||
|
|
||||||
struct WindowState {
|
struct WindowState {
|
||||||
|
id: usize,
|
||||||
native_window: id,
|
native_window: id,
|
||||||
event_callback: Option<Box<dyn FnMut(Event)>>,
|
event_callback: Option<Box<dyn FnMut(Event)>>,
|
||||||
resize_callback: Option<Box<dyn FnMut(&mut dyn platform::WindowContext)>>,
|
resize_callback: Option<Box<dyn FnMut(&mut dyn platform::WindowContext)>>,
|
||||||
|
@ -131,6 +132,7 @@ struct WindowState {
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn open(
|
pub fn open(
|
||||||
|
id: usize,
|
||||||
options: platform::WindowOptions,
|
options: platform::WindowOptions,
|
||||||
executor: Rc<executor::Foreground>,
|
executor: Rc<executor::Foreground>,
|
||||||
fonts: Arc<dyn platform::FontSystem>,
|
fonts: Arc<dyn platform::FontSystem>,
|
||||||
|
@ -180,6 +182,7 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
let window = Self(Rc::new(RefCell::new(WindowState {
|
let window = Self(Rc::new(RefCell::new(WindowState {
|
||||||
|
id,
|
||||||
native_window,
|
native_window,
|
||||||
event_callback: None,
|
event_callback: None,
|
||||||
resize_callback: None,
|
resize_callback: None,
|
||||||
|
@ -230,6 +233,19 @@ impl Window {
|
||||||
Ok(window)
|
Ok(window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn key_window_id() -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
let app = NSApplication::sharedApplication(nil);
|
||||||
|
let key_window: id = msg_send![app, keyWindow];
|
||||||
|
if key_window.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let id = get_window_state(&*key_window).borrow().id;
|
||||||
|
Some(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Window {
|
impl Drop for Window {
|
||||||
|
|
|
@ -20,10 +20,10 @@ use crate::{
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_task::Runnable;
|
use async_task::Runnable;
|
||||||
pub use event::Event;
|
pub use event::Event;
|
||||||
use std::{ops::Range, path::PathBuf, rc::Rc, sync::Arc};
|
use std::{any::Any, ops::Range, path::PathBuf, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
pub trait Platform {
|
pub trait Platform {
|
||||||
fn on_menu_command(&self, callback: Box<dyn FnMut(&str)>);
|
fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>);
|
||||||
fn on_become_active(&self, callback: Box<dyn FnMut()>);
|
fn on_become_active(&self, callback: Box<dyn FnMut()>);
|
||||||
fn on_resign_active(&self, callback: Box<dyn FnMut()>);
|
fn on_resign_active(&self, callback: Box<dyn FnMut()>);
|
||||||
fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
|
fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
|
||||||
|
@ -36,13 +36,15 @@ pub trait Platform {
|
||||||
fn activate(&self, ignoring_other_apps: bool);
|
fn activate(&self, ignoring_other_apps: bool);
|
||||||
fn open_window(
|
fn open_window(
|
||||||
&self,
|
&self,
|
||||||
|
id: usize,
|
||||||
options: WindowOptions,
|
options: WindowOptions,
|
||||||
executor: Rc<executor::Foreground>,
|
executor: Rc<executor::Foreground>,
|
||||||
) -> Result<Box<dyn Window>>;
|
) -> Result<Box<dyn Window>>;
|
||||||
|
fn key_window_id(&self) -> Option<usize>;
|
||||||
fn prompt_for_paths(&self, options: PathPromptOptions) -> Option<Vec<PathBuf>>;
|
fn prompt_for_paths(&self, options: PathPromptOptions) -> Option<Vec<PathBuf>>;
|
||||||
fn quit(&self);
|
fn quit(&self);
|
||||||
fn copy(&self, text: &str);
|
fn copy(&self, text: &str);
|
||||||
fn set_menus(&self, menus: &[Menu]);
|
fn set_menus(&self, menus: Vec<Menu>);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Dispatcher: Send + Sync {
|
pub trait Dispatcher: Send + Sync {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use pathfinder_geometry::vector::Vector2F;
|
use pathfinder_geometry::vector::Vector2F;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::{any::Any, rc::Rc};
|
||||||
|
|
||||||
struct Platform {
|
struct Platform {
|
||||||
dispatcher: Arc<dyn super::Dispatcher>,
|
dispatcher: Arc<dyn super::Dispatcher>,
|
||||||
|
@ -27,7 +27,7 @@ impl Platform {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Platform for Platform {
|
impl super::Platform for Platform {
|
||||||
fn on_menu_command(&self, _: Box<dyn FnMut(&str)>) {}
|
fn on_menu_command(&self, _: Box<dyn FnMut(&str, Option<&dyn Any>)>) {}
|
||||||
|
|
||||||
fn on_become_active(&self, _: Box<dyn FnMut()>) {}
|
fn on_become_active(&self, _: Box<dyn FnMut()>) {}
|
||||||
|
|
||||||
|
@ -53,13 +53,18 @@ impl super::Platform for Platform {
|
||||||
|
|
||||||
fn open_window(
|
fn open_window(
|
||||||
&self,
|
&self,
|
||||||
|
_: usize,
|
||||||
options: super::WindowOptions,
|
options: super::WindowOptions,
|
||||||
_executor: Rc<super::executor::Foreground>,
|
_executor: Rc<super::executor::Foreground>,
|
||||||
) -> anyhow::Result<Box<dyn super::Window>> {
|
) -> anyhow::Result<Box<dyn super::Window>> {
|
||||||
Ok(Box::new(Window::new(options.bounds.size())))
|
Ok(Box::new(Window::new(options.bounds.size())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_menus(&self, _menus: &[crate::Menu]) {}
|
fn key_window_id(&self) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_menus(&self, _menus: Vec<crate::Menu>) {}
|
||||||
|
|
||||||
fn quit(&self) {}
|
fn quit(&self) {}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,6 @@ mod test;
|
||||||
mod time;
|
mod time;
|
||||||
mod timer;
|
mod timer;
|
||||||
mod util;
|
mod util;
|
||||||
mod watch;
|
pub mod watch;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
mod worktree;
|
mod worktree;
|
||||||
|
|
|
@ -4,7 +4,9 @@ use log::LevelFilter;
|
||||||
use simplelog::SimpleLogger;
|
use simplelog::SimpleLogger;
|
||||||
use std::{fs, path::PathBuf};
|
use std::{fs, path::PathBuf};
|
||||||
use zed::{
|
use zed::{
|
||||||
assets, editor, file_finder, menus, settings,
|
assets, editor, file_finder, menus,
|
||||||
|
settings::{self, Settings},
|
||||||
|
watch::Receiver,
|
||||||
workspace::{self, OpenParams},
|
workspace::{self, OpenParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,27 +15,28 @@ fn main() {
|
||||||
|
|
||||||
let app = gpui::App::new(assets::Assets).unwrap();
|
let app = gpui::App::new(assets::Assets).unwrap();
|
||||||
let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap();
|
let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap();
|
||||||
app.set_menus(menus::MENUS);
|
app.set_menus(menus::menus(settings_rx.clone()));
|
||||||
app.on_menu_command({
|
app.on_menu_command(move |command, arg, ctx| match command {
|
||||||
let settings_rx = settings_rx.clone();
|
"app:open" => {
|
||||||
move |command, ctx| match command {
|
if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions {
|
||||||
"app:open" => {
|
files: true,
|
||||||
if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions {
|
directories: true,
|
||||||
files: true,
|
multiple: true,
|
||||||
directories: true,
|
}) {
|
||||||
multiple: true,
|
ctx.dispatch_global_action(
|
||||||
}) {
|
"workspace:open_paths",
|
||||||
ctx.dispatch_global_action(
|
OpenParams {
|
||||||
"workspace:open_paths",
|
paths,
|
||||||
OpenParams {
|
settings: arg
|
||||||
paths,
|
.unwrap()
|
||||||
settings: settings_rx.clone(),
|
.downcast_ref::<Receiver<Settings>>()
|
||||||
},
|
.unwrap()
|
||||||
);
|
.clone(),
|
||||||
}
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => ctx.dispatch_global_action(command, ()),
|
|
||||||
}
|
}
|
||||||
|
_ => ctx.dispatch_global_action(command, ()),
|
||||||
})
|
})
|
||||||
.run(move |ctx| {
|
.run(move |ctx| {
|
||||||
workspace::init(ctx);
|
workspace::init(ctx);
|
||||||
|
|
125
zed/src/menus.rs
125
zed/src/menus.rs
|
@ -1,60 +1,71 @@
|
||||||
|
use crate::{settings::Settings, watch::Receiver};
|
||||||
use gpui::{Menu, MenuItem};
|
use gpui::{Menu, MenuItem};
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub const MENUS: &'static [Menu] = &[
|
pub fn menus(settings: Receiver<Settings>) -> Vec<Menu<'static>> {
|
||||||
Menu {
|
vec![
|
||||||
name: "Zed",
|
Menu {
|
||||||
items: &[
|
name: "Zed",
|
||||||
MenuItem::Action {
|
items: vec![
|
||||||
name: "About Zed…",
|
MenuItem::Action {
|
||||||
keystroke: None,
|
name: "About Zed…",
|
||||||
action: "app:about-zed",
|
keystroke: None,
|
||||||
},
|
action: "app:about-zed",
|
||||||
MenuItem::Separator,
|
arg: None,
|
||||||
MenuItem::Action {
|
},
|
||||||
name: "Quit",
|
MenuItem::Separator,
|
||||||
keystroke: Some("cmd-q"),
|
MenuItem::Action {
|
||||||
action: "app:quit",
|
name: "Quit",
|
||||||
},
|
keystroke: Some("cmd-q"),
|
||||||
],
|
action: "app:quit",
|
||||||
},
|
arg: None,
|
||||||
Menu {
|
},
|
||||||
name: "File",
|
],
|
||||||
items: &[MenuItem::Action {
|
},
|
||||||
name: "Open…",
|
Menu {
|
||||||
keystroke: Some("cmd-o"),
|
name: "File",
|
||||||
action: "app:open",
|
items: vec![MenuItem::Action {
|
||||||
}],
|
name: "Open…",
|
||||||
},
|
keystroke: Some("cmd-o"),
|
||||||
Menu {
|
action: "app:open",
|
||||||
name: "Edit",
|
arg: Some(Box::new(settings)),
|
||||||
items: &[
|
}],
|
||||||
MenuItem::Action {
|
},
|
||||||
name: "Undo",
|
Menu {
|
||||||
keystroke: Some("cmd-z"),
|
name: "Edit",
|
||||||
action: "editor:undo",
|
items: vec![
|
||||||
},
|
MenuItem::Action {
|
||||||
MenuItem::Action {
|
name: "Undo",
|
||||||
name: "Redo",
|
keystroke: Some("cmd-z"),
|
||||||
keystroke: Some("cmd-Z"),
|
action: "editor:undo",
|
||||||
action: "editor:redo",
|
arg: None,
|
||||||
},
|
},
|
||||||
MenuItem::Separator,
|
MenuItem::Action {
|
||||||
MenuItem::Action {
|
name: "Redo",
|
||||||
name: "Cut",
|
keystroke: Some("cmd-Z"),
|
||||||
keystroke: Some("cmd-x"),
|
action: "editor:redo",
|
||||||
action: "editor:cut",
|
arg: None,
|
||||||
},
|
},
|
||||||
MenuItem::Action {
|
MenuItem::Separator,
|
||||||
name: "Copy",
|
MenuItem::Action {
|
||||||
keystroke: Some("cmd-c"),
|
name: "Cut",
|
||||||
action: "editor:copy",
|
keystroke: Some("cmd-x"),
|
||||||
},
|
action: "editor:cut",
|
||||||
MenuItem::Action {
|
arg: None,
|
||||||
name: "Paste",
|
},
|
||||||
keystroke: Some("cmd-v"),
|
MenuItem::Action {
|
||||||
action: "editor:paste",
|
name: "Copy",
|
||||||
},
|
keystroke: Some("cmd-c"),
|
||||||
],
|
action: "editor:copy",
|
||||||
},
|
arg: None,
|
||||||
];
|
},
|
||||||
|
MenuItem::Action {
|
||||||
|
name: "Paste",
|
||||||
|
keystroke: Some("cmd-v"),
|
||||||
|
action: "editor:paste",
|
||||||
|
arg: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue