WIP: Start on rendering scenes via presenter

This commit is contained in:
Nathan Sobo 2021-03-18 21:33:16 -06:00
parent e809d6119a
commit 605bdd62dd
12 changed files with 495 additions and 388 deletions

34
Cargo.lock generated
View File

@ -1298,6 +1298,39 @@ dependencies = [
"crossbeam-utils 0.8.2", "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]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"
@ -1679,6 +1712,7 @@ dependencies = [
"num_cpus", "num_cpus",
"parking_lot", "parking_lot",
"rand 0.8.3", "rand 0.8.3",
"rust-embed",
"serde_json", "serde_json",
"simplelog", "simplelog",
"smallvec", "smallvec",

View File

@ -2,12 +2,14 @@ use crate::{
elements::Element, elements::Element,
executor::{self, ForegroundTask}, executor::{self, ForegroundTask},
keymap::{self, Keystroke}, keymap::{self, Keystroke},
platform::{self, App as _}, platform::{self, App as _, WindowOptions},
util::post_inc, util::post_inc,
AssetCache, AssetSource, FontCache, Presenter,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use keymap::MatchResult; use keymap::MatchResult;
use parking_lot::Mutex; use parking_lot::Mutex;
use pathfinder_geometry::{rect::RectF, vector::vec2f};
use smol::{channel, prelude::*}; use smol::{channel, prelude::*};
use std::{ use std::{
any::{type_name, Any, TypeId}, any::{type_name, Any, TypeId},
@ -66,22 +68,28 @@ pub trait UpdateView {
pub struct App(Rc<RefCell<MutableAppContext>>); pub struct App(Rc<RefCell<MutableAppContext>>);
impl App { impl App {
pub fn test<T, F: Future<Output = T>>(f: impl FnOnce(App) -> F) -> T { pub fn test<T, F: Future<Output = T>>(
asset_source: impl AssetSource,
f: impl FnOnce(App) -> F,
) -> T {
let platform = platform::current::app(); // TODO: Make a test platform app let platform = platform::current::app(); // TODO: Make a test platform app
let foreground = Rc::new(executor::Foreground::test()); let foreground = Rc::new(executor::Foreground::test());
let app = Self(Rc::new(RefCell::new(MutableAppContext::new( let app = Self(Rc::new(RefCell::new(MutableAppContext::new(
foreground.clone(), foreground.clone(),
Arc::new(platform), Arc::new(platform),
asset_source,
)))); ))));
app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0)); app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0));
smol::block_on(foreground.run(f(app))) smol::block_on(foreground.run(f(app)))
} }
pub fn new() -> Result<Self> { pub fn new(asset_source: impl AssetSource) -> Result<Self> {
let platform = Arc::new(platform::current::app()); let platform = Arc::new(platform::current::app());
let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?); let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?);
let app = Self(Rc::new(RefCell::new(MutableAppContext::new( 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)); app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0));
Ok(app) Ok(app)
@ -217,7 +225,7 @@ impl App {
} }
pub fn read<T, F: FnOnce(&AppContext) -> T>(&mut self, callback: F) -> T { pub fn read<T, F: FnOnce(&AppContext) -> T>(&mut self, callback: F) -> T {
callback(self.0.borrow().ctx()) callback(self.0.borrow().downgrade())
} }
pub fn update<T, F: FnOnce(&mut MutableAppContext) -> T>(&mut self, callback: F) -> T { pub fn update<T, F: FnOnce(&mut MutableAppContext) -> T>(&mut self, callback: F) -> T {
@ -234,7 +242,7 @@ impl App {
F: FnOnce(&T, &AppContext) -> S, F: FnOnce(&T, &AppContext) -> S,
{ {
let state = self.0.borrow(); 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<Output = ()> { pub fn finish_pending_tasks(&self) -> impl Future<Output = ()> {
@ -277,6 +285,8 @@ type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext);
pub struct MutableAppContext { pub struct MutableAppContext {
platform: Arc<dyn platform::App>, platform: Arc<dyn platform::App>,
fonts: Arc<FontCache>,
assets: Arc<AssetCache>,
ctx: AppContext, ctx: AppContext,
actions: HashMap<TypeId, HashMap<String, Vec<Box<ActionCallback>>>>, actions: HashMap<TypeId, HashMap<String, Vec<Box<ActionCallback>>>>,
global_actions: HashMap<String, Vec<Box<GlobalActionCallback>>>, global_actions: HashMap<String, Vec<Box<GlobalActionCallback>>>,
@ -301,9 +311,15 @@ pub struct MutableAppContext {
} }
impl MutableAppContext { impl MutableAppContext {
pub fn new(foreground: Rc<executor::Foreground>, platform: Arc<dyn platform::App>) -> Self { pub fn new(
foreground: Rc<executor::Foreground>,
platform: Arc<dyn platform::App>,
asset_source: impl AssetSource,
) -> Self {
Self { Self {
platform, platform,
fonts: Arc::new(FontCache::new()),
assets: Arc::new(AssetCache::new(asset_source)),
ctx: AppContext { ctx: AppContext {
models: HashMap::new(), models: HashMap::new(),
windows: HashMap::new(), windows: HashMap::new(),
@ -331,7 +347,7 @@ impl MutableAppContext {
} }
} }
pub fn ctx(&self) -> &AppContext { pub fn downgrade(&self) -> &AppContext {
&self.ctx &self.ctx
} }
@ -532,7 +548,7 @@ impl MutableAppContext {
.get(&window_id) .get(&window_id)
.and_then(|w| w.views.get(view_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()); context_chain.push(context.clone());
} else { } else {
return Err(anyhow!( 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.ctx.windows.get_mut(&window_id).unwrap().root_view = Some(root_handle.clone().into());
self.focus(window_id, root_handle.id()); self.focus(window_id, root_handle.id());
// self.emit_ui_update(UiUpdate::OpenWindow { match self.platform.open_window(
// window_id, WindowOptions {
// width: 1024.0, bounds: RectF::new(vec2f(0., 0.), vec2f(1024., 768.)),
// height: 768.0, 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) (window_id, root_handle)
} }
@ -1606,7 +1641,7 @@ impl<'a, T: View> ViewContext<'a, T> {
window_id: self.window_id, window_id: self.window_id,
view_id: self.view_id, view_id: self.view_id,
callback: Box::new(move |view, payload, app, window_id, 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 model = view.downcast_mut().expect("downcast is type safe");
let payload = payload.downcast_ref().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); 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, window_id: self.window_id,
view_id: self.view_id, view_id: self.view_id,
callback: Box::new(move |view, payload, app, window_id, 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 model = view.downcast_mut().expect("downcast is type safe");
let payload = payload.downcast_ref().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); let mut ctx = ViewContext::new(app, window_id, view_id);
@ -2261,42 +2296,43 @@ mod tests {
} }
} }
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let app = &mut app; let app = &mut app;
let handle_1 = app.add_model(|ctx| Model::new(None, ctx)); 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)); let handle_2 = app.add_model(|ctx| Model::new(Some(handle_1.clone()), ctx));
assert_eq!(app.0.borrow().ctx.models.len(), 2); assert_eq!(app.0.borrow().ctx.models.len(), 2);
handle_1.update(app, |model, ctx| { handle_1.update(app, |model, ctx| {
model.events.push("updated".into()); model.events.push("updated".into());
ctx.emit(1); ctx.emit(1);
ctx.notify(); ctx.notify();
ctx.emit(2); ctx.emit(2);
}); });
handle_1.read(app, |model, _| { handle_1.read(app, |model, _| {
assert_eq!(model.events, vec!["updated".to_string()]); assert_eq!(model.events, vec!["updated".to_string()]);
}); });
handle_2.read(app, |model, _| { handle_2.read(app, |model, _| {
assert_eq!( assert_eq!(
model.events, model.events,
vec![ vec![
"observed event 1".to_string(), "observed event 1".to_string(),
"notified".to_string(), "notified".to_string(),
"observed event 2".to_string(), "observed event 2".to_string(),
] ]
); );
}); });
handle_2.update(app, |model, _| { handle_2.update(app, |model, _| {
drop(handle_1); drop(handle_1);
model.other.take(); model.other.take();
}); });
let app_state = app.0.borrow(); let app_state = app.0.borrow();
assert_eq!(app_state.ctx.models.len(), 1); assert_eq!(app_state.ctx.models.len(), 1);
assert!(app_state.subscriptions.is_empty()); assert!(app_state.subscriptions.is_empty());
assert!(app_state.observations.is_empty()); assert!(app_state.observations.is_empty());
})
} }
#[test] #[test]
@ -2310,28 +2346,28 @@ mod tests {
type Event = usize; type Event = usize;
} }
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let app = &mut app; 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()); handle_1.update(app, |_, c| {
let handle_2 = app.add_model(|_| Model::default()); c.subscribe(&handle_2, move |model: &mut Model, event, c| {
let handle_2b = handle_2.clone(); model.events.push(*event);
handle_1.update(app, |_, c| { c.subscribe(&handle_2b, |model, event, _| {
c.subscribe(&handle_2, move |model: &mut Model, event, c| { model.events.push(*event * 2);
model.events.push(*event); });
c.subscribe(&handle_2b, |model, event, _| {
model.events.push(*event * 2);
}); });
}); });
});
handle_2.update(app, |_, c| c.emit(7)); handle_2.update(app, |_, c| c.emit(7));
handle_1.read(app, |model, _| assert_eq!(model.events, vec![7])); handle_1.read(app, |model, _| assert_eq!(model.events, vec![7]));
handle_2.update(app, |_, c| c.emit(5)); handle_2.update(app, |_, c| c.emit(5));
handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5])); handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5]));
})
} }
#[test] #[test]
@ -2346,33 +2382,33 @@ mod tests {
type Event = (); 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; handle_1.update(app, |_, c| {
let handle_1 = app.add_model(|_| Model::default()); c.observe(&handle_2, move |model, observed, c| {
let handle_2 = app.add_model(|_| Model::default()); model.events.push(observed.as_ref(c).count);
let handle_2b = handle_2.clone(); 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| { handle_2.update(app, |model, c| {
model.count = 7; model.count = 7;
c.notify() c.notify()
}); });
handle_1.read(app, |model, _| assert_eq!(model.events, vec![7])); handle_1.read(app, |model, _| assert_eq!(model.events, vec![7]));
handle_2.update(app, |model, c| { handle_2.update(app, |model, c| {
model.count = 5; model.count = 5;
c.notify() c.notify()
}); });
handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5])) handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5]))
})
} }
#[test] #[test]
@ -2386,7 +2422,7 @@ mod tests {
type Event = (); type Event = ();
} }
App::test(|mut app| async move { App::test((), |mut app| async move {
let handle = app.add_model(|_| Model::default()); let handle = app.add_model(|_| Model::default());
handle handle
.update(&mut app, |_, c| { .update(&mut app, |_, c| {
@ -2419,7 +2455,7 @@ mod tests {
type Event = (); type Event = ();
} }
App::test(|mut app| async move { App::test((), |mut app| async move {
let handle = app.add_model(|_| Model::default()); let handle = app.add_model(|_| Model::default());
handle handle
.update(&mut app, |_, c| { .update(&mut app, |_, c| {
@ -2476,41 +2512,41 @@ mod tests {
} }
} }
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let app = &mut app; 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)); handle_1.update(app, |view, ctx| {
let handle_1 = app.add_view(window_id, |ctx| View::new(None, ctx)); view.events.push("updated".into());
let handle_2 = app.add_view(window_id, |ctx| View::new(Some(handle_1.clone()), ctx)); ctx.emit(1);
assert_eq!(app.0.borrow().ctx.windows[&window_id].views.len(), 3); 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| { handle_2.update(app, |view, _| {
view.events.push("updated".into()); drop(handle_1);
ctx.emit(1); view.other.take();
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, _| { let app_state = app.0.borrow();
drop(handle_1); assert_eq!(app_state.ctx.windows[&window_id].views.len(), 2);
view.other.take(); 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] #[test]
@ -2540,36 +2576,36 @@ mod tests {
type Event = usize; type Event = usize;
} }
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let app = &mut app; 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()); handle_1.update(app, |_, c| {
let handle_2 = app.add_view(window_id, |_| View::default()); c.subscribe_to_view(&handle_2, move |me, _, event, c| {
let handle_2b = handle_2.clone(); me.events.push(*event);
let handle_3 = app.add_model(|_| Model);
handle_1.update(app, |_, c| { c.subscribe_to_view(&handle_2b, |me, _, event, _| {
c.subscribe_to_view(&handle_2, move |me, _, event, c| { me.events.push(*event * 2);
me.events.push(*event); });
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, _| { handle_2.update(app, |_, c| c.emit(7));
me.events.push(*event); handle_1.read(app, |view, _| assert_eq!(view.events, vec![7]));
})
});
handle_2.update(app, |_, c| c.emit(7)); handle_2.update(app, |_, c| c.emit(5));
handle_1.read(app, |view, _| assert_eq!(view.events, vec![7])); handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5]));
handle_2.update(app, |_, c| c.emit(5)); handle_3.update(app, |_, c| c.emit(9));
handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5])); 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] #[test]
@ -2596,30 +2632,31 @@ mod tests {
type Event = (); type Event = ();
} }
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let app = &mut app; let app = &mut app;
let (window_id, _) = app.add_window(|_| View); let (window_id, _) = app.add_window(|_| View);
let observing_view = app.add_view(window_id, |_| View); let observing_view = app.add_view(window_id, |_| View);
let emitting_view = app.add_view(window_id, |_| View); let emitting_view = app.add_view(window_id, |_| View);
let observing_model = app.add_model(|_| Model); let observing_model = app.add_model(|_| Model);
let observed_model = app.add_model(|_| Model); let observed_model = app.add_model(|_| Model);
observing_view.update(app, |_, ctx| { observing_view.update(app, |_, ctx| {
ctx.subscribe_to_view(&emitting_view, |_, _, _, _| {}); ctx.subscribe_to_view(&emitting_view, |_, _, _, _| {});
ctx.subscribe_to_model(&observed_model, |_, _, _, _| {}); ctx.subscribe_to_model(&observed_model, |_, _, _, _| {});
}); });
observing_model.update(app, |_, ctx| { observing_model.update(app, |_, ctx| {
ctx.subscribe(&observed_model, |_, _, _| {}); ctx.subscribe(&observed_model, |_, _, _| {});
}); });
app.update(|_| { app.update(|_| {
drop(observing_view); drop(observing_view);
drop(observing_model); drop(observing_model);
}); });
emitting_view.update(app, |_, ctx| ctx.emit(())); emitting_view.update(app, |_, ctx| ctx.emit(()));
observed_model.update(app, |_, ctx| ctx.emit(())); observed_model.update(app, |_, ctx| ctx.emit(()));
})
} }
#[test] #[test]
@ -2652,22 +2689,23 @@ mod tests {
type Event = (); type Event = ();
} }
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let app = &mut app; let app = &mut app;
let (_, view) = app.add_window(|_| View::default()); let (_, view) = app.add_window(|_| View::default());
let model = app.add_model(|_| Model::default()); let model = app.add_model(|_| Model::default());
view.update(app, |_, c| { view.update(app, |_, c| {
c.observe(&model, |me, observed, c| { c.observe(&model, |me, observed, c| {
me.events.push(observed.as_ref(c).count) me.events.push(observed.as_ref(c).count)
});
}); });
});
model.update(app, |model, c| { model.update(app, |model, c| {
model.count = 11; model.count = 11;
c.notify(); c.notify();
}); });
view.read(app, |view, _| assert_eq!(view.events, vec![11])); view.read(app, |view, _| assert_eq!(view.events, vec![11]));
})
} }
#[test] #[test]
@ -2694,27 +2732,28 @@ mod tests {
type Event = (); type Event = ();
} }
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let app = &mut app; let app = &mut app;
let (window_id, _) = app.add_window(|_| View); let (window_id, _) = app.add_window(|_| View);
let observing_view = app.add_view(window_id, |_| View); let observing_view = app.add_view(window_id, |_| View);
let observing_model = app.add_model(|_| Model); let observing_model = app.add_model(|_| Model);
let observed_model = app.add_model(|_| Model); let observed_model = app.add_model(|_| Model);
observing_view.update(app, |_, ctx| { observing_view.update(app, |_, ctx| {
ctx.observe(&observed_model, |_, _, _| {}); ctx.observe(&observed_model, |_, _, _| {});
}); });
observing_model.update(app, |_, ctx| { observing_model.update(app, |_, ctx| {
ctx.observe(&observed_model, |_, _, _| {}); ctx.observe(&observed_model, |_, _, _| {});
}); });
app.update(|_| { app.update(|_| {
drop(observing_view); drop(observing_view);
drop(observing_model); drop(observing_model);
}); });
observed_model.update(app, |_, ctx| ctx.notify()); observed_model.update(app, |_, ctx| ctx.notify());
})
} }
#[test] #[test]
@ -2748,34 +2787,35 @@ mod tests {
} }
} }
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let app = &mut app; let app = &mut app;
let (window_id, view_1) = app.add_window(|_| View::default()); let (window_id, view_1) = app.add_window(|_| View::default());
let view_2 = app.add_view(window_id, |_| View::default()); let view_2 = app.add_view(window_id, |_| View::default());
view_1.update(app, |_, ctx| { view_1.update(app, |_, ctx| {
ctx.subscribe_to_view(&view_2, |view_1, _, event, _| { ctx.subscribe_to_view(&view_2, |view_1, _, event, _| {
view_1.events.push(format!("view 2 {}", event)); view_1.events.push(format!("view 2 {}", event));
});
ctx.focus(&view_2);
}); });
ctx.focus(&view_2);
});
view_1.update(app, |_, ctx| { view_1.update(app, |_, ctx| {
ctx.focus(&view_1); ctx.focus(&view_1);
}); });
view_1.read(app, |view_1, _| { view_1.read(app, |view_1, _| {
assert_eq!( assert_eq!(
view_1.events, view_1.events,
[ [
"self focused".to_string(), "self focused".to_string(),
"self blurred".to_string(), "self blurred".to_string(),
"view 2 focused".to_string(), "view 2 focused".to_string(),
"self focused".to_string(), "self focused".to_string(),
"view 2 blurred".to_string(), "view 2 blurred".to_string(),
], ],
); );
}); });
})
} }
#[test] #[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()); let (_, handle) = app.add_window(|_| View::default());
handle handle
.update(&mut app, |_, c| { .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()); let (_, handle) = app.add_window(|_| View::default());
handle handle
.update(&mut app, |_, c| { .update(&mut app, |_, c| {
@ -2905,76 +2945,77 @@ mod tests {
foo: String, foo: String,
} }
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let actions = Rc::new(RefCell::new(Vec::new())); let actions = Rc::new(RefCell::new(Vec::new()));
let actions_clone = actions.clone(); let actions_clone = actions.clone();
app.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| { app.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| {
actions_clone.borrow_mut().push("global a".to_string()); actions_clone.borrow_mut().push("global a".to_string());
}); });
let actions_clone = actions.clone(); let actions_clone = actions.clone();
app.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| { app.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| {
actions_clone.borrow_mut().push("global b".to_string()); actions_clone.borrow_mut().push("global b".to_string());
}); });
let actions_clone = actions.clone(); let actions_clone = actions.clone();
app.add_action("action", move |view: &mut ViewA, arg: &ActionArg, ctx| { app.add_action("action", move |view: &mut ViewA, arg: &ActionArg, ctx| {
assert_eq!(arg.foo, "bar"); 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 {
ctx.propagate_action(); ctx.propagate_action();
} actions_clone.borrow_mut().push(format!("{} a", view.id));
actions_clone.borrow_mut().push(format!("{} b", view.id)); });
});
let actions_clone = actions.clone(); let actions_clone = actions.clone();
app.add_action("action", move |view: &mut ViewB, _: &ActionArg, ctx| { app.add_action("action", move |view: &mut ViewA, _: &ActionArg, ctx| {
ctx.propagate_action(); if view.id != 1 {
actions_clone.borrow_mut().push(format!("{} c", view.id)); ctx.propagate_action();
}); }
actions_clone.borrow_mut().push(format!("{} b", view.id));
});
let actions_clone = actions.clone(); let actions_clone = actions.clone();
app.add_action("action", move |view: &mut ViewB, _: &ActionArg, ctx| { app.add_action("action", move |view: &mut ViewB, _: &ActionArg, ctx| {
ctx.propagate_action(); ctx.propagate_action();
actions_clone.borrow_mut().push(format!("{} d", view.id)); actions_clone.borrow_mut().push(format!("{} c", view.id));
}); });
let (window_id, view_1) = app.add_window(|_| ViewA { id: 1 }); let actions_clone = actions.clone();
let view_2 = app.add_view(window_id, |_| ViewB { id: 2 }); app.add_action("action", move |view: &mut ViewB, _: &ActionArg, ctx| {
let view_3 = app.add_view(window_id, |_| ViewA { id: 3 }); ctx.propagate_action();
let view_4 = app.add_view(window_id, |_| ViewB { id: 4 }); actions_clone.borrow_mut().push(format!("{} d", view.id));
});
app.dispatch_action( let (window_id, view_1) = app.add_window(|_| ViewA { id: 1 });
window_id, let view_2 = app.add_view(window_id, |_| ViewB { id: 2 });
vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()], let view_3 = app.add_view(window_id, |_| ViewA { id: 3 });
"action", let view_4 = app.add_view(window_id, |_| ViewB { id: 4 });
ActionArg { foo: "bar".into() },
);
assert_eq!( app.dispatch_action(
*actions.borrow(), window_id,
vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "1 b"] 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 assert_eq!(
actions.borrow_mut().clear(); *actions.borrow(),
app.dispatch_action( vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "1 b"]
window_id, );
vec![view_2.id(), view_3.id(), view_4.id()],
"action",
ActionArg { foo: "bar".into() },
);
assert_eq!( // Remove view_1, which doesn't propagate the action
*actions.borrow(), actions.borrow_mut().clear();
vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "global b", "global a"] 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] #[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 (window_id, view_1) = app.add_window(|_| view_1);
let mut view_2 = View::new(2); let view_2 = app.add_view(window_id, |_| view_2);
let mut view_3 = View::new(3); let view_3 = app.add_view(window_id, |_| view_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); // This keymap's only binding dispatches an action on view 2 because that view will have
let view_2 = app.add_view(window_id, |_| view_2); // "a" and "b" in its context, but not "c".
let view_3 = app.add_view(window_id, |_| view_3); 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 let handled_action = Rc::new(Cell::new(false));
// "a" and "b" in its context, but not "c". let handled_action_clone = handled_action.clone();
let binding = keymap::Binding::new("a", "action", Some("a && b && !c")) app.add_action("action", move |view: &mut View, arg: &ActionArg, _ctx| {
.with_arg(ActionArg { key: "a".into() }); handled_action_clone.set(true);
app.add_bindings(vec![binding]); assert_eq!(view.id, 2);
assert_eq!(arg.key, "a");
});
let handled_action = Rc::new(Cell::new(false)); app.dispatch_keystroke(
let handled_action_clone = handled_action.clone(); window_id,
app.add_action("action", move |view: &mut View, arg: &ActionArg, _ctx| { vec![view_1.id(), view_2.id(), view_3.id()],
handled_action_clone.set(true); &Keystroke::parse("a")?,
assert_eq!(view.id, 2); )?;
assert_eq!(arg.key, "a");
});
app.dispatch_keystroke( assert!(handled_action.get());
window_id, Ok(())
vec![view_1.id(), view_2.id(), view_3.id()], })
&Keystroke::parse("a")?,
)?;
assert!(handled_action.get());
Ok(())
} }
// #[test] // #[test]
@ -3160,7 +3201,7 @@ mod tests {
type Event = (); type Event = ();
} }
App::test(|mut app| async move { App::test((), |mut app| async move {
let model = app.add_model(|_| Model); let model = app.add_model(|_| Model);
let (_, view) = app.add_window(|_| View); let (_, view) = app.add_window(|_| View);

View File

@ -2,6 +2,7 @@ use crate::{
executor, executor,
geometry::vector::Vector2F, geometry::vector::Vector2F,
platform::{self, Event}, platform::{self, Event},
Scene,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use cocoa::{ use cocoa::{
@ -20,6 +21,7 @@ use objc::{
runtime::{Class, Object, Sel, BOOL, NO, YES}, runtime::{Class, Object, Sel, BOOL, NO, YES},
sel, sel_impl, sel, sel_impl,
}; };
use pathfinder_geometry::vector::vec2f;
use smol::Timer; use smol::Timer;
use std::{ use std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
@ -105,7 +107,7 @@ pub struct Window(Rc<WindowState>);
struct WindowState { struct WindowState {
native_window: id, native_window: id,
event_callback: RefCell<Option<Box<dyn FnMut(Event) -> bool>>>, event_callback: RefCell<Option<Box<dyn FnMut(Event) -> bool>>>,
resize_callback: RefCell<Option<Box<dyn FnMut(NSSize, f64)>>>, resize_callback: RefCell<Option<Box<dyn FnMut(Vector2F, f32)>>>,
synthetic_drag_counter: Cell<usize>, synthetic_drag_counter: Cell<usize>,
executor: Rc<executor::Foreground>, executor: Rc<executor::Foreground>,
} }
@ -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<F: 'static + FnMut(Event) -> bool>(&mut self, callback: F) { pub fn on_event<F: 'static + FnMut(Event) -> bool>(&mut self, callback: F) {
*self.0.event_callback.borrow_mut() = Some(Box::new(callback)); *self.0.event_callback.borrow_mut() = Some(Box::new(callback));
} }
pub fn on_resize<F: 'static + FnMut(NSSize, f64)>(&mut self, callback: F) { pub fn on_resize<F: 'static + FnMut(Vector2F, f32)>(&mut self, callback: F) {
*self.0.resize_callback.borrow_mut() = Some(Box::new(callback)); *self.0.resize_callback.borrow_mut() = Some(Box::new(callback));
} }
} }
@ -233,18 +227,31 @@ impl Drop for Window {
} }
} }
impl platform::Window for Window {} impl platform::Window for Window {
fn size(&self) -> Vector2F {
impl WindowState { self.0.size()
fn size(&self) -> NSSize {
let view_frame = unsafe { NSView::frame(self.native_window.contentView()) };
view_frame.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 { unsafe {
let screen: id = msg_send![self.native_window, screen]; 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) { extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
let window = unsafe { window_state(this) }; 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 { if let Some(event) = event {
match 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) { extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
let window = unsafe { window_state(this) }; let window = unsafe { window_state(this) };
let size = window.size(); 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() { if let Some(callback) = window.resize_callback.borrow_mut().as_mut() {
callback(size, scale_factor); callback(size, scale_factor);
} }

View File

@ -6,7 +6,11 @@ pub mod current {
pub use super::mac::*; pub use super::mac::*;
} }
use crate::{executor, geometry::rect::RectF}; use crate::{
executor,
geometry::{rect::RectF, vector::Vector2F},
Scene,
};
use anyhow::Result; use anyhow::Result;
use async_task::Runnable; use async_task::Runnable;
pub use event::Event; pub use event::Event;
@ -36,7 +40,11 @@ pub trait Dispatcher: Send + Sync {
fn run_on_main_thread(&self, task: Runnable); 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 struct WindowOptions<'a> {
pub bounds: RectF, pub bounds: RectF,

View File

@ -7,22 +7,22 @@ use crate::{
AssetCache, Scene, AssetCache, Scene,
}; };
use pathfinder_geometry::vector::{vec2f, Vector2F}; 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 { pub struct Presenter {
window_id: usize, window_id: usize,
rendered_views: HashMap<usize, Box<dyn Element>>, rendered_views: HashMap<usize, Box<dyn Element>>,
parents: HashMap<usize, usize>, parents: HashMap<usize, usize>,
font_cache: Rc<FontCache>, font_cache: Arc<FontCache>,
text_layout_cache: TextLayoutCache, text_layout_cache: TextLayoutCache,
asset_cache: Rc<AssetCache>, asset_cache: Arc<AssetCache>,
} }
impl Presenter { impl Presenter {
pub fn new( pub fn new(
window_id: usize, window_id: usize,
font_cache: Rc<FontCache>, font_cache: Arc<FontCache>,
asset_cache: Rc<AssetCache>, asset_cache: Arc<AssetCache>,
app: &MutableAppContext, app: &MutableAppContext,
) -> Self { ) -> Self {
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 { for view_id in invalidation.updated {
self.rendered_views self.rendered_views
.insert(view_id, app.render_view(self.window_id, view_id).unwrap()); .insert(view_id, app.render_view(self.window_id, view_id).unwrap());
@ -52,9 +52,9 @@ impl Presenter {
scale_factor: f32, scale_factor: f32,
app: &mut MutableAppContext, app: &mut MutableAppContext,
) -> Scene { ) -> Scene {
self.layout(window_size, app.ctx()); self.layout(window_size, app.downgrade());
self.after_layout(app); 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(); self.text_layout_cache.finish_frame();
scene scene
} }

View File

@ -26,6 +26,7 @@ log = "0.4"
num_cpus = "1.13.0" num_cpus = "1.13.0"
parking_lot = "0.11.1" parking_lot = "0.11.1"
rand = "0.8.3" rand = "0.8.3"
rust-embed = "5.9.0"
simplelog = "0.9" simplelog = "0.9"
smallvec = "1.6.1" smallvec = "1.6.1"
smol = "1.2.5" smol = "1.2.5"

13
zed/src/assets.rs Normal file
View File

@ -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<std::borrow::Cow<[u8]>> {
Self::get(path).ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
}
}

View File

@ -1907,46 +1907,46 @@ mod tests {
use gpui::App; use gpui::App;
use std::{cell::RefCell, rc::Rc}; 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 buffer1 = app.add_model(|_| Buffer::new(0, "abcdef"));
let buffer_2_events = Rc::new(RefCell::new(Vec::new())); 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")); buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap()
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(); buffer2.update(&mut app, |buffer, ctx| {
ctx.subscribe(&buffer2, move |_, event, _| { buffer.apply_ops(ops, Some(ctx)).unwrap();
buffer_2_events.borrow_mut().push(event.clone())
}); });
buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap() let buffer_1_events = buffer_1_events.borrow();
}); assert_eq!(
buffer2.update(&mut app, |buffer, ctx| { *buffer_1_events,
buffer.apply_ops(ops, Some(ctx)).unwrap(); vec![Event::Edited(vec![Edit {
}); old_range: 2..4,
new_range: 2..5
}])]
);
let buffer_1_events = buffer_1_events.borrow(); let buffer_2_events = buffer_2_events.borrow();
assert_eq!( assert_eq!(
*buffer_1_events, *buffer_2_events,
vec![Event::Edited(vec![Edit { vec![Event::Edited(vec![Edit {
old_range: 2..4, old_range: 2..4,
new_range: 2..5 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] #[test]

View File

@ -418,7 +418,7 @@ impl Element for BufferElement {
let view = self.view.as_ref(app); let view = self.view.as_ref(app);
view.clamp_scroll_left( view.clamp_scroll_left(
layout 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(), .x(),
); );
@ -426,10 +426,10 @@ impl Element for BufferElement {
view.autoscroll_horizontally( view.autoscroll_horizontally(
view.scroll_position().y() as u32, view.scroll_position().y() as u32,
layout.text_size.x(), 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), view.em_width(ctx.font_cache),
&layout.line_layouts, &layout.line_layouts,
app.ctx(), app.downgrade(),
); );
} }
} }

View File

@ -1269,7 +1269,7 @@ mod tests {
#[test] #[test]
fn test_selection_with_mouse() { 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 buffer = app.add_model(|_| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n"));
let settings = settings::channel(&FontCache::new()).unwrap().1; let settings = settings::channel(&FontCache::new()).unwrap().1;
let (_, buffer_view) = let (_, buffer_view) =
@ -1373,19 +1373,21 @@ mod tests {
let font_cache = FontCache::new(); let font_cache = FontCache::new();
let layout_cache = TextLayoutCache::new(); let layout_cache = TextLayoutCache::new();
let mut app = App::new().unwrap(); App::test((), |mut app| async move {
let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6))); let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6)));
let settings = settings::channel(&font_cache).unwrap().1; let settings = settings::channel(&font_cache).unwrap().1;
let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); let (_, view) =
app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
view.read(&app, |view, app| { view.read(&app, |view, app| {
let layouts = view.layout_line_numbers(1000.0, &font_cache, &layout_cache, app)?; let layouts = view.layout_line_numbers(1000.0, &font_cache, &layout_cache, app)?;
assert_eq!(layouts.len(), 6); assert_eq!(layouts.len(), 6);
Result::<()>::Ok(()) Result::<()>::Ok(())
})?; })?;
Ok(()) Ok(())
})
} }
#[test] #[test]

View File

@ -1,3 +1,4 @@
pub mod assets;
pub mod editor; pub mod editor;
mod operation_queue; mod operation_queue;
pub mod settings; pub mod settings;

View File

@ -27,7 +27,7 @@ fn main() {
let (settings_tx, settings_rx) = settings::channel(&font_cache).unwrap(); 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() platform::runner()
.on_finish_launching(move || { .on_finish_launching(move || {