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",
]
[[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",

View File

@ -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<RefCell<MutableAppContext>>);
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 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<Self> {
pub fn new(asset_source: impl AssetSource) -> Result<Self> {
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, 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 {
@ -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<Output = ()> {
@ -277,6 +285,8 @@ type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext);
pub struct MutableAppContext {
platform: Arc<dyn platform::App>,
fonts: Arc<FontCache>,
assets: Arc<AssetCache>,
ctx: AppContext,
actions: HashMap<TypeId, HashMap<String, Vec<Box<ActionCallback>>>>,
global_actions: HashMap<String, Vec<Box<GlobalActionCallback>>>,
@ -301,9 +311,15 @@ pub struct 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 {
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);

View File

@ -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<WindowState>);
struct WindowState {
native_window: id,
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>,
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) {
*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));
}
}
@ -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);
}

View File

@ -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,

View File

@ -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<usize, Box<dyn Element>>,
parents: HashMap<usize, usize>,
font_cache: Rc<FontCache>,
font_cache: Arc<FontCache>,
text_layout_cache: TextLayoutCache,
asset_cache: Rc<AssetCache>,
asset_cache: Arc<AssetCache>,
}
impl Presenter {
pub fn new(
window_id: usize,
font_cache: Rc<FontCache>,
asset_cache: Rc<AssetCache>,
font_cache: Arc<FontCache>,
asset_cache: Arc<AssetCache>,
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
}

View File

@ -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"

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 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]

View File

@ -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(),
);
}
}

View File

@ -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]

View File

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

View File

@ -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 || {