From 3247f499546e5e1c0a8004d477670de4bc589f0b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Apr 2021 14:09:49 -0700 Subject: [PATCH 1/6] Allow menu items to specify arguments for their commands Co-Authored-By: Nathan Sobo --- gpui/src/app.rs | 12 +-- gpui/src/platform/mac/platform.rs | 30 ++++--- gpui/src/platform/mac/window.rs | 20 ++++- gpui/src/platform/mod.rs | 8 +- gpui/src/platform/test.rs | 11 ++- zed/src/lib.rs | 2 +- zed/src/main.rs | 43 +++++----- zed/src/menus.rs | 125 ++++++++++++++++-------------- 8 files changed, 149 insertions(+), 102 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index e5f40c6ba5..9a8c00788d 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -69,7 +69,7 @@ pub trait UpdateView { pub struct Menu<'a> { pub name: &'a str, - pub items: &'a [MenuItem<'a>], + pub items: Vec>, } pub enum MenuItem<'a> { @@ -77,6 +77,7 @@ pub enum MenuItem<'a> { name: &'a str, keystroke: Option<&'a str>, action: &'a str, + arg: Option>, }, Separator, } @@ -171,14 +172,14 @@ impl App { pub fn on_menu_command(self, mut callback: F) -> Self where - F: 'static + FnMut(&str, &mut MutableAppContext), + F: 'static + FnMut(&str, Option<&dyn Any>, &mut MutableAppContext), { let ctx = self.0.clone(); self.0 .borrow() .platform - .on_menu_command(Box::new(move |command| { - callback(command, &mut *ctx.borrow_mut()) + .on_menu_command(Box::new(move |command, arg| { + callback(command, arg, &mut *ctx.borrow_mut()) })); self } @@ -197,7 +198,7 @@ impl App { self } - pub fn set_menus(&self, menus: &[Menu]) { + pub fn set_menus(&self, menus: Vec) { self.0.borrow().platform.set_menus(menus); } @@ -742,6 +743,7 @@ impl MutableAppContext { fn open_platform_window(&mut self, window_id: usize) { match self.platform.open_window( + window_id, WindowOptions { bounds: RectF::new(vec2f(0., 0.), vec2f(1024., 768.)), title: "Zed".into(), diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index 4201100a6d..cc70487d77 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -20,6 +20,7 @@ use objc::{ }; use ptr::null_mut; use std::{ + any::Any, cell::RefCell, ffi::{c_void, CStr}, os::raw::c_char, @@ -76,7 +77,7 @@ pub struct MacPlatform { dispatcher: Arc, fonts: Arc, callbacks: RefCell, - menu_item_actions: RefCell>, + menu_item_actions: RefCell>)>>, } #[derive(Default)] @@ -84,7 +85,7 @@ struct Callbacks { become_active: Option>, resign_active: Option>, event: Option bool>>, - menu_command: Option>, + menu_command: Option)>>, open_files: Option)>>, finish_launching: Option ()>>, } @@ -99,7 +100,7 @@ impl MacPlatform { } } - unsafe fn create_menu_bar(&self, menus: &[Menu]) -> id { + unsafe fn create_menu_bar(&self, menus: Vec) -> id { let menu_bar = NSMenu::new(nil).autorelease(); let mut menu_item_actions = self.menu_item_actions.borrow_mut(); menu_item_actions.clear(); @@ -107,8 +108,9 @@ impl MacPlatform { for menu_config in menus { let menu_bar_item = NSMenuItem::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 { let item; @@ -121,12 +123,13 @@ impl MacPlatform { name, keystroke, action, + arg, } => { if let Some(keystroke) = keystroke { let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| { panic!( "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 _: () = 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); } - fn on_menu_command(&self, callback: Box) { + fn on_menu_command(&self, callback: Box)>) { self.callbacks.borrow_mut().menu_command = Some(callback); } @@ -231,10 +234,15 @@ impl platform::Platform for MacPlatform { fn open_window( &self, + id: usize, options: platform::WindowOptions, executor: Rc, ) -> Result> { - 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 { + Window::key_window_id() } 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) { unsafe { let app: id = msg_send![APP_CLASS, sharedApplication]; 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() { let tag: NSInteger = msg_send![item, tag]; let index = tag as usize; - if let Some(action) = platform.menu_item_actions.borrow().get(index) { - callback(&action); + if let Some((action, arg)) = platform.menu_item_actions.borrow().get(index) { + callback(action, arg.as_ref().map(Box::as_ref)); } } } diff --git a/gpui/src/platform/mac/window.rs b/gpui/src/platform/mac/window.rs index e9e6318b5d..940c00686c 100644 --- a/gpui/src/platform/mac/window.rs +++ b/gpui/src/platform/mac/window.rs @@ -7,8 +7,8 @@ use crate::{ use anyhow::{anyhow, Result}; use cocoa::{ appkit::{ - NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable, NSViewWidthSizable, - NSWindow, NSWindowStyleMask, + NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable, + NSViewWidthSizable, NSWindow, NSWindowStyleMask, }, base::{id, nil}, foundation::{NSAutoreleasePool, NSInteger, NSSize, NSString}, @@ -118,6 +118,7 @@ unsafe fn build_classes() { pub struct Window(Rc>); struct WindowState { + id: usize, native_window: id, event_callback: Option>, resize_callback: Option>, @@ -131,6 +132,7 @@ struct WindowState { impl Window { pub fn open( + id: usize, options: platform::WindowOptions, executor: Rc, fonts: Arc, @@ -180,6 +182,7 @@ impl Window { } let window = Self(Rc::new(RefCell::new(WindowState { + id, native_window, event_callback: None, resize_callback: None, @@ -230,6 +233,19 @@ impl Window { Ok(window) } } + + pub fn key_window_id() -> Option { + 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 { diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index 39825c941e..76d93c03be 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -20,10 +20,10 @@ use crate::{ use anyhow::Result; use async_task::Runnable; 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 { - fn on_menu_command(&self, callback: Box); + fn on_menu_command(&self, callback: Box)>); fn on_become_active(&self, callback: Box); fn on_resign_active(&self, callback: Box); fn on_event(&self, callback: Box bool>); @@ -36,13 +36,15 @@ pub trait Platform { fn activate(&self, ignoring_other_apps: bool); fn open_window( &self, + id: usize, options: WindowOptions, executor: Rc, ) -> Result>; + fn key_window_id(&self) -> Option; fn prompt_for_paths(&self, options: PathPromptOptions) -> Option>; fn quit(&self); fn copy(&self, text: &str); - fn set_menus(&self, menus: &[Menu]); + fn set_menus(&self, menus: Vec); } pub trait Dispatcher: Send + Sync { diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index f1d6bead66..fb120a8eb5 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -1,6 +1,6 @@ use pathfinder_geometry::vector::Vector2F; -use std::rc::Rc; use std::sync::Arc; +use std::{any::Any, rc::Rc}; struct Platform { dispatcher: Arc, @@ -27,7 +27,7 @@ impl Platform { } impl super::Platform for Platform { - fn on_menu_command(&self, _: Box) {} + fn on_menu_command(&self, _: Box)>) {} fn on_become_active(&self, _: Box) {} @@ -53,13 +53,18 @@ impl super::Platform for Platform { fn open_window( &self, + _: usize, options: super::WindowOptions, _executor: Rc, ) -> anyhow::Result> { Ok(Box::new(Window::new(options.bounds.size()))) } - fn set_menus(&self, _menus: &[crate::Menu]) {} + fn key_window_id(&self) -> Option { + None + } + + fn set_menus(&self, _menus: Vec) {} fn quit(&self) {} diff --git a/zed/src/lib.rs b/zed/src/lib.rs index 752df470c5..14c2369258 100644 --- a/zed/src/lib.rs +++ b/zed/src/lib.rs @@ -10,6 +10,6 @@ mod test; mod time; mod timer; mod util; -mod watch; +pub mod watch; pub mod workspace; mod worktree; diff --git a/zed/src/main.rs b/zed/src/main.rs index 407b4952c1..980262d197 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -4,7 +4,9 @@ use log::LevelFilter; use simplelog::SimpleLogger; use std::{fs, path::PathBuf}; use zed::{ - assets, editor, file_finder, menus, settings, + assets, editor, file_finder, menus, + settings::{self, Settings}, + watch::Receiver, workspace::{self, OpenParams}, }; @@ -13,27 +15,28 @@ fn main() { let app = gpui::App::new(assets::Assets).unwrap(); let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap(); - app.set_menus(menus::MENUS); - app.on_menu_command({ - let settings_rx = settings_rx.clone(); - move |command, ctx| match command { - "app:open" => { - if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions { - files: true, - directories: true, - multiple: true, - }) { - ctx.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths, - settings: settings_rx.clone(), - }, - ); - } + app.set_menus(menus::menus(settings_rx.clone())); + app.on_menu_command(move |command, arg, ctx| match command { + "app:open" => { + if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions { + files: true, + directories: true, + multiple: true, + }) { + ctx.dispatch_global_action( + "workspace:open_paths", + OpenParams { + paths, + settings: arg + .unwrap() + .downcast_ref::>() + .unwrap() + .clone(), + }, + ); } - _ => ctx.dispatch_global_action(command, ()), } + _ => ctx.dispatch_global_action(command, ()), }) .run(move |ctx| { workspace::init(ctx); diff --git a/zed/src/menus.rs b/zed/src/menus.rs index cda749c30e..7da690972e 100644 --- a/zed/src/menus.rs +++ b/zed/src/menus.rs @@ -1,60 +1,71 @@ +use crate::{settings::Settings, watch::Receiver}; use gpui::{Menu, MenuItem}; #[cfg(target_os = "macos")] -pub const MENUS: &'static [Menu] = &[ - Menu { - name: "Zed", - items: &[ - MenuItem::Action { - name: "About Zed…", - keystroke: None, - action: "app:about-zed", - }, - MenuItem::Separator, - MenuItem::Action { - name: "Quit", - keystroke: Some("cmd-q"), - action: "app:quit", - }, - ], - }, - Menu { - name: "File", - items: &[MenuItem::Action { - name: "Open…", - keystroke: Some("cmd-o"), - action: "app:open", - }], - }, - Menu { - name: "Edit", - items: &[ - MenuItem::Action { - name: "Undo", - keystroke: Some("cmd-z"), - action: "editor:undo", - }, - MenuItem::Action { - name: "Redo", - keystroke: Some("cmd-Z"), - action: "editor:redo", - }, - MenuItem::Separator, - MenuItem::Action { - name: "Cut", - keystroke: Some("cmd-x"), - action: "editor:cut", - }, - MenuItem::Action { - name: "Copy", - keystroke: Some("cmd-c"), - action: "editor:copy", - }, - MenuItem::Action { - name: "Paste", - keystroke: Some("cmd-v"), - action: "editor:paste", - }, - ], - }, -]; +pub fn menus(settings: Receiver) -> Vec> { + vec![ + Menu { + name: "Zed", + items: vec![ + MenuItem::Action { + name: "About Zed…", + keystroke: None, + action: "app:about-zed", + arg: None, + }, + MenuItem::Separator, + MenuItem::Action { + name: "Quit", + keystroke: Some("cmd-q"), + action: "app:quit", + arg: None, + }, + ], + }, + Menu { + name: "File", + items: vec![MenuItem::Action { + name: "Open…", + keystroke: Some("cmd-o"), + action: "app:open", + arg: Some(Box::new(settings)), + }], + }, + Menu { + name: "Edit", + items: vec![ + MenuItem::Action { + name: "Undo", + keystroke: Some("cmd-z"), + action: "editor:undo", + arg: None, + }, + MenuItem::Action { + name: "Redo", + keystroke: Some("cmd-Z"), + action: "editor:redo", + arg: None, + }, + MenuItem::Separator, + MenuItem::Action { + name: "Cut", + keystroke: Some("cmd-x"), + action: "editor:cut", + arg: None, + }, + MenuItem::Action { + name: "Copy", + keystroke: Some("cmd-c"), + action: "editor:copy", + arg: None, + }, + MenuItem::Action { + name: "Paste", + keystroke: Some("cmd-v"), + action: "editor:paste", + arg: None, + }, + ], + }, + ] +} From 2332b7e06b4cd569950a629df34fd0f7bcb974b2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Apr 2021 14:18:24 -0700 Subject: [PATCH 2/6] Move open command handler to workspace global action Co-Authored-By: Nathan Sobo --- gpui/src/app.rs | 6 +++--- zed/src/main.rs | 24 +++--------------------- zed/src/menus.rs | 2 +- zed/src/workspace/mod.rs | 24 ++++++++++++++++++++++-- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 9a8c00788d..3c84da64dd 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -77,7 +77,7 @@ pub enum MenuItem<'a> { name: &'a str, keystroke: Option<&'a str>, action: &'a str, - arg: Option>, + arg: Option>, }, Separator, } @@ -172,7 +172,7 @@ impl App { pub fn on_menu_command(self, mut callback: F) -> Self where - F: 'static + FnMut(&str, Option<&dyn Any>, &mut MutableAppContext), + F: 'static + FnMut(&str, Option<&(dyn Any + 'static)>, &mut MutableAppContext), { let ctx = self.0.clone(); self.0 @@ -646,7 +646,7 @@ impl MutableAppContext { self.dispatch_global_action_with_dyn_arg(name, Box::new(arg).as_ref()); } - fn dispatch_global_action_with_dyn_arg(&mut self, name: &str, arg: &dyn Any) { + pub fn dispatch_global_action_with_dyn_arg(&mut self, name: &str, arg: &dyn Any) { if let Some((name, mut handlers)) = self.global_actions.remove_entry(name) { self.pending_flushes += 1; for handler in handlers.iter_mut().rev() { diff --git a/zed/src/main.rs b/zed/src/main.rs index 980262d197..b5fc625eec 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -16,27 +16,9 @@ fn main() { let app = gpui::App::new(assets::Assets).unwrap(); let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap(); app.set_menus(menus::menus(settings_rx.clone())); - app.on_menu_command(move |command, arg, ctx| match command { - "app:open" => { - if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions { - files: true, - directories: true, - multiple: true, - }) { - ctx.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths, - settings: arg - .unwrap() - .downcast_ref::>() - .unwrap() - .clone(), - }, - ); - } - } - _ => ctx.dispatch_global_action(command, ()), + app.on_menu_command(move |command, arg, ctx| { + eprintln!("command: {:?} {:?}", command, arg); + ctx.dispatch_global_action_with_dyn_arg(command, arg.unwrap_or(&())) }) .run(move |ctx| { workspace::init(ctx); diff --git a/zed/src/menus.rs b/zed/src/menus.rs index 7da690972e..03681f4dd2 100644 --- a/zed/src/menus.rs +++ b/zed/src/menus.rs @@ -27,7 +27,7 @@ pub fn menus(settings: Receiver) -> Vec> { items: vec![MenuItem::Action { name: "Open…", keystroke: Some("cmd-o"), - action: "app:open", + action: "workspace:open", arg: Some(Box::new(settings)), }], }, diff --git a/zed/src/workspace/mod.rs b/zed/src/workspace/mod.rs index a441eb8b8b..4cf765b0aa 100644 --- a/zed/src/workspace/mod.rs +++ b/zed/src/workspace/mod.rs @@ -8,11 +8,15 @@ pub use pane_group::*; pub use workspace::*; pub use workspace_view::*; -use crate::{settings::Settings, watch}; -use gpui::MutableAppContext; +use crate::{ + settings::Settings, + watch::{self, Receiver}, +}; +use gpui::{MutableAppContext, PathPromptOptions}; use std::path::PathBuf; pub fn init(app: &mut MutableAppContext) { + app.add_global_action("workspace:open", open); app.add_global_action("workspace:open_paths", open_paths); app.add_global_action("app:quit", quit); pane::init(app); @@ -24,6 +28,22 @@ pub struct OpenParams { pub settings: watch::Receiver, } +fn open(settings: &Receiver, ctx: &mut MutableAppContext) { + if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions { + files: true, + directories: true, + multiple: true, + }) { + ctx.dispatch_global_action( + "workspace:open_paths", + OpenParams { + paths, + settings: settings.clone(), + }, + ); + } +} + fn open_paths(params: &OpenParams, app: &mut MutableAppContext) { log::info!("open paths {:?}", params.paths); From 41358f34e3ed7c7080bee1f1aa273e79c982201b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Apr 2021 14:25:00 -0700 Subject: [PATCH 3/6] Set up menu handler in App::new Co-Authored-By: Nathan Sobo --- gpui/src/app.rs | 25 +++++++++---------------- zed/src/main.rs | 11 ++--------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 3c84da64dd..97030a48e3 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -128,9 +128,16 @@ impl App { let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?); let app = Self(Rc::new(RefCell::new(MutableAppContext::new( foreground, - platform, + platform.clone(), asset_source, )))); + + let ctx = app.0.clone(); + platform.on_menu_command(Box::new(move |command, arg| { + ctx.borrow_mut() + .dispatch_global_action_with_dyn_arg(command, arg.unwrap_or(&())); + })); + app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0)); Ok(app) } @@ -170,20 +177,6 @@ impl App { self } - pub fn on_menu_command(self, mut callback: F) -> Self - where - F: 'static + FnMut(&str, Option<&(dyn Any + 'static)>, &mut MutableAppContext), - { - let ctx = self.0.clone(); - self.0 - .borrow() - .platform - .on_menu_command(Box::new(move |command, arg| { - callback(command, arg, &mut *ctx.borrow_mut()) - })); - self - } - pub fn on_open_files(self, mut callback: F) -> Self where F: 'static + FnMut(Vec, &mut MutableAppContext), @@ -646,7 +639,7 @@ impl MutableAppContext { self.dispatch_global_action_with_dyn_arg(name, Box::new(arg).as_ref()); } - pub fn dispatch_global_action_with_dyn_arg(&mut self, name: &str, arg: &dyn Any) { + fn dispatch_global_action_with_dyn_arg(&mut self, name: &str, arg: &dyn Any) { if let Some((name, mut handlers)) = self.global_actions.remove_entry(name) { self.pending_flushes += 1; for handler in handlers.iter_mut().rev() { diff --git a/zed/src/main.rs b/zed/src/main.rs index b5fc625eec..7edb0eecd5 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -1,12 +1,9 @@ use fs::OpenOptions; -use gpui::PathPromptOptions; use log::LevelFilter; use simplelog::SimpleLogger; use std::{fs, path::PathBuf}; use zed::{ - assets, editor, file_finder, menus, - settings::{self, Settings}, - watch::Receiver, + assets, editor, file_finder, menus, settings, workspace::{self, OpenParams}, }; @@ -16,11 +13,7 @@ fn main() { let app = gpui::App::new(assets::Assets).unwrap(); let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap(); app.set_menus(menus::menus(settings_rx.clone())); - app.on_menu_command(move |command, arg, ctx| { - eprintln!("command: {:?} {:?}", command, arg); - ctx.dispatch_global_action_with_dyn_arg(command, arg.unwrap_or(&())) - }) - .run(move |ctx| { + app.run(move |ctx| { workspace::init(ctx); editor::init(ctx); file_finder::init(ctx); From 86c0f41c9e4d1908c81f40181ebb2e42b0122a1e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Apr 2021 14:38:18 -0700 Subject: [PATCH 4/6] Move set_menus method to MutableAppContext Co-Authored-By: Nathan Sobo --- gpui/src/app.rs | 8 ++++---- zed/src/main.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 97030a48e3..8602dd1b25 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -191,10 +191,6 @@ impl App { self } - pub fn set_menus(&self, menus: Vec) { - self.0.borrow().platform.set_menus(menus); - } - pub fn run(self, on_finish_launching: F) where F: 'static + FnOnce(&mut MutableAppContext), @@ -567,6 +563,10 @@ impl MutableAppContext { result } + pub fn set_menus(&self, menus: Vec) { + self.platform.set_menus(menus); + } + pub fn dispatch_action( &mut self, window_id: usize, diff --git a/zed/src/main.rs b/zed/src/main.rs index 7edb0eecd5..3aaddff884 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -12,8 +12,8 @@ fn main() { let app = gpui::App::new(assets::Assets).unwrap(); let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap(); - app.set_menus(menus::menus(settings_rx.clone())); app.run(move |ctx| { + ctx.set_menus(menus::menus(settings_rx.clone())); workspace::init(ctx); editor::init(ctx); file_finder::init(ctx); From d4436f301816c0003e929bfd0ce2c646ade0fe77 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Apr 2021 15:14:25 -0700 Subject: [PATCH 5/6] Don't try to handle errors when opening platform windows Co-Authored-By: Nathan Sobo --- gpui/src/app.rs | 140 ++++++++++++++---------------- gpui/src/platform/mac/platform.rs | 5 +- gpui/src/platform/mac/renderer.rs | 33 ++++--- gpui/src/platform/mac/window.rs | 20 ++--- gpui/src/platform/mod.rs | 3 +- gpui/src/platform/test.rs | 4 +- 6 files changed, 95 insertions(+), 110 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 8602dd1b25..68f59dea91 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -735,88 +735,82 @@ impl MutableAppContext { } fn open_platform_window(&mut self, window_id: usize) { - match self.platform.open_window( + let mut window = self.platform.open_window( window_id, WindowOptions { bounds: RectF::new(vec2f(0., 0.), vec2f(1024., 768.)), title: "Zed".into(), }, self.foreground.clone(), - ) { - Err(e) => log::error!("error opening window: {}", e), - Ok(mut window) => { - let text_layout_cache = TextLayoutCache::new(self.platform.fonts()); - let presenter = Rc::new(RefCell::new(Presenter::new( - window_id, - self.font_cache.clone(), - text_layout_cache, - self.assets.clone(), - self, - ))); + ); + let text_layout_cache = TextLayoutCache::new(self.platform.fonts()); + let presenter = Rc::new(RefCell::new(Presenter::new( + window_id, + self.font_cache.clone(), + text_layout_cache, + self.assets.clone(), + self, + ))); - { - let mut app = self.upgrade(); - let presenter = presenter.clone(); - window.on_event(Box::new(move |event| { - app.update(|ctx| { - if let Event::KeyDown { keystroke, .. } = &event { - if ctx - .dispatch_keystroke( - window_id, - presenter.borrow().dispatch_path(ctx.as_ref()), - keystroke, - ) - .unwrap() - { - return; - } - } + { + let mut app = self.upgrade(); + let presenter = presenter.clone(); + window.on_event(Box::new(move |event| { + app.update(|ctx| { + if let Event::KeyDown { keystroke, .. } = &event { + if ctx + .dispatch_keystroke( + window_id, + presenter.borrow().dispatch_path(ctx.as_ref()), + keystroke, + ) + .unwrap() + { + return; + } + } - let actions = - presenter.borrow_mut().dispatch_event(event, ctx.as_ref()); - for action in actions { - ctx.dispatch_action_any( - window_id, - &action.path, - action.name, - action.arg.as_ref(), - ); - } - }) - })); - } - - { - let mut app = self.upgrade(); - let presenter = presenter.clone(); - window.on_resize(Box::new(move |window| { - app.update(|ctx| { - let scene = presenter.borrow_mut().build_scene( - window.size(), - window.scale_factor(), - ctx, - ); - window.present_scene(scene); - }) - })); - } - - { - let presenter = presenter.clone(); - self.on_window_invalidated(window_id, move |invalidation, ctx| { - let mut presenter = presenter.borrow_mut(); - presenter.invalidate(invalidation, ctx.as_ref()); - let scene = - presenter.build_scene(window.size(), window.scale_factor(), ctx); - window.present_scene(scene); - }); - } - - self.on_debug_elements(window_id, move |ctx| { - presenter.borrow().debug_elements(ctx).unwrap() - }); - } + let actions = presenter.borrow_mut().dispatch_event(event, ctx.as_ref()); + for action in actions { + ctx.dispatch_action_any( + window_id, + &action.path, + action.name, + action.arg.as_ref(), + ); + } + }) + })); } + + { + let mut app = self.upgrade(); + let presenter = presenter.clone(); + window.on_resize(Box::new(move |window| { + app.update(|ctx| { + let scene = presenter.borrow_mut().build_scene( + window.size(), + window.scale_factor(), + ctx, + ); + window.present_scene(scene); + }) + })); + } + + { + let presenter = presenter.clone(); + self.on_window_invalidated(window_id, move |invalidation, ctx| { + let mut presenter = presenter.borrow_mut(); + presenter.invalidate(invalidation, ctx.as_ref()); + let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx); + window.present_scene(scene); + }); + } + + self.on_debug_elements(window_id, move |ctx| { + presenter.borrow().debug_elements(ctx).unwrap() + }); } pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index cc70487d77..4fc0f160a3 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -1,6 +1,5 @@ use super::{BoolExt as _, Dispatcher, FontSystem, Window}; use crate::{executor, keymap::Keystroke, platform, Event, Menu, MenuItem}; -use anyhow::Result; use cocoa::{ appkit::{ NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, @@ -237,8 +236,8 @@ impl platform::Platform for MacPlatform { id: usize, options: platform::WindowOptions, executor: Rc, - ) -> Result> { - Ok(Box::new(Window::open(id, options, executor, self.fonts())?)) + ) -> Box { + Box::new(Window::open(id, options, executor, self.fonts())) } fn key_window_id(&self) -> Option { diff --git a/gpui/src/platform/mac/renderer.rs b/gpui/src/platform/mac/renderer.rs index 1c6b64a4c2..52296c69c3 100644 --- a/gpui/src/platform/mac/renderer.rs +++ b/gpui/src/platform/mac/renderer.rs @@ -9,7 +9,6 @@ use crate::{ scene::Layer, Scene, }; -use anyhow::{anyhow, Result}; use cocoa::foundation::NSUInteger; use metal::{MTLPixelFormat, MTLResourceOptions, NSRange}; use shaders::{ToFloat2 as _, ToUchar4 as _}; @@ -41,10 +40,10 @@ impl Renderer { device: metal::Device, pixel_format: metal::MTLPixelFormat, fonts: Arc, - ) -> Result { + ) -> Self { let library = device .new_library_with_data(SHADERS_METALLIB) - .map_err(|message| anyhow!("error building metal library: {}", message))?; + .expect("error building metal library"); let unit_vertices = [ (0., 0.).to_float2(), @@ -73,7 +72,7 @@ impl Renderer { "quad_vertex", "quad_fragment", pixel_format, - )?; + ); let shadow_pipeline_state = build_pipeline_state( &device, &library, @@ -81,7 +80,7 @@ impl Renderer { "shadow_vertex", "shadow_fragment", pixel_format, - )?; + ); let sprite_pipeline_state = build_pipeline_state( &device, &library, @@ -89,7 +88,7 @@ impl Renderer { "sprite_vertex", "sprite_fragment", pixel_format, - )?; + ); let path_atlas_pipeline_state = build_path_atlas_pipeline_state( &device, &library, @@ -97,8 +96,8 @@ impl Renderer { "path_atlas_vertex", "path_atlas_fragment", MTLPixelFormat::R8Unorm, - )?; - Ok(Self { + ); + Self { sprite_cache, path_atlases, quad_pipeline_state, @@ -107,7 +106,7 @@ impl Renderer { path_atlas_pipeline_state, unit_vertices, instances, - }) + } } pub fn render( @@ -713,13 +712,13 @@ fn build_pipeline_state( vertex_fn_name: &str, fragment_fn_name: &str, pixel_format: metal::MTLPixelFormat, -) -> Result { +) -> metal::RenderPipelineState { let vertex_fn = library .get_function(vertex_fn_name, None) - .map_err(|message| anyhow!("error locating vertex function: {}", message))?; + .expect("error locating vertex function"); let fragment_fn = library .get_function(fragment_fn_name, None) - .map_err(|message| anyhow!("error locating fragment function: {}", message))?; + .expect("error locating fragment function"); let descriptor = metal::RenderPipelineDescriptor::new(); descriptor.set_label(label); @@ -737,7 +736,7 @@ fn build_pipeline_state( device .new_render_pipeline_state(&descriptor) - .map_err(|message| anyhow!("could not create render pipeline state: {}", message)) + .expect("could not create render pipeline state") } fn build_path_atlas_pipeline_state( @@ -747,13 +746,13 @@ fn build_path_atlas_pipeline_state( vertex_fn_name: &str, fragment_fn_name: &str, pixel_format: metal::MTLPixelFormat, -) -> Result { +) -> metal::RenderPipelineState { let vertex_fn = library .get_function(vertex_fn_name, None) - .map_err(|message| anyhow!("error locating vertex function: {}", message))?; + .expect("error locating vertex function"); let fragment_fn = library .get_function(fragment_fn_name, None) - .map_err(|message| anyhow!("error locating fragment function: {}", message))?; + .expect("error locating fragment function"); let descriptor = metal::RenderPipelineDescriptor::new(); descriptor.set_label(label); @@ -771,7 +770,7 @@ fn build_path_atlas_pipeline_state( device .new_render_pipeline_state(&descriptor) - .map_err(|message| anyhow!("could not create render pipeline state: {}", message)) + .expect("could not create render pipeline state") } mod shaders { diff --git a/gpui/src/platform/mac/window.rs b/gpui/src/platform/mac/window.rs index 940c00686c..d45077e3b5 100644 --- a/gpui/src/platform/mac/window.rs +++ b/gpui/src/platform/mac/window.rs @@ -4,7 +4,6 @@ use crate::{ platform::{self, Event, WindowContext}, Scene, }; -use anyhow::{anyhow, Result}; use cocoa::{ appkit::{ NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable, @@ -136,7 +135,7 @@ impl Window { options: platform::WindowOptions, executor: Rc, fonts: Arc, - ) -> Result { + ) -> Self { const PIXEL_FORMAT: metal::MTLPixelFormat = metal::MTLPixelFormat::BGRA8Unorm; unsafe { @@ -155,13 +154,10 @@ impl Window { NSBackingStoreBuffered, NO, ); + assert!(!native_window.is_null()); - if native_window == nil { - return Err(anyhow!("window returned nil from initializer")); - } - - let device = metal::Device::system_default() - .ok_or_else(|| anyhow!("could not find default metal device"))?; + let device = + metal::Device::system_default().expect("could not find default metal device"); let layer: id = msg_send![class!(CAMetalLayer), layer]; let _: () = msg_send![layer, setDevice: device.as_ptr()]; @@ -177,9 +173,7 @@ impl Window { let native_view: id = msg_send![VIEW_CLASS, alloc]; let native_view = NSView::init(native_view); - if native_view == nil { - return Err(anyhow!("view return nil from initializer")); - } + assert!(!native_view.is_null()); let window = Self(Rc::new(RefCell::new(WindowState { id, @@ -189,7 +183,7 @@ impl Window { synthetic_drag_counter: 0, executor, scene_to_render: Default::default(), - renderer: Renderer::new(device.clone(), PIXEL_FORMAT, fonts)?, + renderer: Renderer::new(device.clone(), PIXEL_FORMAT, fonts), command_queue: device.new_command_queue(), layer, }))); @@ -230,7 +224,7 @@ impl Window { pool.drain(); - Ok(window) + window } } diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index 76d93c03be..52148ee27c 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -17,7 +17,6 @@ use crate::{ text_layout::Line, Menu, Scene, }; -use anyhow::Result; use async_task::Runnable; pub use event::Event; use std::{any::Any, ops::Range, path::PathBuf, rc::Rc, sync::Arc}; @@ -39,7 +38,7 @@ pub trait Platform { id: usize, options: WindowOptions, executor: Rc, - ) -> Result>; + ) -> Box; fn key_window_id(&self) -> Option; fn prompt_for_paths(&self, options: PathPromptOptions) -> Option>; fn quit(&self); diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index fb120a8eb5..eed3709cba 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -56,8 +56,8 @@ impl super::Platform for Platform { _: usize, options: super::WindowOptions, _executor: Rc, - ) -> anyhow::Result> { - Ok(Box::new(Window::new(options.bounds.size()))) + ) -> Box { + Box::new(Window::new(options.bounds.size())) } fn key_window_id(&self) -> Option { From 902b0f7dfa481a4f06d97f67c03a6a836e248a02 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 12 Apr 2021 15:42:33 -0700 Subject: [PATCH 6/6] Dispatch menu commands on the focused view Co-Authored-By: Nathan Sobo --- gpui/src/app.rs | 61 +++++++++++++++++++++++++----------------------- zed/src/menus.rs | 10 ++++---- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 68f59dea91..4f5fb7b905 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -134,8 +134,19 @@ impl App { let ctx = app.0.clone(); platform.on_menu_command(Box::new(move |command, arg| { - ctx.borrow_mut() - .dispatch_global_action_with_dyn_arg(command, arg.unwrap_or(&())); + let mut ctx = ctx.borrow_mut(); + if let Some(key_window_id) = ctx.platform.key_window_id() { + if let Some((presenter, _)) = + ctx.presenters_and_platform_windows.get(&key_window_id) + { + let presenter = presenter.clone(); + let path = presenter.borrow().dispatch_path(ctx.as_ref()); + if ctx.dispatch_action_any(key_window_id, &path, command, arg.unwrap_or(&())) { + return; + } + } + } + ctx.dispatch_global_action_any(command, arg.unwrap_or(&())); })); app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0)); @@ -373,8 +384,8 @@ pub struct MutableAppContext { subscriptions: HashMap>, observations: HashMap>, window_invalidations: HashMap, - invalidation_callbacks: - HashMap>, + presenters_and_platform_windows: + HashMap>, Box)>, debug_elements_callbacks: HashMap crate::json::Value>>, foreground: Rc, future_handlers: Rc>>, @@ -412,7 +423,7 @@ impl MutableAppContext { subscriptions: HashMap::new(), observations: HashMap::new(), window_invalidations: HashMap::new(), - invalidation_callbacks: HashMap::new(), + presenters_and_platform_windows: HashMap::new(), debug_elements_callbacks: HashMap::new(), foreground, future_handlers: Default::default(), @@ -444,15 +455,6 @@ impl MutableAppContext { &self.ctx.background } - pub fn on_window_invalidated(&mut self, window_id: usize, callback: F) - where - F: 'static + FnMut(WindowInvalidation, &mut MutableAppContext), - { - self.invalidation_callbacks - .insert(window_id, Box::new(callback)); - self.update_windows(); - } - pub fn on_debug_elements(&mut self, window_id: usize, callback: F) where F: 'static + Fn(&AppContext) -> crate::json::Value, @@ -628,7 +630,7 @@ impl MutableAppContext { } if !halted_dispatch { - self.dispatch_global_action_with_dyn_arg(name, arg); + self.dispatch_global_action_any(name, arg); } self.flush_effects(); @@ -636,10 +638,10 @@ impl MutableAppContext { } pub fn dispatch_global_action(&mut self, name: &str, arg: T) { - self.dispatch_global_action_with_dyn_arg(name, Box::new(arg).as_ref()); + self.dispatch_global_action_any(name, Box::new(arg).as_ref()); } - fn dispatch_global_action_with_dyn_arg(&mut self, name: &str, arg: &dyn Any) { + fn dispatch_global_action_any(&mut self, name: &str, arg: &dyn Any) { if let Some((name, mut handlers)) = self.global_actions.remove_entry(name) { self.pending_flushes += 1; for handler in handlers.iter_mut().rev() { @@ -798,15 +800,8 @@ impl MutableAppContext { })); } - { - let presenter = presenter.clone(); - self.on_window_invalidated(window_id, move |invalidation, ctx| { - let mut presenter = presenter.borrow_mut(); - presenter.invalidate(invalidation, ctx.as_ref()); - let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx); - window.present_scene(scene); - }); - } + self.presenters_and_platform_windows + .insert(window_id, (presenter.clone(), window)); self.on_debug_elements(window_id, move |ctx| { presenter.borrow().debug_elements(ctx).unwrap() @@ -911,9 +906,17 @@ impl MutableAppContext { std::mem::swap(&mut invalidations, &mut self.window_invalidations); for (window_id, invalidation) in invalidations { - if let Some(mut callback) = self.invalidation_callbacks.remove(&window_id) { - callback(invalidation, self); - self.invalidation_callbacks.insert(window_id, callback); + if let Some((presenter, mut window)) = + self.presenters_and_platform_windows.remove(&window_id) + { + { + let mut presenter = presenter.borrow_mut(); + presenter.invalidate(invalidation, self.as_ref()); + let scene = presenter.build_scene(window.size(), window.scale_factor(), self); + window.present_scene(scene); + } + self.presenters_and_platform_windows + .insert(window_id, (presenter, window)); } } } diff --git a/zed/src/menus.rs b/zed/src/menus.rs index 03681f4dd2..08afb1e990 100644 --- a/zed/src/menus.rs +++ b/zed/src/menus.rs @@ -37,32 +37,32 @@ pub fn menus(settings: Receiver) -> Vec> { MenuItem::Action { name: "Undo", keystroke: Some("cmd-z"), - action: "editor:undo", + action: "buffer:undo", arg: None, }, MenuItem::Action { name: "Redo", keystroke: Some("cmd-Z"), - action: "editor:redo", + action: "buffer:redo", arg: None, }, MenuItem::Separator, MenuItem::Action { name: "Cut", keystroke: Some("cmd-x"), - action: "editor:cut", + action: "buffer:cut", arg: None, }, MenuItem::Action { name: "Copy", keystroke: Some("cmd-c"), - action: "editor:copy", + action: "buffer:copy", arg: None, }, MenuItem::Action { name: "Paste", keystroke: Some("cmd-v"), - action: "editor:paste", + action: "buffer:paste", arg: None, }, ],