diff --git a/Cargo.lock b/Cargo.lock index 5c3136f082..f60dd21a41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1298,6 +1298,39 @@ dependencies = [ "crossbeam-utils 0.8.2", ] +[[package]] +name = "rust-embed" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe1fe6aac5d6bb9e1ffd81002340363272a7648234ec7bdfac5ee202cb65523" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed91c41c42ef7bf687384439c312e75e0da9c149b0390889b94de3c7d9d9e66" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a512219132473ab0a77b52077059f1c47ce4af7fbdc94503e9862a34422876d" +dependencies = [ + "walkdir", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1679,6 +1712,7 @@ dependencies = [ "num_cpus", "parking_lot", "rand 0.8.3", + "rust-embed", "serde_json", "simplelog", "smallvec", diff --git a/gpui/src/app.rs b/gpui/src/app.rs index cf58a389b6..6777e16a13 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -2,12 +2,14 @@ use crate::{ elements::Element, executor::{self, ForegroundTask}, keymap::{self, Keystroke}, - platform::{self, App as _}, + platform::{self, App as _, WindowOptions}, util::post_inc, + AssetCache, AssetSource, FontCache, Presenter, }; use anyhow::{anyhow, Result}; use keymap::MatchResult; use parking_lot::Mutex; +use pathfinder_geometry::{rect::RectF, vector::vec2f}; use smol::{channel, prelude::*}; use std::{ any::{type_name, Any, TypeId}, @@ -66,22 +68,28 @@ pub trait UpdateView { pub struct App(Rc>); impl App { - pub fn test>(f: impl FnOnce(App) -> F) -> T { + pub fn test>( + asset_source: impl AssetSource, + f: impl FnOnce(App) -> F, + ) -> T { let platform = platform::current::app(); // TODO: Make a test platform app let foreground = Rc::new(executor::Foreground::test()); let app = Self(Rc::new(RefCell::new(MutableAppContext::new( foreground.clone(), Arc::new(platform), + asset_source, )))); app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0)); smol::block_on(foreground.run(f(app))) } - pub fn new() -> Result { + pub fn new(asset_source: impl AssetSource) -> Result { let platform = Arc::new(platform::current::app()); let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?); let app = Self(Rc::new(RefCell::new(MutableAppContext::new( - foreground, platform, + foreground, + platform, + asset_source, )))); app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0)); Ok(app) @@ -217,7 +225,7 @@ impl App { } pub fn read T>(&mut self, callback: F) -> T { - callback(self.0.borrow().ctx()) + callback(self.0.borrow().downgrade()) } pub fn update T>(&mut self, callback: F) -> T { @@ -234,7 +242,7 @@ impl App { F: FnOnce(&T, &AppContext) -> S, { let state = self.0.borrow(); - read(state.view(handle), state.ctx()) + read(state.view(handle), state.downgrade()) } pub fn finish_pending_tasks(&self) -> impl Future { @@ -277,6 +285,8 @@ type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext); pub struct MutableAppContext { platform: Arc, + fonts: Arc, + assets: Arc, ctx: AppContext, actions: HashMap>>>, global_actions: HashMap>>, @@ -301,9 +311,15 @@ pub struct MutableAppContext { } impl MutableAppContext { - pub fn new(foreground: Rc, platform: Arc) -> Self { + pub fn new( + foreground: Rc, + platform: Arc, + asset_source: impl AssetSource, + ) -> Self { Self { platform, + fonts: Arc::new(FontCache::new()), + assets: Arc::new(AssetCache::new(asset_source)), ctx: AppContext { models: HashMap::new(), windows: HashMap::new(), @@ -331,7 +347,7 @@ impl MutableAppContext { } } - pub fn ctx(&self) -> &AppContext { + pub fn downgrade(&self) -> &AppContext { &self.ctx } @@ -532,7 +548,7 @@ impl MutableAppContext { .get(&window_id) .and_then(|w| w.views.get(view_id)) { - context.extend(view.keymap_context(self.ctx())); + context.extend(view.keymap_context(self.downgrade())); context_chain.push(context.clone()); } else { return Err(anyhow!( @@ -592,11 +608,30 @@ impl MutableAppContext { self.ctx.windows.get_mut(&window_id).unwrap().root_view = Some(root_handle.clone().into()); self.focus(window_id, root_handle.id()); - // self.emit_ui_update(UiUpdate::OpenWindow { - // window_id, - // width: 1024.0, - // height: 768.0, - // }); + match self.platform.open_window( + 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(window) => { + let presenter = Rc::new(RefCell::new(Presenter::new( + window_id, + self.fonts.clone(), + self.assets.clone(), + self, + ))); + + self.on_window_invalidated(window_id, move |invalidation, ctx| { + let mut presenter = presenter.borrow_mut(); + presenter.invalidate(invalidation, ctx.downgrade()); + let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx); + window.render_scene(scene); + }); + } + } (window_id, root_handle) } @@ -1606,7 +1641,7 @@ impl<'a, T: View> ViewContext<'a, T> { window_id: self.window_id, view_id: self.view_id, callback: Box::new(move |view, payload, app, window_id, view_id| { - if let Some(emitter_handle) = emitter_handle.upgrade(app.ctx()) { + if let Some(emitter_handle) = emitter_handle.upgrade(app.downgrade()) { let model = view.downcast_mut().expect("downcast is type safe"); let payload = payload.downcast_ref().expect("downcast is type safe"); let mut ctx = ViewContext::new(app, window_id, view_id); @@ -1632,7 +1667,7 @@ impl<'a, T: View> ViewContext<'a, T> { window_id: self.window_id, view_id: self.view_id, callback: Box::new(move |view, payload, app, window_id, view_id| { - if let Some(emitter_handle) = emitter_handle.upgrade(app.ctx()) { + if let Some(emitter_handle) = emitter_handle.upgrade(app.downgrade()) { let model = view.downcast_mut().expect("downcast is type safe"); let payload = payload.downcast_ref().expect("downcast is type safe"); let mut ctx = ViewContext::new(app, window_id, view_id); @@ -2261,42 +2296,43 @@ mod tests { } } - let mut app = App::new().unwrap(); - let app = &mut app; + App::test((), |mut app| async move { + let app = &mut app; - let handle_1 = app.add_model(|ctx| Model::new(None, ctx)); - let handle_2 = app.add_model(|ctx| Model::new(Some(handle_1.clone()), ctx)); - assert_eq!(app.0.borrow().ctx.models.len(), 2); + let handle_1 = app.add_model(|ctx| Model::new(None, ctx)); + let handle_2 = app.add_model(|ctx| Model::new(Some(handle_1.clone()), ctx)); + assert_eq!(app.0.borrow().ctx.models.len(), 2); - handle_1.update(app, |model, ctx| { - model.events.push("updated".into()); - ctx.emit(1); - ctx.notify(); - ctx.emit(2); - }); - handle_1.read(app, |model, _| { - assert_eq!(model.events, vec!["updated".to_string()]); - }); - handle_2.read(app, |model, _| { - assert_eq!( - model.events, - vec![ - "observed event 1".to_string(), - "notified".to_string(), - "observed event 2".to_string(), - ] - ); - }); + handle_1.update(app, |model, ctx| { + model.events.push("updated".into()); + ctx.emit(1); + ctx.notify(); + ctx.emit(2); + }); + handle_1.read(app, |model, _| { + assert_eq!(model.events, vec!["updated".to_string()]); + }); + handle_2.read(app, |model, _| { + assert_eq!( + model.events, + vec![ + "observed event 1".to_string(), + "notified".to_string(), + "observed event 2".to_string(), + ] + ); + }); - handle_2.update(app, |model, _| { - drop(handle_1); - model.other.take(); - }); + handle_2.update(app, |model, _| { + drop(handle_1); + model.other.take(); + }); - let app_state = app.0.borrow(); - assert_eq!(app_state.ctx.models.len(), 1); - assert!(app_state.subscriptions.is_empty()); - assert!(app_state.observations.is_empty()); + let app_state = app.0.borrow(); + assert_eq!(app_state.ctx.models.len(), 1); + assert!(app_state.subscriptions.is_empty()); + assert!(app_state.observations.is_empty()); + }) } #[test] @@ -2310,28 +2346,28 @@ mod tests { type Event = usize; } - let mut app = App::new().unwrap(); - let app = &mut app; + App::test((), |mut app| async move { + let app = &mut app; + let handle_1 = app.add_model(|_| Model::default()); + let handle_2 = app.add_model(|_| Model::default()); + let handle_2b = handle_2.clone(); - let handle_1 = app.add_model(|_| Model::default()); - let handle_2 = app.add_model(|_| Model::default()); - let handle_2b = handle_2.clone(); + handle_1.update(app, |_, c| { + c.subscribe(&handle_2, move |model: &mut Model, event, c| { + model.events.push(*event); - handle_1.update(app, |_, c| { - c.subscribe(&handle_2, move |model: &mut Model, event, c| { - model.events.push(*event); - - c.subscribe(&handle_2b, |model, event, _| { - model.events.push(*event * 2); + c.subscribe(&handle_2b, |model, event, _| { + model.events.push(*event * 2); + }); }); }); - }); - handle_2.update(app, |_, c| c.emit(7)); - handle_1.read(app, |model, _| assert_eq!(model.events, vec![7])); + handle_2.update(app, |_, c| c.emit(7)); + handle_1.read(app, |model, _| assert_eq!(model.events, vec![7])); - handle_2.update(app, |_, c| c.emit(5)); - handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5])); + handle_2.update(app, |_, c| c.emit(5)); + handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5])); + }) } #[test] @@ -2346,33 +2382,33 @@ mod tests { type Event = (); } - let mut app = App::new().unwrap(); + App::test((), |mut app| async move { + let app = &mut app; + let handle_1 = app.add_model(|_| Model::default()); + let handle_2 = app.add_model(|_| Model::default()); + let handle_2b = handle_2.clone(); - let app = &mut app; - let handle_1 = app.add_model(|_| Model::default()); - let handle_2 = app.add_model(|_| Model::default()); - let handle_2b = handle_2.clone(); - - handle_1.update(app, |_, c| { - c.observe(&handle_2, move |model, observed, c| { - model.events.push(observed.as_ref(c).count); - c.observe(&handle_2b, |model, observed, c| { - model.events.push(observed.as_ref(c).count * 2); + handle_1.update(app, |_, c| { + c.observe(&handle_2, move |model, observed, c| { + model.events.push(observed.as_ref(c).count); + c.observe(&handle_2b, |model, observed, c| { + model.events.push(observed.as_ref(c).count * 2); + }); }); }); - }); - handle_2.update(app, |model, c| { - model.count = 7; - c.notify() - }); - handle_1.read(app, |model, _| assert_eq!(model.events, vec![7])); + handle_2.update(app, |model, c| { + model.count = 7; + c.notify() + }); + handle_1.read(app, |model, _| assert_eq!(model.events, vec![7])); - handle_2.update(app, |model, c| { - model.count = 5; - c.notify() - }); - handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5])) + handle_2.update(app, |model, c| { + model.count = 5; + c.notify() + }); + handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5])) + }) } #[test] @@ -2386,7 +2422,7 @@ mod tests { type Event = (); } - App::test(|mut app| async move { + App::test((), |mut app| async move { let handle = app.add_model(|_| Model::default()); handle .update(&mut app, |_, c| { @@ -2419,7 +2455,7 @@ mod tests { type Event = (); } - App::test(|mut app| async move { + App::test((), |mut app| async move { let handle = app.add_model(|_| Model::default()); handle .update(&mut app, |_, c| { @@ -2476,41 +2512,41 @@ mod tests { } } - let mut app = App::new().unwrap(); - let app = &mut app; + App::test((), |mut app| async move { + let app = &mut app; + let (window_id, _) = app.add_window(|ctx| View::new(None, ctx)); + let handle_1 = app.add_view(window_id, |ctx| View::new(None, ctx)); + let handle_2 = app.add_view(window_id, |ctx| View::new(Some(handle_1.clone()), ctx)); + assert_eq!(app.0.borrow().ctx.windows[&window_id].views.len(), 3); - let (window_id, _) = app.add_window(|ctx| View::new(None, ctx)); - let handle_1 = app.add_view(window_id, |ctx| View::new(None, ctx)); - let handle_2 = app.add_view(window_id, |ctx| View::new(Some(handle_1.clone()), ctx)); - assert_eq!(app.0.borrow().ctx.windows[&window_id].views.len(), 3); + handle_1.update(app, |view, ctx| { + view.events.push("updated".into()); + ctx.emit(1); + ctx.emit(2); + }); + handle_1.read(app, |view, _| { + assert_eq!(view.events, vec!["updated".to_string()]); + }); + handle_2.read(app, |view, _| { + assert_eq!( + view.events, + vec![ + "observed event 1".to_string(), + "observed event 2".to_string(), + ] + ); + }); - handle_1.update(app, |view, ctx| { - view.events.push("updated".into()); - ctx.emit(1); - ctx.emit(2); - }); - handle_1.read(app, |view, _| { - assert_eq!(view.events, vec!["updated".to_string()]); - }); - handle_2.read(app, |view, _| { - assert_eq!( - view.events, - vec![ - "observed event 1".to_string(), - "observed event 2".to_string(), - ] - ); - }); + handle_2.update(app, |view, _| { + drop(handle_1); + view.other.take(); + }); - handle_2.update(app, |view, _| { - drop(handle_1); - view.other.take(); - }); - - let app_state = app.0.borrow(); - assert_eq!(app_state.ctx.windows[&window_id].views.len(), 2); - assert!(app_state.subscriptions.is_empty()); - assert!(app_state.observations.is_empty()); + let app_state = app.0.borrow(); + assert_eq!(app_state.ctx.windows[&window_id].views.len(), 2); + assert!(app_state.subscriptions.is_empty()); + assert!(app_state.observations.is_empty()); + }) } #[test] @@ -2540,36 +2576,36 @@ mod tests { type Event = usize; } - let mut app = App::new().unwrap(); - let app = &mut app; + App::test((), |mut app| async move { + let app = &mut app; + let (window_id, handle_1) = app.add_window(|_| View::default()); + let handle_2 = app.add_view(window_id, |_| View::default()); + let handle_2b = handle_2.clone(); + let handle_3 = app.add_model(|_| Model); - let (window_id, handle_1) = app.add_window(|_| View::default()); - let handle_2 = app.add_view(window_id, |_| View::default()); - let handle_2b = handle_2.clone(); - let handle_3 = app.add_model(|_| Model); + handle_1.update(app, |_, c| { + c.subscribe_to_view(&handle_2, move |me, _, event, c| { + me.events.push(*event); - handle_1.update(app, |_, c| { - c.subscribe_to_view(&handle_2, move |me, _, event, c| { - me.events.push(*event); - - c.subscribe_to_view(&handle_2b, |me, _, event, _| { - me.events.push(*event * 2); + c.subscribe_to_view(&handle_2b, |me, _, event, _| { + me.events.push(*event * 2); + }); }); + + c.subscribe_to_model(&handle_3, |me, _, event, _| { + me.events.push(*event); + }) }); - c.subscribe_to_model(&handle_3, |me, _, event, _| { - me.events.push(*event); - }) - }); + handle_2.update(app, |_, c| c.emit(7)); + handle_1.read(app, |view, _| assert_eq!(view.events, vec![7])); - handle_2.update(app, |_, c| c.emit(7)); - handle_1.read(app, |view, _| assert_eq!(view.events, vec![7])); + handle_2.update(app, |_, c| c.emit(5)); + handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5])); - handle_2.update(app, |_, c| c.emit(5)); - handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5])); - - handle_3.update(app, |_, c| c.emit(9)); - handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5, 9])); + handle_3.update(app, |_, c| c.emit(9)); + handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5, 9])); + }) } #[test] @@ -2596,30 +2632,31 @@ mod tests { type Event = (); } - let mut app = App::new().unwrap(); - let app = &mut app; + App::test((), |mut app| async move { + let app = &mut app; - let (window_id, _) = app.add_window(|_| View); - let observing_view = app.add_view(window_id, |_| View); - let emitting_view = app.add_view(window_id, |_| View); - let observing_model = app.add_model(|_| Model); - let observed_model = app.add_model(|_| Model); + let (window_id, _) = app.add_window(|_| View); + let observing_view = app.add_view(window_id, |_| View); + let emitting_view = app.add_view(window_id, |_| View); + let observing_model = app.add_model(|_| Model); + let observed_model = app.add_model(|_| Model); - observing_view.update(app, |_, ctx| { - ctx.subscribe_to_view(&emitting_view, |_, _, _, _| {}); - ctx.subscribe_to_model(&observed_model, |_, _, _, _| {}); - }); - observing_model.update(app, |_, ctx| { - ctx.subscribe(&observed_model, |_, _, _| {}); - }); + observing_view.update(app, |_, ctx| { + ctx.subscribe_to_view(&emitting_view, |_, _, _, _| {}); + ctx.subscribe_to_model(&observed_model, |_, _, _, _| {}); + }); + observing_model.update(app, |_, ctx| { + ctx.subscribe(&observed_model, |_, _, _| {}); + }); - app.update(|_| { - drop(observing_view); - drop(observing_model); - }); + app.update(|_| { + drop(observing_view); + drop(observing_model); + }); - emitting_view.update(app, |_, ctx| ctx.emit(())); - observed_model.update(app, |_, ctx| ctx.emit(())); + emitting_view.update(app, |_, ctx| ctx.emit(())); + observed_model.update(app, |_, ctx| ctx.emit(())); + }) } #[test] @@ -2652,22 +2689,23 @@ mod tests { type Event = (); } - let mut app = App::new().unwrap(); - let app = &mut app; - let (_, view) = app.add_window(|_| View::default()); - let model = app.add_model(|_| Model::default()); + App::test((), |mut app| async move { + let app = &mut app; + let (_, view) = app.add_window(|_| View::default()); + let model = app.add_model(|_| Model::default()); - view.update(app, |_, c| { - c.observe(&model, |me, observed, c| { - me.events.push(observed.as_ref(c).count) + view.update(app, |_, c| { + c.observe(&model, |me, observed, c| { + me.events.push(observed.as_ref(c).count) + }); }); - }); - model.update(app, |model, c| { - model.count = 11; - c.notify(); - }); - view.read(app, |view, _| assert_eq!(view.events, vec![11])); + model.update(app, |model, c| { + model.count = 11; + c.notify(); + }); + view.read(app, |view, _| assert_eq!(view.events, vec![11])); + }) } #[test] @@ -2694,27 +2732,28 @@ mod tests { type Event = (); } - let mut app = App::new().unwrap(); - let app = &mut app; + App::test((), |mut app| async move { + let app = &mut app; - let (window_id, _) = app.add_window(|_| View); - let observing_view = app.add_view(window_id, |_| View); - let observing_model = app.add_model(|_| Model); - let observed_model = app.add_model(|_| Model); + let (window_id, _) = app.add_window(|_| View); + let observing_view = app.add_view(window_id, |_| View); + let observing_model = app.add_model(|_| Model); + let observed_model = app.add_model(|_| Model); - observing_view.update(app, |_, ctx| { - ctx.observe(&observed_model, |_, _, _| {}); - }); - observing_model.update(app, |_, ctx| { - ctx.observe(&observed_model, |_, _, _| {}); - }); + observing_view.update(app, |_, ctx| { + ctx.observe(&observed_model, |_, _, _| {}); + }); + observing_model.update(app, |_, ctx| { + ctx.observe(&observed_model, |_, _, _| {}); + }); - app.update(|_| { - drop(observing_view); - drop(observing_model); - }); + app.update(|_| { + drop(observing_view); + drop(observing_model); + }); - observed_model.update(app, |_, ctx| ctx.notify()); + observed_model.update(app, |_, ctx| ctx.notify()); + }) } #[test] @@ -2748,34 +2787,35 @@ mod tests { } } - let mut app = App::new().unwrap(); - let app = &mut app; - let (window_id, view_1) = app.add_window(|_| View::default()); - let view_2 = app.add_view(window_id, |_| View::default()); + App::test((), |mut app| async move { + let app = &mut app; + let (window_id, view_1) = app.add_window(|_| View::default()); + let view_2 = app.add_view(window_id, |_| View::default()); - view_1.update(app, |_, ctx| { - ctx.subscribe_to_view(&view_2, |view_1, _, event, _| { - view_1.events.push(format!("view 2 {}", event)); + view_1.update(app, |_, ctx| { + ctx.subscribe_to_view(&view_2, |view_1, _, event, _| { + view_1.events.push(format!("view 2 {}", event)); + }); + ctx.focus(&view_2); }); - ctx.focus(&view_2); - }); - view_1.update(app, |_, ctx| { - ctx.focus(&view_1); - }); + view_1.update(app, |_, ctx| { + ctx.focus(&view_1); + }); - view_1.read(app, |view_1, _| { - assert_eq!( - view_1.events, - [ - "self focused".to_string(), - "self blurred".to_string(), - "view 2 focused".to_string(), - "self focused".to_string(), - "view 2 blurred".to_string(), - ], - ); - }); + view_1.read(app, |view_1, _| { + assert_eq!( + view_1.events, + [ + "self focused".to_string(), + "self blurred".to_string(), + "view 2 focused".to_string(), + "self focused".to_string(), + "view 2 blurred".to_string(), + ], + ); + }); + }) } #[test] @@ -2799,7 +2839,7 @@ mod tests { } } - App::test(|mut app| async move { + App::test((), |mut app| async move { let (_, handle) = app.add_window(|_| View::default()); handle .update(&mut app, |_, c| { @@ -2841,7 +2881,7 @@ mod tests { } } - App::test(|mut app| async move { + App::test((), |mut app| async move { let (_, handle) = app.add_window(|_| View::default()); handle .update(&mut app, |_, c| { @@ -2905,76 +2945,77 @@ mod tests { foo: String, } - let mut app = App::new().unwrap(); - let actions = Rc::new(RefCell::new(Vec::new())); + App::test((), |mut app| async move { + let actions = Rc::new(RefCell::new(Vec::new())); - let actions_clone = actions.clone(); - app.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| { - actions_clone.borrow_mut().push("global a".to_string()); - }); + let actions_clone = actions.clone(); + app.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| { + actions_clone.borrow_mut().push("global a".to_string()); + }); - let actions_clone = actions.clone(); - app.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| { - actions_clone.borrow_mut().push("global b".to_string()); - }); + let actions_clone = actions.clone(); + app.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| { + actions_clone.borrow_mut().push("global b".to_string()); + }); - let actions_clone = actions.clone(); - app.add_action("action", move |view: &mut ViewA, arg: &ActionArg, ctx| { - assert_eq!(arg.foo, "bar"); - ctx.propagate_action(); - actions_clone.borrow_mut().push(format!("{} a", view.id)); - }); - - let actions_clone = actions.clone(); - app.add_action("action", move |view: &mut ViewA, _: &ActionArg, ctx| { - if view.id != 1 { + let actions_clone = actions.clone(); + app.add_action("action", move |view: &mut ViewA, arg: &ActionArg, ctx| { + assert_eq!(arg.foo, "bar"); ctx.propagate_action(); - } - actions_clone.borrow_mut().push(format!("{} b", view.id)); - }); + actions_clone.borrow_mut().push(format!("{} a", view.id)); + }); - let actions_clone = actions.clone(); - app.add_action("action", move |view: &mut ViewB, _: &ActionArg, ctx| { - ctx.propagate_action(); - actions_clone.borrow_mut().push(format!("{} c", view.id)); - }); + let actions_clone = actions.clone(); + app.add_action("action", move |view: &mut ViewA, _: &ActionArg, ctx| { + if view.id != 1 { + ctx.propagate_action(); + } + actions_clone.borrow_mut().push(format!("{} b", view.id)); + }); - let actions_clone = actions.clone(); - app.add_action("action", move |view: &mut ViewB, _: &ActionArg, ctx| { - ctx.propagate_action(); - actions_clone.borrow_mut().push(format!("{} d", view.id)); - }); + let actions_clone = actions.clone(); + app.add_action("action", move |view: &mut ViewB, _: &ActionArg, ctx| { + ctx.propagate_action(); + actions_clone.borrow_mut().push(format!("{} c", view.id)); + }); - let (window_id, view_1) = app.add_window(|_| ViewA { id: 1 }); - let view_2 = app.add_view(window_id, |_| ViewB { id: 2 }); - let view_3 = app.add_view(window_id, |_| ViewA { id: 3 }); - let view_4 = app.add_view(window_id, |_| ViewB { id: 4 }); + let actions_clone = actions.clone(); + app.add_action("action", move |view: &mut ViewB, _: &ActionArg, ctx| { + ctx.propagate_action(); + actions_clone.borrow_mut().push(format!("{} d", view.id)); + }); - app.dispatch_action( - window_id, - vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()], - "action", - ActionArg { foo: "bar".into() }, - ); + let (window_id, view_1) = app.add_window(|_| ViewA { id: 1 }); + let view_2 = app.add_view(window_id, |_| ViewB { id: 2 }); + let view_3 = app.add_view(window_id, |_| ViewA { id: 3 }); + let view_4 = app.add_view(window_id, |_| ViewB { id: 4 }); - assert_eq!( - *actions.borrow(), - vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "1 b"] - ); + app.dispatch_action( + window_id, + vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()], + "action", + ActionArg { foo: "bar".into() }, + ); - // Remove view_1, which doesn't propagate the action - actions.borrow_mut().clear(); - app.dispatch_action( - window_id, - vec![view_2.id(), view_3.id(), view_4.id()], - "action", - ActionArg { foo: "bar".into() }, - ); + assert_eq!( + *actions.borrow(), + vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "1 b"] + ); - assert_eq!( - *actions.borrow(), - vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "global b", "global a"] - ); + // Remove view_1, which doesn't propagate the action + actions.borrow_mut().clear(); + app.dispatch_action( + window_id, + vec![view_2.id(), view_3.id(), view_4.id()], + "action", + ActionArg { foo: "bar".into() }, + ); + + assert_eq!( + *actions.borrow(), + vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "global b", "global a"] + ); + }) } #[test] @@ -3018,41 +3059,41 @@ mod tests { } } - let mut app = App::new().unwrap(); + App::test((), |mut app| async move { + let mut view_1 = View::new(1); + let mut view_2 = View::new(2); + let mut view_3 = View::new(3); + view_1.keymap_context.set.insert("a".into()); + view_2.keymap_context.set.insert("b".into()); + view_3.keymap_context.set.insert("c".into()); - let mut view_1 = View::new(1); - let mut view_2 = View::new(2); - let mut view_3 = View::new(3); - view_1.keymap_context.set.insert("a".into()); - view_2.keymap_context.set.insert("b".into()); - view_3.keymap_context.set.insert("c".into()); + let (window_id, view_1) = app.add_window(|_| view_1); + let view_2 = app.add_view(window_id, |_| view_2); + let view_3 = app.add_view(window_id, |_| view_3); - let (window_id, view_1) = app.add_window(|_| view_1); - let view_2 = app.add_view(window_id, |_| view_2); - let view_3 = app.add_view(window_id, |_| view_3); + // This keymap's only binding dispatches an action on view 2 because that view will have + // "a" and "b" in its context, but not "c". + let binding = keymap::Binding::new("a", "action", Some("a && b && !c")) + .with_arg(ActionArg { key: "a".into() }); + app.add_bindings(vec![binding]); - // This keymap's only binding dispatches an action on view 2 because that view will have - // "a" and "b" in its context, but not "c". - let binding = keymap::Binding::new("a", "action", Some("a && b && !c")) - .with_arg(ActionArg { key: "a".into() }); - app.add_bindings(vec![binding]); + let handled_action = Rc::new(Cell::new(false)); + let handled_action_clone = handled_action.clone(); + app.add_action("action", move |view: &mut View, arg: &ActionArg, _ctx| { + handled_action_clone.set(true); + assert_eq!(view.id, 2); + assert_eq!(arg.key, "a"); + }); - let handled_action = Rc::new(Cell::new(false)); - let handled_action_clone = handled_action.clone(); - app.add_action("action", move |view: &mut View, arg: &ActionArg, _ctx| { - handled_action_clone.set(true); - assert_eq!(view.id, 2); - assert_eq!(arg.key, "a"); - }); + app.dispatch_keystroke( + window_id, + vec![view_1.id(), view_2.id(), view_3.id()], + &Keystroke::parse("a")?, + )?; - app.dispatch_keystroke( - window_id, - vec![view_1.id(), view_2.id(), view_3.id()], - &Keystroke::parse("a")?, - )?; - - assert!(handled_action.get()); - Ok(()) + assert!(handled_action.get()); + Ok(()) + }) } // #[test] @@ -3160,7 +3201,7 @@ mod tests { type Event = (); } - App::test(|mut app| async move { + App::test((), |mut app| async move { let model = app.add_model(|_| Model); let (_, view) = app.add_window(|_| View); diff --git a/gpui/src/platform/mac/window.rs b/gpui/src/platform/mac/window.rs index 5bb7bc6190..fdedf545cf 100644 --- a/gpui/src/platform/mac/window.rs +++ b/gpui/src/platform/mac/window.rs @@ -2,6 +2,7 @@ use crate::{ executor, geometry::vector::Vector2F, platform::{self, Event}, + Scene, }; use anyhow::{anyhow, Result}; use cocoa::{ @@ -20,6 +21,7 @@ use objc::{ runtime::{Class, Object, Sel, BOOL, NO, YES}, sel, sel_impl, }; +use pathfinder_geometry::vector::vec2f; use smol::Timer; use std::{ cell::{Cell, RefCell}, @@ -105,7 +107,7 @@ pub struct Window(Rc); struct WindowState { native_window: id, event_callback: RefCell bool>>>, - resize_callback: RefCell>>, + resize_callback: RefCell>>, synthetic_drag_counter: Cell, executor: Rc, } @@ -207,19 +209,11 @@ impl Window { } } - pub fn size(&self) -> NSSize { - self.0.size() - } - - pub fn backing_scale_factor(&self) -> f64 { - self.0.backing_scale_factor() - } - pub fn on_event bool>(&mut self, callback: F) { *self.0.event_callback.borrow_mut() = Some(Box::new(callback)); } - pub fn on_resize(&mut self, callback: F) { + pub fn on_resize(&mut self, callback: F) { *self.0.resize_callback.borrow_mut() = Some(Box::new(callback)); } } @@ -233,18 +227,31 @@ impl Drop for Window { } } -impl platform::Window for Window {} - -impl WindowState { - fn size(&self) -> NSSize { - let view_frame = unsafe { NSView::frame(self.native_window.contentView()) }; - view_frame.size +impl platform::Window for Window { + fn size(&self) -> Vector2F { + self.0.size() } - fn backing_scale_factor(&self) -> f64 { + fn scale_factor(&self) -> f32 { + self.0.scale_factor() + } + + fn render_scene(&self, scene: Scene) { + log::info!("render scene"); + } +} + +impl WindowState { + fn size(&self) -> Vector2F { + let NSSize { width, height, .. } = + unsafe { NSView::frame(self.native_window.contentView()) }.size; + vec2f(width as f32, height as f32) + } + + fn scale_factor(&self) -> f32 { unsafe { let screen: id = msg_send![self.native_window, screen]; - NSScreen::backingScaleFactor(screen) + NSScreen::backingScaleFactor(screen) as f32 } } @@ -297,7 +304,7 @@ extern "C" fn dealloc_delegate(this: &Object, _: Sel) { extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { let window = unsafe { window_state(this) }; - let event = unsafe { Event::from_native(native_event, Some(window.size().height as f32)) }; + let event = unsafe { Event::from_native(native_event, Some(window.size().y())) }; if let Some(event) = event { match event { @@ -325,7 +332,7 @@ extern "C" fn send_event(this: &Object, _: Sel, native_event: id) { extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) { let window = unsafe { window_state(this) }; let size = window.size(); - let scale_factor = window.backing_scale_factor(); + let scale_factor = window.scale_factor(); if let Some(callback) = window.resize_callback.borrow_mut().as_mut() { callback(size, scale_factor); } diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index cf858c15e7..868ab9f413 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -6,7 +6,11 @@ pub mod current { pub use super::mac::*; } -use crate::{executor, geometry::rect::RectF}; +use crate::{ + executor, + geometry::{rect::RectF, vector::Vector2F}, + Scene, +}; use anyhow::Result; use async_task::Runnable; pub use event::Event; @@ -36,7 +40,11 @@ pub trait Dispatcher: Send + Sync { fn run_on_main_thread(&self, task: Runnable); } -pub trait Window {} +pub trait Window { + fn size(&self) -> Vector2F; + fn scale_factor(&self) -> f32; + fn render_scene(&self, scene: Scene); +} pub struct WindowOptions<'a> { pub bounds: RectF, diff --git a/gpui/src/presenter.rs b/gpui/src/presenter.rs index fc4b31667a..c910ceaf69 100644 --- a/gpui/src/presenter.rs +++ b/gpui/src/presenter.rs @@ -7,22 +7,22 @@ use crate::{ AssetCache, Scene, }; use pathfinder_geometry::vector::{vec2f, Vector2F}; -use std::{any::Any, collections::HashMap, rc::Rc}; +use std::{any::Any, collections::HashMap, sync::Arc}; pub struct Presenter { window_id: usize, rendered_views: HashMap>, parents: HashMap, - font_cache: Rc, + font_cache: Arc, text_layout_cache: TextLayoutCache, - asset_cache: Rc, + asset_cache: Arc, } impl Presenter { pub fn new( window_id: usize, - font_cache: Rc, - asset_cache: Rc, + font_cache: Arc, + asset_cache: Arc, app: &MutableAppContext, ) -> Self { Self { @@ -35,7 +35,7 @@ impl Presenter { } } - fn invalidate(&mut self, invalidation: WindowInvalidation, app: &AppContext) { + pub fn invalidate(&mut self, invalidation: WindowInvalidation, app: &AppContext) { for view_id in invalidation.updated { self.rendered_views .insert(view_id, app.render_view(self.window_id, view_id).unwrap()); @@ -52,9 +52,9 @@ impl Presenter { scale_factor: f32, app: &mut MutableAppContext, ) -> Scene { - self.layout(window_size, app.ctx()); + self.layout(window_size, app.downgrade()); self.after_layout(app); - let scene = self.paint(window_size, scale_factor, app.ctx()); + let scene = self.paint(window_size, scale_factor, app.downgrade()); self.text_layout_cache.finish_frame(); scene } diff --git a/zed/Cargo.toml b/zed/Cargo.toml index 553cafd37f..c7026445cc 100644 --- a/zed/Cargo.toml +++ b/zed/Cargo.toml @@ -26,6 +26,7 @@ log = "0.4" num_cpus = "1.13.0" parking_lot = "0.11.1" rand = "0.8.3" +rust-embed = "5.9.0" simplelog = "0.9" smallvec = "1.6.1" smol = "1.2.5" diff --git a/zed/src/assets.rs b/zed/src/assets.rs new file mode 100644 index 0000000000..209b89c031 --- /dev/null +++ b/zed/src/assets.rs @@ -0,0 +1,13 @@ +use anyhow::{anyhow, Result}; +use gpui::AssetSource; +use rust_embed::RustEmbed; + +#[derive(RustEmbed)] +#[folder = "assets"] +struct Assets; + +impl AssetSource for Assets { + fn load(&self, path: &str) -> Result> { + Self::get(path).ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path)) + } +} diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 0e684e26f7..1de4f5781e 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -1907,46 +1907,46 @@ mod tests { use gpui::App; use std::{cell::RefCell, rc::Rc}; - let mut app = App::new().unwrap(); + let mut app = App::test((), |mut app| async move { + let buffer_1_events = Rc::new(RefCell::new(Vec::new())); + let buffer_2_events = Rc::new(RefCell::new(Vec::new())); - let buffer_1_events = Rc::new(RefCell::new(Vec::new())); - let buffer_2_events = Rc::new(RefCell::new(Vec::new())); + let buffer1 = app.add_model(|_| Buffer::new(0, "abcdef")); + let buffer2 = app.add_model(|_| Buffer::new(1, "abcdef")); + let ops = buffer1.update(&mut app, |buffer, ctx| { + let buffer_1_events = buffer_1_events.clone(); + ctx.subscribe(&buffer1, move |_, event, _| { + buffer_1_events.borrow_mut().push(event.clone()) + }); + let buffer_2_events = buffer_2_events.clone(); + ctx.subscribe(&buffer2, move |_, event, _| { + buffer_2_events.borrow_mut().push(event.clone()) + }); - let buffer1 = app.add_model(|_| Buffer::new(0, "abcdef")); - let buffer2 = app.add_model(|_| Buffer::new(1, "abcdef")); - let ops = buffer1.update(&mut app, |buffer, ctx| { - let buffer_1_events = buffer_1_events.clone(); - ctx.subscribe(&buffer1, move |_, event, _| { - buffer_1_events.borrow_mut().push(event.clone()) + buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap() }); - let buffer_2_events = buffer_2_events.clone(); - ctx.subscribe(&buffer2, move |_, event, _| { - buffer_2_events.borrow_mut().push(event.clone()) + buffer2.update(&mut app, |buffer, ctx| { + buffer.apply_ops(ops, Some(ctx)).unwrap(); }); - buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap() - }); - buffer2.update(&mut app, |buffer, ctx| { - buffer.apply_ops(ops, Some(ctx)).unwrap(); - }); + let buffer_1_events = buffer_1_events.borrow(); + assert_eq!( + *buffer_1_events, + vec![Event::Edited(vec![Edit { + old_range: 2..4, + new_range: 2..5 + }])] + ); - let buffer_1_events = buffer_1_events.borrow(); - assert_eq!( - *buffer_1_events, - vec![Event::Edited(vec![Edit { - old_range: 2..4, - new_range: 2..5 - }])] - ); - - let buffer_2_events = buffer_2_events.borrow(); - assert_eq!( - *buffer_2_events, - vec![Event::Edited(vec![Edit { - old_range: 2..4, - new_range: 2..5 - }])] - ); + let buffer_2_events = buffer_2_events.borrow(); + assert_eq!( + *buffer_2_events, + vec![Event::Edited(vec![Edit { + old_range: 2..4, + new_range: 2..5 + }])] + ); + }); } #[test] diff --git a/zed/src/editor/buffer_element.rs b/zed/src/editor/buffer_element.rs index 739d63fc04..58a114813e 100644 --- a/zed/src/editor/buffer_element.rs +++ b/zed/src/editor/buffer_element.rs @@ -418,7 +418,7 @@ impl Element for BufferElement { let view = self.view.as_ref(app); view.clamp_scroll_left( layout - .scroll_max(view, ctx.font_cache, ctx.text_layout_cache, app.ctx()) + .scroll_max(view, ctx.font_cache, ctx.text_layout_cache, app.downgrade()) .x(), ); @@ -426,10 +426,10 @@ impl Element for BufferElement { view.autoscroll_horizontally( view.scroll_position().y() as u32, layout.text_size.x(), - layout.scroll_width(view, ctx.font_cache, ctx.text_layout_cache, app.ctx()), + layout.scroll_width(view, ctx.font_cache, ctx.text_layout_cache, app.downgrade()), view.em_width(ctx.font_cache), &layout.line_layouts, - app.ctx(), + app.downgrade(), ); } } diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index d6fd9672f4..5d1b447118 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -1269,7 +1269,7 @@ mod tests { #[test] fn test_selection_with_mouse() { - App::test(|mut app| async move { + App::test((), |mut app| async move { let buffer = app.add_model(|_| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n")); let settings = settings::channel(&FontCache::new()).unwrap().1; let (_, buffer_view) = @@ -1373,19 +1373,21 @@ mod tests { let font_cache = FontCache::new(); let layout_cache = TextLayoutCache::new(); - let mut app = App::new().unwrap(); - let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6))); + App::test((), |mut app| async move { + let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6))); - let settings = settings::channel(&font_cache).unwrap().1; - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); + let settings = settings::channel(&font_cache).unwrap().1; + let (_, view) = + app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - view.read(&app, |view, app| { - let layouts = view.layout_line_numbers(1000.0, &font_cache, &layout_cache, app)?; - assert_eq!(layouts.len(), 6); - Result::<()>::Ok(()) - })?; + view.read(&app, |view, app| { + let layouts = view.layout_line_numbers(1000.0, &font_cache, &layout_cache, app)?; + assert_eq!(layouts.len(), 6); + Result::<()>::Ok(()) + })?; - Ok(()) + Ok(()) + }) } #[test] diff --git a/zed/src/lib.rs b/zed/src/lib.rs index 3192f91250..baea784d16 100644 --- a/zed/src/lib.rs +++ b/zed/src/lib.rs @@ -1,3 +1,4 @@ +pub mod assets; pub mod editor; mod operation_queue; pub mod settings; diff --git a/zed/src/main.rs b/zed/src/main.rs index 0884913b48..1df3950202 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -27,7 +27,7 @@ fn main() { let (settings_tx, settings_rx) = settings::channel(&font_cache).unwrap(); - let mut app = gpui::App::new().unwrap(); + let mut app = gpui::App::new(As).unwrap(); platform::runner() .on_finish_launching(move || {