mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-24 11:01:54 +00:00
WIP: Return WindowHandle<V: View> from AppContext::add_window
This commit is contained in:
parent
5e9f7f10c0
commit
b695c42e11
3 changed files with 268 additions and 118 deletions
|
@ -130,8 +130,12 @@ pub trait BorrowAppContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BorrowWindowContext {
|
pub trait BorrowWindowContext {
|
||||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T;
|
fn read_with<T, F>(&self, window_id: usize, f: F) -> T
|
||||||
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T;
|
where
|
||||||
|
F: FnOnce(&WindowContext) -> T;
|
||||||
|
fn update<T, F>(&mut self, window_id: usize, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut WindowContext) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -402,7 +406,7 @@ impl AsyncAppContext {
|
||||||
&mut self,
|
&mut self,
|
||||||
window_options: WindowOptions,
|
window_options: WindowOptions,
|
||||||
build_root_view: F,
|
build_root_view: F,
|
||||||
) -> (usize, ViewHandle<T>)
|
) -> WindowHandle<T>
|
||||||
where
|
where
|
||||||
T: View,
|
T: View,
|
||||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||||
|
@ -1300,7 +1304,7 @@ impl AppContext {
|
||||||
&mut self,
|
&mut self,
|
||||||
window_options: WindowOptions,
|
window_options: WindowOptions,
|
||||||
build_root_view: F,
|
build_root_view: F,
|
||||||
) -> (usize, ViewHandle<V>)
|
) -> WindowHandle<V>
|
||||||
where
|
where
|
||||||
V: View,
|
V: View,
|
||||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||||
|
@ -1311,9 +1315,8 @@ impl AppContext {
|
||||||
this.platform
|
this.platform
|
||||||
.open_window(window_id, window_options, this.foreground.clone());
|
.open_window(window_id, window_options, this.foreground.clone());
|
||||||
let window = this.build_window(window_id, platform_window, build_root_view);
|
let window = this.build_window(window_id, platform_window, build_root_view);
|
||||||
let root_view = window.root_view().clone().downcast::<V>().unwrap();
|
|
||||||
this.windows.insert(window_id, window);
|
this.windows.insert(window_id, window);
|
||||||
(window_id, root_view)
|
WindowHandle::new(window_id, this.ref_counts.clone())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3802,6 +3805,131 @@ impl<T> Clone for WeakModelHandle<T> {
|
||||||
|
|
||||||
impl<T> Copy for WeakModelHandle<T> {}
|
impl<T> Copy for WeakModelHandle<T> {}
|
||||||
|
|
||||||
|
pub struct WindowHandle<T> {
|
||||||
|
any_handle: AnyWindowHandle,
|
||||||
|
view_type: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View> WindowHandle<V> {
|
||||||
|
fn id(&self) -> usize {
|
||||||
|
self.any_handle.id()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(window_id: usize, ref_counts: Arc<Mutex<RefCounts>>) -> Self {
|
||||||
|
WindowHandle {
|
||||||
|
any_handle: AnyWindowHandle::new::<V>(window_id, ref_counts),
|
||||||
|
view_type: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root(&self, cx: &impl BorrowAppContext) -> ViewHandle<V> {
|
||||||
|
self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_with<C, F, R>(&self, cx: &C, read: F) -> R
|
||||||
|
where
|
||||||
|
C: BorrowAppContext,
|
||||||
|
F: FnOnce(&WindowContext) -> R,
|
||||||
|
{
|
||||||
|
cx.read_with(|cx| cx.read_window(self.id(), read).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update<C, F, R>(&self, cx: &mut C, update: F) -> R
|
||||||
|
where
|
||||||
|
C: BorrowAppContext,
|
||||||
|
F: FnOnce(&mut WindowContext) -> R,
|
||||||
|
{
|
||||||
|
cx.update(|cx| cx.update_window(self.id(), update).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_root<C, F, R>(&self, cx: &mut C, update: F) -> R
|
||||||
|
where
|
||||||
|
C: BorrowAppContext,
|
||||||
|
F: FnOnce(&mut V, &mut ViewContext<V>) -> R,
|
||||||
|
{
|
||||||
|
let window_id = self.id();
|
||||||
|
cx.update(|cx| {
|
||||||
|
cx.update_window(window_id, |cx| {
|
||||||
|
cx.root_view()
|
||||||
|
.clone()
|
||||||
|
.downcast::<V>()
|
||||||
|
.unwrap()
|
||||||
|
.update(cx, update)
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_root<'a>(&self, cx: &'a AppContext) -> &'a V {
|
||||||
|
let root_view = cx
|
||||||
|
.read_window(self.id(), |cx| cx.root_view().clone().downcast().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
root_view.read(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_root_with<C, F, R>(&self, cx: &C, read: F) -> R
|
||||||
|
where
|
||||||
|
C: BorrowAppContext,
|
||||||
|
F: FnOnce(&V, &ViewContext<V>) -> R,
|
||||||
|
{
|
||||||
|
self.read_with(cx, |cx| {
|
||||||
|
cx.root_view()
|
||||||
|
.downcast_ref::<V>()
|
||||||
|
.unwrap()
|
||||||
|
.read_with(cx, read)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_view<C, U, F>(&self, cx: &mut C, build_view: F) -> ViewHandle<U>
|
||||||
|
where
|
||||||
|
C: BorrowAppContext,
|
||||||
|
U: View,
|
||||||
|
F: FnOnce(&mut ViewContext<U>) -> U,
|
||||||
|
{
|
||||||
|
self.update(cx, |cx| cx.add_view(build_view))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AnyWindowHandle {
|
||||||
|
window_id: usize,
|
||||||
|
root_view_type: TypeId,
|
||||||
|
ref_counts: Arc<Mutex<RefCounts>>,
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
handle_id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyWindowHandle {
|
||||||
|
fn new<V: View>(window_id: usize, ref_counts: Arc<Mutex<RefCounts>>) -> Self {
|
||||||
|
ref_counts.lock().inc_window(window_id);
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
let handle_id = ref_counts
|
||||||
|
.lock()
|
||||||
|
.leak_detector
|
||||||
|
.lock()
|
||||||
|
.handle_created(None, window_id);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
window_id,
|
||||||
|
root_view_type: TypeId::of::<V>(),
|
||||||
|
ref_counts,
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
handle_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> usize {
|
||||||
|
self.window_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AnyWindowHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.ref_counts.lock().dec_window(self.window_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct ViewHandle<T> {
|
pub struct ViewHandle<T> {
|
||||||
any_handle: AnyViewHandle,
|
any_handle: AnyViewHandle,
|
||||||
|
@ -4684,11 +4812,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, view) = cx.add_window(|_| View { render_count: 0 });
|
let window = cx.add_window(|_| View { render_count: 0 });
|
||||||
let called_defer = Rc::new(AtomicBool::new(false));
|
let called_defer = Rc::new(AtomicBool::new(false));
|
||||||
let called_after_window_update = Rc::new(AtomicBool::new(false));
|
let called_after_window_update = Rc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
view.update(cx, |this, cx| {
|
window.update_root(cx, |this, cx| {
|
||||||
assert_eq!(this.render_count, 1);
|
assert_eq!(this.render_count, 1);
|
||||||
cx.defer({
|
cx.defer({
|
||||||
let called_defer = called_defer.clone();
|
let called_defer = called_defer.clone();
|
||||||
|
@ -4712,7 +4840,7 @@ mod tests {
|
||||||
|
|
||||||
assert!(called_defer.load(SeqCst));
|
assert!(called_defer.load(SeqCst));
|
||||||
assert!(called_after_window_update.load(SeqCst));
|
assert!(called_after_window_update.load(SeqCst));
|
||||||
assert_eq!(view.read_with(cx, |view, _| view.render_count), 3);
|
assert_eq!(window.read_root_with(cx, |view, _| view.render_count), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
|
@ -4751,9 +4879,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, _root_view) = cx.add_window(|cx| View::new(None, cx));
|
let window = cx.add_window(|cx| View::new(None, cx));
|
||||||
let handle_1 = cx.add_view(window_id, |cx| View::new(None, cx));
|
let handle_1 = window.add_view(cx, |cx| View::new(None, cx));
|
||||||
let handle_2 = cx.add_view(window_id, |cx| View::new(Some(handle_1.clone()), cx));
|
let handle_2 = window.add_view(cx, |cx| View::new(Some(handle_1.clone()), cx));
|
||||||
assert_eq!(cx.read(|cx| cx.views.len()), 3);
|
assert_eq!(cx.read(|cx| cx.views.len()), 3);
|
||||||
|
|
||||||
handle_1.update(cx, |view, cx| {
|
handle_1.update(cx, |view, cx| {
|
||||||
|
@ -4813,11 +4941,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mouse_down_count = Arc::new(AtomicUsize::new(0));
|
let mouse_down_count = Arc::new(AtomicUsize::new(0));
|
||||||
let (window_id, _) = cx.add_window(Default::default(), |_| View {
|
let window = cx.add_window(Default::default(), |_| View {
|
||||||
mouse_down_count: mouse_down_count.clone(),
|
mouse_down_count: mouse_down_count.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.update_window(window_id, |cx| {
|
window.update(cx, |cx| {
|
||||||
// Ensure window's root element is in a valid lifecycle state.
|
// Ensure window's root element is in a valid lifecycle state.
|
||||||
cx.dispatch_event(
|
cx.dispatch_event(
|
||||||
Event::MouseDown(MouseButtonEvent {
|
Event::MouseDown(MouseButtonEvent {
|
||||||
|
@ -4876,9 +5004,11 @@ mod tests {
|
||||||
let model = cx.add_model(|_| Model {
|
let model = cx.add_model(|_| Model {
|
||||||
released: model_released.clone(),
|
released: model_released.clone(),
|
||||||
});
|
});
|
||||||
let (window_id, view) = cx.add_window(Default::default(), |_| View {
|
let window = cx.add_window(Default::default(), |_| View {
|
||||||
released: view_released.clone(),
|
released: view_released.clone(),
|
||||||
});
|
});
|
||||||
|
let view = window.root(cx);
|
||||||
|
|
||||||
assert!(!model_released.get());
|
assert!(!model_released.get());
|
||||||
assert!(!view_released.get());
|
assert!(!view_released.get());
|
||||||
|
|
||||||
|
@ -4900,7 +5030,7 @@ mod tests {
|
||||||
assert!(model_release_observed.get());
|
assert!(model_release_observed.get());
|
||||||
|
|
||||||
drop(view);
|
drop(view);
|
||||||
cx.update_window(window_id, |cx| cx.remove_window());
|
window.update(cx, |cx| cx.remove_window());
|
||||||
assert!(view_released.get());
|
assert!(view_released.get());
|
||||||
assert!(view_release_observed.get());
|
assert!(view_release_observed.get());
|
||||||
}
|
}
|
||||||
|
@ -4913,8 +5043,9 @@ mod tests {
|
||||||
type Event = String;
|
type Event = String;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, handle_1) = cx.add_window(|_| TestView::default());
|
let window = cx.add_window(|_| TestView::default());
|
||||||
let handle_2 = cx.add_view(window_id, |_| TestView::default());
|
let handle_1 = window.root(cx);
|
||||||
|
let handle_2 = window.add_view(cx, |_| TestView::default());
|
||||||
let handle_3 = cx.add_model(|_| Model);
|
let handle_3 = cx.add_model(|_| Model);
|
||||||
|
|
||||||
handle_1.update(cx, |_, cx| {
|
handle_1.update(cx, |_, cx| {
|
||||||
|
@ -5140,9 +5271,9 @@ mod tests {
|
||||||
type Event = ();
|
type Event = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, _root_view) = cx.add_window(|_| TestView::default());
|
let window = cx.add_window(|_| TestView::default());
|
||||||
let observing_view = cx.add_view(window_id, |_| TestView::default());
|
let observing_view = window.add_view(cx, |_| TestView::default());
|
||||||
let emitting_view = cx.add_view(window_id, |_| TestView::default());
|
let emitting_view = window.add_view(cx, |_| TestView::default());
|
||||||
let observing_model = cx.add_model(|_| Model);
|
let observing_model = cx.add_model(|_| Model);
|
||||||
let observed_model = cx.add_model(|_| Model);
|
let observed_model = cx.add_model(|_| Model);
|
||||||
|
|
||||||
|
@ -5165,7 +5296,7 @@ mod tests {
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
fn test_view_emit_before_subscribe_in_same_update_cycle(cx: &mut AppContext) {
|
fn test_view_emit_before_subscribe_in_same_update_cycle(cx: &mut AppContext) {
|
||||||
let (_, view) = cx.add_window::<TestView, _>(Default::default(), |cx| {
|
let window = cx.add_window::<TestView, _>(Default::default(), |cx| {
|
||||||
drop(cx.subscribe(&cx.handle(), {
|
drop(cx.subscribe(&cx.handle(), {
|
||||||
move |this, _, _, _| this.events.push("dropped before flush".into())
|
move |this, _, _, _| this.events.push("dropped before flush".into())
|
||||||
}));
|
}));
|
||||||
|
@ -5181,7 +5312,7 @@ mod tests {
|
||||||
TestView { events: Vec::new() }
|
TestView { events: Vec::new() }
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(view.read(cx).events, ["before emit"]);
|
assert_eq!(window.read_root(cx).events, ["before emit"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
|
@ -5195,7 +5326,8 @@ mod tests {
|
||||||
type Event = ();
|
type Event = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, view) = cx.add_window(|_| TestView::default());
|
let window = cx.add_window(|_| TestView::default());
|
||||||
|
let view = window.root(cx);
|
||||||
let model = cx.add_model(|_| Model {
|
let model = cx.add_model(|_| Model {
|
||||||
state: "old-state".into(),
|
state: "old-state".into(),
|
||||||
});
|
});
|
||||||
|
@ -5216,7 +5348,7 @@ mod tests {
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
fn test_view_notify_before_observe_in_same_update_cycle(cx: &mut AppContext) {
|
fn test_view_notify_before_observe_in_same_update_cycle(cx: &mut AppContext) {
|
||||||
let (_, view) = cx.add_window::<TestView, _>(Default::default(), |cx| {
|
let window = cx.add_window::<TestView, _>(Default::default(), |cx| {
|
||||||
drop(cx.observe(&cx.handle(), {
|
drop(cx.observe(&cx.handle(), {
|
||||||
move |this, _, _| this.events.push("dropped before flush".into())
|
move |this, _, _| this.events.push("dropped before flush".into())
|
||||||
}));
|
}));
|
||||||
|
@ -5232,7 +5364,7 @@ mod tests {
|
||||||
TestView { events: Vec::new() }
|
TestView { events: Vec::new() }
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(view.read(cx).events, ["before notify"]);
|
assert_eq!(window.read_root(cx).events, ["before notify"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
|
@ -5243,7 +5375,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let model = cx.add_model(|_| Model);
|
let model = cx.add_model(|_| Model);
|
||||||
let (_, view) = cx.add_window(|_| TestView::default());
|
let window = cx.add_window(|_| TestView::default());
|
||||||
|
let view = window.root(cx);
|
||||||
|
|
||||||
view.update(cx, |_, cx| {
|
view.update(cx, |_, cx| {
|
||||||
model.update(cx, |_, cx| cx.notify());
|
model.update(cx, |_, cx| cx.notify());
|
||||||
|
@ -5267,8 +5400,8 @@ mod tests {
|
||||||
type Event = ();
|
type Event = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, _root_view) = cx.add_window(|_| TestView::default());
|
let window = cx.add_window(|_| TestView::default());
|
||||||
let observing_view = cx.add_view(window_id, |_| TestView::default());
|
let observing_view = window.add_view(cx, |_| TestView::default());
|
||||||
let observing_model = cx.add_model(|_| Model);
|
let observing_model = cx.add_model(|_| Model);
|
||||||
let observed_model = cx.add_model(|_| Model);
|
let observed_model = cx.add_model(|_| Model);
|
||||||
|
|
||||||
|
@ -5390,9 +5523,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, _root_view) = cx.add_window(|_| View);
|
let window = cx.add_window(|_| View);
|
||||||
let observing_view = cx.add_view(window_id, |_| View);
|
let observing_view = window.add_view(cx, |_| View);
|
||||||
let observed_view = cx.add_view(window_id, |_| View);
|
let observed_view = window.add_view(cx, |_| View);
|
||||||
|
|
||||||
let observation_count = Rc::new(RefCell::new(0));
|
let observation_count = Rc::new(RefCell::new(0));
|
||||||
observing_view.update(cx, |_, cx| {
|
observing_view.update(cx, |_, cx| {
|
||||||
|
@ -5474,13 +5607,14 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let view_events: Arc<Mutex<Vec<String>>> = Default::default();
|
let view_events: Arc<Mutex<Vec<String>>> = Default::default();
|
||||||
let (window_id, view_1) = cx.add_window(|_| View {
|
let window = cx.add_window(|_| View {
|
||||||
events: view_events.clone(),
|
events: view_events.clone(),
|
||||||
name: "view 1".to_string(),
|
name: "view 1".to_string(),
|
||||||
child: None,
|
child: None,
|
||||||
});
|
});
|
||||||
let view_2 = cx
|
let view_1 = window.root(cx);
|
||||||
.update_window(window_id, |cx| {
|
let view_2 = window
|
||||||
|
.update(cx, |cx| {
|
||||||
let view_2 = cx.add_view(|_| View {
|
let view_2 = cx.add_view(|_| View {
|
||||||
events: view_events.clone(),
|
events: view_events.clone(),
|
||||||
name: "view 2".to_string(),
|
name: "view 2".to_string(),
|
||||||
|
@ -5731,40 +5865,34 @@ mod tests {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
let (window_id, view_1) =
|
let window = cx.add_window(Default::default(), |_| ViewA { id: 1, child: None });
|
||||||
cx.add_window(Default::default(), |_| ViewA { id: 1, child: None });
|
let view_1 = window.root(cx);
|
||||||
let view_2 = cx
|
let view_2 = window.update(cx, |cx| {
|
||||||
.update_window(window_id, |cx| {
|
|
||||||
let child = cx.add_view(|_| ViewB { id: 2, child: None });
|
let child = cx.add_view(|_| ViewB { id: 2, child: None });
|
||||||
view_1.update(cx, |view, cx| {
|
view_1.update(cx, |view, cx| {
|
||||||
view.child = Some(child.clone().into_any());
|
view.child = Some(child.clone().into_any());
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
child
|
child
|
||||||
})
|
});
|
||||||
.unwrap();
|
let view_3 = window.update(cx, |cx| {
|
||||||
let view_3 = cx
|
|
||||||
.update_window(window_id, |cx| {
|
|
||||||
let child = cx.add_view(|_| ViewA { id: 3, child: None });
|
let child = cx.add_view(|_| ViewA { id: 3, child: None });
|
||||||
view_2.update(cx, |view, cx| {
|
view_2.update(cx, |view, cx| {
|
||||||
view.child = Some(child.clone().into_any());
|
view.child = Some(child.clone().into_any());
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
child
|
child
|
||||||
})
|
});
|
||||||
.unwrap();
|
let view_4 = window.update(cx, |cx| {
|
||||||
let view_4 = cx
|
|
||||||
.update_window(window_id, |cx| {
|
|
||||||
let child = cx.add_view(|_| ViewB { id: 4, child: None });
|
let child = cx.add_view(|_| ViewB { id: 4, child: None });
|
||||||
view_3.update(cx, |view, cx| {
|
view_3.update(cx, |view, cx| {
|
||||||
view.child = Some(child.clone().into_any());
|
view.child = Some(child.clone().into_any());
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
child
|
child
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
cx.update_window(window_id, |cx| {
|
window.update(cx, |cx| {
|
||||||
cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string()))
|
cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string()))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5786,31 +5914,27 @@ mod tests {
|
||||||
|
|
||||||
// Remove view_1, which doesn't propagate the action
|
// Remove view_1, which doesn't propagate the action
|
||||||
|
|
||||||
let (window_id, view_2) =
|
let window = cx.add_window(Default::default(), |_| ViewB { id: 2, child: None });
|
||||||
cx.add_window(Default::default(), |_| ViewB { id: 2, child: None });
|
let view_2 = window.root(cx);
|
||||||
let view_3 = cx
|
let view_3 = window.update(cx, |cx| {
|
||||||
.update_window(window_id, |cx| {
|
|
||||||
let child = cx.add_view(|_| ViewA { id: 3, child: None });
|
let child = cx.add_view(|_| ViewA { id: 3, child: None });
|
||||||
view_2.update(cx, |view, cx| {
|
view_2.update(cx, |view, cx| {
|
||||||
view.child = Some(child.clone().into_any());
|
view.child = Some(child.clone().into_any());
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
child
|
child
|
||||||
})
|
});
|
||||||
.unwrap();
|
let view_4 = window.update(cx, |cx| {
|
||||||
let view_4 = cx
|
|
||||||
.update_window(window_id, |cx| {
|
|
||||||
let child = cx.add_view(|_| ViewB { id: 4, child: None });
|
let child = cx.add_view(|_| ViewB { id: 4, child: None });
|
||||||
view_3.update(cx, |view, cx| {
|
view_3.update(cx, |view, cx| {
|
||||||
view.child = Some(child.clone().into_any());
|
view.child = Some(child.clone().into_any());
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
child
|
child
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
actions.borrow_mut().clear();
|
actions.borrow_mut().clear();
|
||||||
cx.update_window(window_id, |cx| {
|
window.update(cx, |cx| {
|
||||||
cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string()))
|
cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string()))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5887,7 +6011,7 @@ mod tests {
|
||||||
view_3.keymap_context.add_identifier("b");
|
view_3.keymap_context.add_identifier("b");
|
||||||
view_3.keymap_context.add_identifier("c");
|
view_3.keymap_context.add_identifier("c");
|
||||||
|
|
||||||
let (window_id, _view_1) = cx.add_window(Default::default(), |cx| {
|
let window = cx.add_window(Default::default(), |cx| {
|
||||||
let view_2 = cx.add_view(|cx| {
|
let view_2 = cx.add_view(|cx| {
|
||||||
let view_3 = cx.add_view(|cx| {
|
let view_3 = cx.add_view(|cx| {
|
||||||
cx.focus_self();
|
cx.focus_self();
|
||||||
|
@ -6006,13 +6130,14 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, view_1) = cx.add_window(|cx| {
|
let window = cx.add_window(|cx| {
|
||||||
let view_2 = cx.add_view(|cx| {
|
let view_2 = cx.add_view(|cx| {
|
||||||
cx.focus_self();
|
cx.focus_self();
|
||||||
View2 {}
|
View2 {}
|
||||||
});
|
});
|
||||||
View1 { child: view_2 }
|
View1 { child: view_2 }
|
||||||
});
|
});
|
||||||
|
let view_1 = window.root(cx);
|
||||||
let view_2 = view_1.read_with(cx, |view, _| view.child.clone());
|
let view_2 = view_1.read_with(cx, |view, _| view.child.clone());
|
||||||
|
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
|
@ -6138,7 +6263,8 @@ mod tests {
|
||||||
|
|
||||||
impl_actions!(test, [ActionWithArg]);
|
impl_actions!(test, [ActionWithArg]);
|
||||||
|
|
||||||
let (window_id, view) = cx.add_window(|_| View);
|
let window = cx.add_window(|_| View);
|
||||||
|
let view = window.root(cx);
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.add_global_action(|_: &ActionWithArg, _| {});
|
cx.add_global_action(|_: &ActionWithArg, _| {});
|
||||||
cx.add_bindings(vec![
|
cx.add_bindings(vec![
|
||||||
|
@ -6250,7 +6376,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, view) = cx.add_window(|_| Counter(0));
|
let window = cx.add_window(|_| Counter(0));
|
||||||
|
let view = window.root(cx);
|
||||||
|
|
||||||
let condition1 = view.condition(cx, |view, _| view.0 == 2);
|
let condition1 = view.condition(cx, |view, _| view.0 == 2);
|
||||||
let condition2 = view.condition(cx, |view, _| view.0 == 3);
|
let condition2 = view.condition(cx, |view, _| view.0 == 3);
|
||||||
|
@ -6272,15 +6399,15 @@ mod tests {
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
async fn test_view_condition_timeout(cx: &mut TestAppContext) {
|
async fn test_view_condition_timeout(cx: &mut TestAppContext) {
|
||||||
let (_, view) = cx.add_window(|_| TestView::default());
|
let window = cx.add_window(|_| TestView::default());
|
||||||
view.condition(cx, |_, _| false).await;
|
window.root(cx).condition(cx, |_, _| false).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
#[should_panic(expected = "view dropped with pending condition")]
|
#[should_panic(expected = "view dropped with pending condition")]
|
||||||
async fn test_view_condition_panic_on_drop(cx: &mut TestAppContext) {
|
async fn test_view_condition_panic_on_drop(cx: &mut TestAppContext) {
|
||||||
let (window_id, _root_view) = cx.add_window(|_| TestView::default());
|
let window = cx.add_window(|_| TestView::default());
|
||||||
let view = cx.add_view(window_id, |_| TestView::default());
|
let view = window.add_view(cx, |_| TestView::default());
|
||||||
|
|
||||||
let condition = view.condition(cx, |_, _| false);
|
let condition = view.condition(cx, |_, _| false);
|
||||||
cx.update(|_| drop(view));
|
cx.update(|_| drop(view));
|
||||||
|
@ -6305,22 +6432,21 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, root_view) = cx.add_window(Default::default(), |_| View(0));
|
let window = cx.add_window(Default::default(), |_| View(0));
|
||||||
cx.update_window(window_id, |cx| {
|
let root_view = window.root(cx);
|
||||||
|
window.update(cx, |cx| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.window.rendered_views[&root_view.id()].name(),
|
cx.window.rendered_views[&root_view.id()].name(),
|
||||||
Some("render count: 0")
|
Some("render count: 0")
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let view = cx
|
let view = window.update(cx, |cx| {
|
||||||
.update_window(window_id, |cx| {
|
|
||||||
cx.refresh_windows();
|
cx.refresh_windows();
|
||||||
cx.add_view(|_| View(0))
|
cx.add_view(|_| View(0))
|
||||||
})
|
});
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
cx.update_window(window_id, |cx| {
|
window.update(cx, |cx| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.window.rendered_views[&root_view.id()].name(),
|
cx.window.rendered_views[&root_view.id()].name(),
|
||||||
Some("render count: 1")
|
Some("render count: 1")
|
||||||
|
@ -6333,7 +6459,7 @@ mod tests {
|
||||||
|
|
||||||
cx.update(|cx| cx.refresh_windows());
|
cx.update(|cx| cx.refresh_windows());
|
||||||
|
|
||||||
cx.update_window(window_id, |cx| {
|
window.update(cx, |cx| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.window.rendered_views[&root_view.id()].name(),
|
cx.window.rendered_views[&root_view.id()].name(),
|
||||||
Some("render count: 2")
|
Some("render count: 2")
|
||||||
|
@ -6349,7 +6475,7 @@ mod tests {
|
||||||
drop(view);
|
drop(view);
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.update_window(window_id, |cx| {
|
window.update(cx, |cx| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.window.rendered_views[&root_view.id()].name(),
|
cx.window.rendered_views[&root_view.id()].name(),
|
||||||
Some("render count: 3")
|
Some("render count: 3")
|
||||||
|
@ -6397,7 +6523,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let events = Rc::new(RefCell::new(Vec::new()));
|
let events = Rc::new(RefCell::new(Vec::new()));
|
||||||
let (window_1, _) = cx.add_window(|cx: &mut ViewContext<View>| {
|
let window_1 = cx.add_window(|cx: &mut ViewContext<View>| {
|
||||||
cx.observe_window_activation({
|
cx.observe_window_activation({
|
||||||
let events = events.clone();
|
let events = events.clone();
|
||||||
move |this, active, _| events.borrow_mut().push((this.0, active))
|
move |this, active, _| events.borrow_mut().push((this.0, active))
|
||||||
|
@ -6407,7 +6533,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
assert_eq!(mem::take(&mut *events.borrow_mut()), [("window 1", true)]);
|
assert_eq!(mem::take(&mut *events.borrow_mut()), [("window 1", true)]);
|
||||||
|
|
||||||
let (window_2, _) = cx.add_window(|cx: &mut ViewContext<View>| {
|
let window_2 = cx.add_window(|cx: &mut ViewContext<View>| {
|
||||||
cx.observe_window_activation({
|
cx.observe_window_activation({
|
||||||
let events = events.clone();
|
let events = events.clone();
|
||||||
move |this, active, _| events.borrow_mut().push((this.0, active))
|
move |this, active, _| events.borrow_mut().push((this.0, active))
|
||||||
|
@ -6420,7 +6546,7 @@ mod tests {
|
||||||
[("window 1", false), ("window 2", true)]
|
[("window 1", false), ("window 2", true)]
|
||||||
);
|
);
|
||||||
|
|
||||||
let (window_3, _) = cx.add_window(|cx: &mut ViewContext<View>| {
|
let window_3 = cx.add_window(|cx: &mut ViewContext<View>| {
|
||||||
cx.observe_window_activation({
|
cx.observe_window_activation({
|
||||||
let events = events.clone();
|
let events = events.clone();
|
||||||
move |this, active, _| events.borrow_mut().push((this.0, active))
|
move |this, active, _| events.borrow_mut().push((this.0, active))
|
||||||
|
|
|
@ -23,8 +23,10 @@ struct ElementStateRefCount {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RefCounts {
|
pub struct RefCounts {
|
||||||
|
window_counts: HashMap<usize, usize>,
|
||||||
entity_counts: HashMap<usize, usize>,
|
entity_counts: HashMap<usize, usize>,
|
||||||
element_state_counts: HashMap<ElementStateId, ElementStateRefCount>,
|
element_state_counts: HashMap<ElementStateId, ElementStateRefCount>,
|
||||||
|
dropped_windows: HashSet<usize>,
|
||||||
dropped_models: HashSet<usize>,
|
dropped_models: HashSet<usize>,
|
||||||
dropped_views: HashSet<(usize, usize)>,
|
dropped_views: HashSet<(usize, usize)>,
|
||||||
dropped_element_states: HashSet<ElementStateId>,
|
dropped_element_states: HashSet<ElementStateId>,
|
||||||
|
@ -43,6 +45,18 @@ impl RefCounts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn inc_window(&mut self, window_id: usize) {
|
||||||
|
match self.window_counts.entry(window_id) {
|
||||||
|
Entry::Occupied(mut entry) => {
|
||||||
|
*entry.get_mut() += 1;
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(1);
|
||||||
|
self.dropped_windows.remove(&window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn inc_model(&mut self, model_id: usize) {
|
pub fn inc_model(&mut self, model_id: usize) {
|
||||||
match self.entity_counts.entry(model_id) {
|
match self.entity_counts.entry(model_id) {
|
||||||
Entry::Occupied(mut entry) => {
|
Entry::Occupied(mut entry) => {
|
||||||
|
@ -85,6 +99,15 @@ impl RefCounts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dec_window(&mut self, window_id: usize) {
|
||||||
|
let count = self.window_counts.get_mut(&window_id).unwrap();
|
||||||
|
*count -= 1;
|
||||||
|
if *count == 0 {
|
||||||
|
self.entity_counts.remove(&window_id);
|
||||||
|
self.dropped_windows.insert(window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dec_model(&mut self, model_id: usize) {
|
pub fn dec_model(&mut self, model_id: usize) {
|
||||||
let count = self.entity_counts.get_mut(&model_id).unwrap();
|
let count = self.entity_counts.get_mut(&model_id).unwrap();
|
||||||
*count -= 1;
|
*count -= 1;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
platform::{Event, InputHandler, KeyDownEvent, Platform},
|
platform::{Event, InputHandler, KeyDownEvent, Platform},
|
||||||
Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle,
|
Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle,
|
||||||
ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle,
|
ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle,
|
||||||
WindowContext,
|
WindowContext, WindowHandle,
|
||||||
};
|
};
|
||||||
use collections::BTreeMap;
|
use collections::BTreeMap;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
@ -148,17 +148,18 @@ impl TestAppContext {
|
||||||
self.cx.borrow_mut().add_model(build_model)
|
self.cx.borrow_mut().add_model(build_model)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_window<T, F>(&mut self, build_root_view: F) -> (usize, ViewHandle<T>)
|
pub fn add_window<T, F>(&mut self, build_root_view: F) -> WindowHandle<T>
|
||||||
where
|
where
|
||||||
T: View,
|
T: View,
|
||||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||||
{
|
{
|
||||||
let (window_id, view) = self
|
let window = self
|
||||||
.cx
|
.cx
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.add_window(Default::default(), build_root_view);
|
.add_window(Default::default(), build_root_view);
|
||||||
self.simulate_window_activation(Some(window_id));
|
self.simulate_window_activation(Some(window.id()));
|
||||||
(window_id, view)
|
|
||||||
|
WindowHandle::new(window.id(), self.cx.borrow_mut().ref_counts.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
|
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
|
||||||
|
|
Loading…
Reference in a new issue