mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-21 03:18:47 +03:00
WIP: Start on rendering scenes via presenter
This commit is contained in:
parent
e809d6119a
commit
605bdd62dd
34
Cargo.lock
generated
34
Cargo.lock
generated
@ -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",
|
||||||
|
659
gpui/src/app.rs
659
gpui/src/app.rs
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
13
zed/src/assets.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
@ -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]
|
||||||
|
@ -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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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]
|
||||||
|
@ -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;
|
||||||
|
@ -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 || {
|
||||||
|
Loading…
Reference in New Issue
Block a user