diff --git a/gpui/src/app.rs b/gpui/src/app.rs index b11f5c63da..cc8b5bd032 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -112,10 +112,12 @@ impl App { asset_source: A, f: F, ) -> T { + let lifecycle = platform::test::lifecycle(); let platform = platform::test::platform(); let foreground = Rc::new(executor::Foreground::test()); let cx = Rc::new(RefCell::new(MutableAppContext::new( foreground, + Rc::new(lifecycle), Rc::new(platform), asset_source, ))); @@ -129,11 +131,13 @@ impl App { Fn: FnOnce(TestAppContext) -> F, F: Future, { + let lifecycle = Rc::new(platform::test::lifecycle()); let platform = Rc::new(platform::test::platform()); let foreground = Rc::new(executor::Foreground::test()); let cx = TestAppContext( Rc::new(RefCell::new(MutableAppContext::new( foreground.clone(), + lifecycle, platform.clone(), asset_source, ))), @@ -146,16 +150,18 @@ impl App { } pub fn new(asset_source: impl AssetSource) -> Result { + let lifecycle = platform::current::lifecycle(); let platform = platform::current::platform(); let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?); let app = Self(Rc::new(RefCell::new(MutableAppContext::new( foreground, + lifecycle.clone(), platform.clone(), asset_source, )))); let cx = app.0.clone(); - platform.on_menu_command(Box::new(move |command, arg| { + lifecycle.on_menu_command(Box::new(move |command, arg| { let mut cx = cx.borrow_mut(); if let Some(key_window_id) = cx.platform.key_window_id() { if let Some((presenter, _)) = cx.presenters_and_platform_windows.get(&key_window_id) @@ -181,8 +187,8 @@ impl App { { let cx = self.0.clone(); self.0 - .borrow() - .platform + .borrow_mut() + .lifecycle .on_become_active(Box::new(move || callback(&mut *cx.borrow_mut()))); self } @@ -193,8 +199,8 @@ impl App { { let cx = self.0.clone(); self.0 - .borrow() - .platform + .borrow_mut() + .lifecycle .on_resign_active(Box::new(move || callback(&mut *cx.borrow_mut()))); self } @@ -204,9 +210,12 @@ impl App { F: 'static + FnMut(Event, &mut MutableAppContext) -> bool, { let cx = self.0.clone(); - self.0.borrow().platform.on_event(Box::new(move |event| { - callback(event, &mut *cx.borrow_mut()) - })); + self.0 + .borrow_mut() + .lifecycle + .on_event(Box::new(move |event| { + callback(event, &mut *cx.borrow_mut()) + })); self } @@ -216,8 +225,8 @@ impl App { { let cx = self.0.clone(); self.0 - .borrow() - .platform + .borrow_mut() + .lifecycle .on_open_files(Box::new(move |paths| { callback(paths, &mut *cx.borrow_mut()) })); @@ -228,8 +237,8 @@ impl App { where F: 'static + FnOnce(&mut MutableAppContext), { - let platform = self.0.borrow().platform.clone(); - platform.run(Box::new(move || { + let lifecycle = self.0.borrow().lifecycle.clone(); + lifecycle.run(Box::new(move || { let mut cx = self.0.borrow_mut(); on_finish_launching(&mut *cx); })) @@ -516,6 +525,7 @@ type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext); pub struct MutableAppContext { weak_self: Option>>, + lifecycle: Rc, platform: Rc, assets: Arc, cx: AppContext, @@ -537,14 +547,16 @@ pub struct MutableAppContext { } impl MutableAppContext { - pub fn new( + fn new( foreground: Rc, + lifecycle: Rc, platform: Rc, asset_source: impl AssetSource, ) -> Self { let fonts = platform.fonts(); Self { weak_self: None, + lifecycle, platform, assets: Arc::new(AssetCache::new(asset_source)), cx: AppContext { @@ -704,8 +716,8 @@ impl MutableAppContext { result } - pub fn set_menus(&self, menus: Vec) { - self.platform.set_menus(menus); + pub fn set_menus(&mut self, menus: Vec) { + self.lifecycle.set_menus(menus); } fn prompt( diff --git a/gpui/src/platform.rs b/gpui/src/platform.rs index a6c45b2753..4205539195 100644 --- a/gpui/src/platform.rs +++ b/gpui/src/platform.rs @@ -27,14 +27,17 @@ use std::{ sync::Arc, }; -pub trait Platform { +pub(crate) trait Lifecycle { 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>); fn on_open_files(&self, callback: Box)>); + fn set_menus(&self, menus: Vec); fn run(&self, on_finish_launching: Box ()>); +} +pub trait Platform { fn dispatcher(&self) -> Arc; fn fonts(&self) -> Arc; @@ -59,7 +62,6 @@ pub trait Platform { fn quit(&self); fn write_to_clipboard(&self, item: ClipboardItem); fn read_from_clipboard(&self) -> Option; - fn set_menus(&self, menus: Vec); } pub trait Dispatcher: Send + Sync { diff --git a/gpui/src/platform/mac.rs b/gpui/src/platform/mac.rs index c2a88c12a4..96c3a55c95 100644 --- a/gpui/src/platform/mac.rs +++ b/gpui/src/platform/mac.rs @@ -11,11 +11,15 @@ mod window; use cocoa::base::{BOOL, NO, YES}; pub use dispatcher::Dispatcher; pub use fonts::FontSystem; -use platform::MacPlatform; +use platform::{MacLifecycle, MacPlatform}; use std::rc::Rc; use window::Window; -pub fn platform() -> Rc { +pub(crate) fn lifecycle() -> Rc { + Rc::new(MacLifecycle::default()) +} + +pub(crate) fn platform() -> Rc { Rc::new(MacPlatform::new()) } diff --git a/gpui/src/platform/mac/platform.rs b/gpui/src/platform/mac/platform.rs index e7ccd280b4..317bb162fe 100644 --- a/gpui/src/platform/mac/platform.rs +++ b/gpui/src/platform/mac/platform.rs @@ -32,7 +32,7 @@ use std::{ sync::Arc, }; -const MAC_PLATFORM_IVAR: &'static str = "platform"; +const MAC_LIFECYCLE_IVAR: &'static str = "lifecycle"; static mut APP_CLASS: *const Class = ptr::null(); static mut APP_DELEGATE_CLASS: *const Class = ptr::null(); @@ -40,7 +40,7 @@ static mut APP_DELEGATE_CLASS: *const Class = ptr::null(); unsafe fn build_classes() { APP_CLASS = { let mut decl = ClassDecl::new("GPUIApplication", class!(NSApplication)).unwrap(); - decl.add_ivar::<*mut c_void>(MAC_PLATFORM_IVAR); + decl.add_ivar::<*mut c_void>(MAC_LIFECYCLE_IVAR); decl.add_method( sel!(sendEvent:), send_event as extern "C" fn(&mut Object, Sel, id), @@ -50,7 +50,7 @@ unsafe fn build_classes() { APP_DELEGATE_CLASS = { let mut decl = ClassDecl::new("GPUIApplicationDelegate", class!(NSResponder)).unwrap(); - decl.add_ivar::<*mut c_void>(MAC_PLATFORM_IVAR); + decl.add_ivar::<*mut c_void>(MAC_LIFECYCLE_IVAR); decl.add_method( sel!(applicationDidFinishLaunching:), did_finish_launching as extern "C" fn(&mut Object, Sel, id), @@ -75,43 +75,26 @@ unsafe fn build_classes() { } } -pub struct MacPlatform { - dispatcher: Arc, - fonts: Arc, - callbacks: RefCell, - menu_item_actions: RefCell>)>>, - pasteboard: id, - text_hash_pasteboard_type: id, - metadata_pasteboard_type: id, -} +#[derive(Default)] +pub struct MacLifecycle(RefCell); #[derive(Default)] -struct Callbacks { +pub struct MacLifecycleState { become_active: Option>, resign_active: Option>, event: Option bool>>, menu_command: Option)>>, open_files: Option)>>, finish_launching: Option ()>>, + menu_actions: Vec<(String, Option>)>, } -impl MacPlatform { - pub fn new() -> Self { - Self { - dispatcher: Arc::new(Dispatcher), - fonts: Arc::new(FontSystem::new()), - callbacks: Default::default(), - menu_item_actions: Default::default(), - pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) }, - text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") }, - metadata_pasteboard_type: unsafe { ns_string("zed-metadata") }, - } - } - +impl MacLifecycle { 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(); + let mut state = self.0.borrow_mut(); + + state.menu_actions.clear(); for menu_config in menus { let menu_bar_item = NSMenuItem::new(nil).autorelease(); @@ -170,9 +153,9 @@ impl MacPlatform { .autorelease(); } - let tag = menu_item_actions.len() as NSInteger; + let tag = state.menu_actions.len() as NSInteger; let _: () = msg_send![item, setTag: tag]; - menu_item_actions.push((action.to_string(), arg)); + state.menu_actions.push((action.to_string(), arg)); } } @@ -185,6 +168,76 @@ impl MacPlatform { menu_bar } +} + +impl platform::Lifecycle for MacLifecycle { + fn on_become_active(&self, callback: Box) { + self.0.borrow_mut().become_active = Some(callback); + } + + fn on_resign_active(&self, callback: Box) { + self.0.borrow_mut().resign_active = Some(callback); + } + + fn on_event(&self, callback: Box bool>) { + self.0.borrow_mut().event = Some(callback); + } + + fn on_menu_command(&self, callback: Box)>) { + self.0.borrow_mut().menu_command = Some(callback); + } + + fn on_open_files(&self, callback: Box)>) { + self.0.borrow_mut().open_files = Some(callback); + } + + fn set_menus(&self, menus: Vec) { + unsafe { + let app: id = msg_send![APP_CLASS, sharedApplication]; + app.setMainMenu_(self.create_menu_bar(menus)); + } + } + + fn run(&self, on_finish_launching: Box ()>) { + self.0.borrow_mut().finish_launching = Some(on_finish_launching); + + unsafe { + let app: id = msg_send![APP_CLASS, sharedApplication]; + let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; + app.setDelegate_(app_delegate); + + let self_ptr = self as *const Self as *const c_void; + (*app).set_ivar(MAC_LIFECYCLE_IVAR, self_ptr); + (*app_delegate).set_ivar(MAC_LIFECYCLE_IVAR, self_ptr); + + let pool = NSAutoreleasePool::new(nil); + app.run(); + pool.drain(); + + (*app).set_ivar(MAC_LIFECYCLE_IVAR, null_mut::()); + (*app.delegate()).set_ivar(MAC_LIFECYCLE_IVAR, null_mut::()); + } + } +} + +pub struct MacPlatform { + dispatcher: Arc, + fonts: Arc, + pasteboard: id, + text_hash_pasteboard_type: id, + metadata_pasteboard_type: id, +} + +impl MacPlatform { + pub fn new() -> Self { + Self { + dispatcher: Arc::new(Dispatcher), + fonts: Arc::new(FontSystem::new()), + pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) }, + text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") }, + metadata_pasteboard_type: unsafe { ns_string("zed-metadata") }, + } + } unsafe fn read_from_pasteboard(&self, kind: id) -> Option<&[u8]> { let data = self.pasteboard.dataForType(kind); @@ -200,47 +253,6 @@ impl MacPlatform { } impl platform::Platform for MacPlatform { - fn on_become_active(&self, callback: Box) { - self.callbacks.borrow_mut().become_active = Some(callback); - } - - fn on_resign_active(&self, callback: Box) { - self.callbacks.borrow_mut().resign_active = Some(callback); - } - - fn on_event(&self, callback: Box bool>) { - self.callbacks.borrow_mut().event = Some(callback); - } - - fn on_menu_command(&self, callback: Box)>) { - self.callbacks.borrow_mut().menu_command = Some(callback); - } - - fn on_open_files(&self, callback: Box)>) { - self.callbacks.borrow_mut().open_files = Some(callback); - } - - fn run(&self, on_finish_launching: Box ()>) { - self.callbacks.borrow_mut().finish_launching = Some(on_finish_launching); - - unsafe { - let app: id = msg_send![APP_CLASS, sharedApplication]; - let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new]; - app.setDelegate_(app_delegate); - - let self_ptr = self as *const Self as *const c_void; - (*app).set_ivar(MAC_PLATFORM_IVAR, self_ptr); - (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, self_ptr); - - let pool = NSAutoreleasePool::new(nil); - app.run(); - pool.drain(); - - (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); - (*app.delegate()).set_ivar(MAC_PLATFORM_IVAR, null_mut::()); - } - } - fn dispatcher(&self) -> Arc { self.dispatcher.clone() } @@ -434,26 +446,19 @@ impl platform::Platform for MacPlatform { } } } - - fn set_menus(&self, menus: Vec) { - unsafe { - let app: id = msg_send![APP_CLASS, sharedApplication]; - app.setMainMenu_(self.create_menu_bar(menus)); - } - } } -unsafe fn get_platform(object: &mut Object) -> &MacPlatform { - let platform_ptr: *mut c_void = *object.get_ivar(MAC_PLATFORM_IVAR); +unsafe fn get_lifecycle(object: &mut Object) -> &MacLifecycle { + let platform_ptr: *mut c_void = *object.get_ivar(MAC_LIFECYCLE_IVAR); assert!(!platform_ptr.is_null()); - &*(platform_ptr as *const MacPlatform) + &*(platform_ptr as *const MacLifecycle) } extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) { unsafe { if let Some(event) = Event::from_native(native_event, None) { - let platform = get_platform(this); - if let Some(callback) = platform.callbacks.borrow_mut().event.as_mut() { + let platform = get_lifecycle(this); + if let Some(callback) = platform.0.borrow_mut().event.as_mut() { if callback(event) { return; } @@ -469,23 +474,24 @@ extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) { let app: id = msg_send![APP_CLASS, sharedApplication]; app.setActivationPolicy_(NSApplicationActivationPolicyRegular); - let platform = get_platform(this); - if let Some(callback) = platform.callbacks.borrow_mut().finish_launching.take() { + let platform = get_lifecycle(this); + let callback = platform.0.borrow_mut().finish_launching.take(); + if let Some(callback) = callback { callback(); } } } extern "C" fn did_become_active(this: &mut Object, _: Sel, _: id) { - let platform = unsafe { get_platform(this) }; - if let Some(callback) = platform.callbacks.borrow_mut().become_active.as_mut() { + let platform = unsafe { get_lifecycle(this) }; + if let Some(callback) = platform.0.borrow_mut().become_active.as_mut() { callback(); } } extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) { - let platform = unsafe { get_platform(this) }; - if let Some(callback) = platform.callbacks.borrow_mut().resign_active.as_mut() { + let platform = unsafe { get_lifecycle(this) }; + if let Some(callback) = platform.0.borrow_mut().resign_active.as_mut() { callback(); } } @@ -506,19 +512,19 @@ extern "C" fn open_files(this: &mut Object, _: Sel, _: id, paths: id) { }) .collect::>() }; - let platform = unsafe { get_platform(this) }; - if let Some(callback) = platform.callbacks.borrow_mut().open_files.as_mut() { + let platform = unsafe { get_lifecycle(this) }; + if let Some(callback) = platform.0.borrow_mut().open_files.as_mut() { callback(paths); } } extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) { unsafe { - let platform = get_platform(this); - if let Some(callback) = platform.callbacks.borrow_mut().menu_command.as_mut() { + let platform = get_lifecycle(this); + if let Some(callback) = platform.0.borrow_mut().menu_command.as_mut() { let tag: NSInteger = msg_send![item, tag]; let index = tag as usize; - if let Some((action, arg)) = platform.menu_item_actions.borrow().get(index) { + if let Some((action, arg)) = platform.0.borrow_mut().menu_actions.get(index) { callback(action, arg.as_ref().map(Box::as_ref)); } } diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index d3a0c0d3f1..334d674951 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -8,6 +8,8 @@ use std::{ sync::Arc, }; +pub(crate) struct Lifecycle; + pub(crate) struct Platform { dispatcher: Arc, fonts: Arc, @@ -27,6 +29,24 @@ pub struct Window { pub(crate) last_prompt: RefCell>>, } +impl super::Lifecycle for Lifecycle { + fn on_menu_command(&self, _: Box)>) {} + + fn on_become_active(&self, _: Box) {} + + fn on_resign_active(&self, _: Box) {} + + fn on_event(&self, _: Box bool>) {} + + fn on_open_files(&self, _: Box)>) {} + + fn set_menus(&self, _: Vec) {} + + fn run(&self, _on_finish_launching: Box ()>) { + unimplemented!() + } +} + impl Platform { fn new() -> Self { Self { @@ -54,20 +74,6 @@ impl Platform { } impl super::Platform for Platform { - fn on_menu_command(&self, _: Box)>) {} - - fn on_become_active(&self, _: Box) {} - - fn on_resign_active(&self, _: Box) {} - - fn on_event(&self, _: Box bool>) {} - - fn on_open_files(&self, _: Box)>) {} - - fn run(&self, _on_finish_launching: Box ()>) { - unimplemented!() - } - fn dispatcher(&self) -> Arc { self.dispatcher.clone() } @@ -91,8 +97,6 @@ impl super::Platform for Platform { None } - fn set_menus(&self, _menus: Vec) {} - fn quit(&self) {} fn prompt_for_paths( @@ -175,6 +179,10 @@ impl super::Window for Window { } } +pub(crate) fn lifecycle() -> Lifecycle { + Lifecycle +} + pub(crate) fn platform() -> Platform { Platform::new() }