WIP: Return WindowHandle<V: View> from AppContext::add_window

This commit is contained in:
Nathan Sobo 2023-08-01 22:28:04 -06:00
parent 5e9f7f10c0
commit b695c42e11
3 changed files with 268 additions and 118 deletions

View file

@ -130,8 +130,12 @@ pub trait BorrowAppContext {
}
pub trait BorrowWindowContext {
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T;
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T;
fn read_with<T, F>(&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)]
@ -402,7 +406,7 @@ impl AsyncAppContext {
&mut self,
window_options: WindowOptions,
build_root_view: F,
) -> (usize, ViewHandle<T>)
) -> WindowHandle<T>
where
T: View,
F: FnOnce(&mut ViewContext<T>) -> T,
@ -1300,7 +1304,7 @@ impl AppContext {
&mut self,
window_options: WindowOptions,
build_root_view: F,
) -> (usize, ViewHandle<V>)
) -> WindowHandle<V>
where
V: View,
F: FnOnce(&mut ViewContext<V>) -> V,
@ -1311,9 +1315,8 @@ impl AppContext {
this.platform
.open_window(window_id, window_options, this.foreground.clone());
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);
(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> {}
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)]
pub struct ViewHandle<T> {
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_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);
cx.defer({
let called_defer = called_defer.clone();
@ -4712,7 +4840,7 @@ mod tests {
assert!(called_defer.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)]
@ -4751,9 +4879,9 @@ mod tests {
}
}
let (window_id, _root_view) = cx.add_window(|cx| View::new(None, cx));
let handle_1 = cx.add_view(window_id, |cx| View::new(None, cx));
let handle_2 = cx.add_view(window_id, |cx| View::new(Some(handle_1.clone()), cx));
let window = cx.add_window(|cx| View::new(None, cx));
let handle_1 = window.add_view(cx, |cx| View::new(None, 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);
handle_1.update(cx, |view, cx| {
@ -4813,11 +4941,11 @@ mod tests {
}
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(),
});
cx.update_window(window_id, |cx| {
window.update(cx, |cx| {
// Ensure window's root element is in a valid lifecycle state.
cx.dispatch_event(
Event::MouseDown(MouseButtonEvent {
@ -4876,9 +5004,11 @@ mod tests {
let model = cx.add_model(|_| Model {
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(),
});
let view = window.root(cx);
assert!(!model_released.get());
assert!(!view_released.get());
@ -4900,7 +5030,7 @@ mod tests {
assert!(model_release_observed.get());
drop(view);
cx.update_window(window_id, |cx| cx.remove_window());
window.update(cx, |cx| cx.remove_window());
assert!(view_released.get());
assert!(view_release_observed.get());
}
@ -4913,8 +5043,9 @@ mod tests {
type Event = String;
}
let (window_id, handle_1) = cx.add_window(|_| TestView::default());
let handle_2 = cx.add_view(window_id, |_| TestView::default());
let window = cx.add_window(|_| TestView::default());
let handle_1 = window.root(cx);
let handle_2 = window.add_view(cx, |_| TestView::default());
let handle_3 = cx.add_model(|_| Model);
handle_1.update(cx, |_, cx| {
@ -5140,9 +5271,9 @@ mod tests {
type Event = ();
}
let (window_id, _root_view) = cx.add_window(|_| TestView::default());
let observing_view = cx.add_view(window_id, |_| TestView::default());
let emitting_view = cx.add_view(window_id, |_| TestView::default());
let window = cx.add_window(|_| TestView::default());
let observing_view = window.add_view(cx, |_| TestView::default());
let emitting_view = window.add_view(cx, |_| TestView::default());
let observing_model = cx.add_model(|_| Model);
let observed_model = cx.add_model(|_| Model);
@ -5165,7 +5296,7 @@ mod tests {
#[crate::test(self)]
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(), {
move |this, _, _, _| this.events.push("dropped before flush".into())
}));
@ -5181,7 +5312,7 @@ mod tests {
TestView { events: Vec::new() }
});
assert_eq!(view.read(cx).events, ["before emit"]);
assert_eq!(window.read_root(cx).events, ["before emit"]);
}
#[crate::test(self)]
@ -5195,7 +5326,8 @@ mod tests {
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 {
state: "old-state".into(),
});
@ -5216,7 +5348,7 @@ mod tests {
#[crate::test(self)]
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(), {
move |this, _, _| this.events.push("dropped before flush".into())
}));
@ -5232,7 +5364,7 @@ mod tests {
TestView { events: Vec::new() }
});
assert_eq!(view.read(cx).events, ["before notify"]);
assert_eq!(window.read_root(cx).events, ["before notify"]);
}
#[crate::test(self)]
@ -5243,7 +5375,8 @@ mod tests {
}
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| {
model.update(cx, |_, cx| cx.notify());
@ -5267,8 +5400,8 @@ mod tests {
type Event = ();
}
let (window_id, _root_view) = cx.add_window(|_| TestView::default());
let observing_view = cx.add_view(window_id, |_| TestView::default());
let window = cx.add_window(|_| TestView::default());
let observing_view = window.add_view(cx, |_| TestView::default());
let observing_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 observing_view = cx.add_view(window_id, |_| View);
let observed_view = cx.add_view(window_id, |_| View);
let window = cx.add_window(|_| View);
let observing_view = window.add_view(cx, |_| View);
let observed_view = window.add_view(cx, |_| View);
let observation_count = Rc::new(RefCell::new(0));
observing_view.update(cx, |_, cx| {
@ -5474,13 +5607,14 @@ mod tests {
}
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(),
name: "view 1".to_string(),
child: None,
});
let view_2 = cx
.update_window(window_id, |cx| {
let view_1 = window.root(cx);
let view_2 = window
.update(cx, |cx| {
let view_2 = cx.add_view(|_| View {
events: view_events.clone(),
name: "view 2".to_string(),
@ -5731,40 +5865,34 @@ mod tests {
})
.detach();
let (window_id, view_1) =
cx.add_window(Default::default(), |_| ViewA { id: 1, child: None });
let view_2 = cx
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewB { id: 2, child: None });
view_1.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
})
.unwrap();
let view_3 = cx
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewA { id: 3, child: None });
view_2.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
})
.unwrap();
let view_4 = cx
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewB { id: 4, child: None });
view_3.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
})
.unwrap();
let window = cx.add_window(Default::default(), |_| ViewA { id: 1, child: None });
let view_1 = window.root(cx);
let view_2 = window.update(cx, |cx| {
let child = cx.add_view(|_| ViewB { id: 2, child: None });
view_1.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
});
let view_3 = window.update(cx, |cx| {
let child = cx.add_view(|_| ViewA { id: 3, child: None });
view_2.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
});
let view_4 = window.update(cx, |cx| {
let child = cx.add_view(|_| ViewB { id: 4, child: None });
view_3.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
});
cx.update_window(window_id, |cx| {
window.update(cx, |cx| {
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
let (window_id, view_2) =
cx.add_window(Default::default(), |_| ViewB { id: 2, child: None });
let view_3 = cx
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewA { id: 3, child: None });
view_2.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
})
.unwrap();
let view_4 = cx
.update_window(window_id, |cx| {
let child = cx.add_view(|_| ViewB { id: 4, child: None });
view_3.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
})
.unwrap();
let window = cx.add_window(Default::default(), |_| ViewB { id: 2, child: None });
let view_2 = window.root(cx);
let view_3 = window.update(cx, |cx| {
let child = cx.add_view(|_| ViewA { id: 3, child: None });
view_2.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
});
let view_4 = window.update(cx, |cx| {
let child = cx.add_view(|_| ViewB { id: 4, child: None });
view_3.update(cx, |view, cx| {
view.child = Some(child.clone().into_any());
cx.notify();
});
child
});
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()))
});
@ -5887,7 +6011,7 @@ mod tests {
view_3.keymap_context.add_identifier("b");
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_3 = cx.add_view(|cx| {
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| {
cx.focus_self();
View2 {}
});
View1 { child: view_2 }
});
let view_1 = window.root(cx);
let view_2 = view_1.read_with(cx, |view, _| view.child.clone());
cx.update(|cx| {
@ -6138,7 +6263,8 @@ mod tests {
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.add_global_action(|_: &ActionWithArg, _| {});
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 condition2 = view.condition(cx, |view, _| view.0 == 3);
@ -6272,15 +6399,15 @@ mod tests {
#[crate::test(self)]
#[should_panic]
async fn test_view_condition_timeout(cx: &mut TestAppContext) {
let (_, view) = cx.add_window(|_| TestView::default());
view.condition(cx, |_, _| false).await;
let window = cx.add_window(|_| TestView::default());
window.root(cx).condition(cx, |_, _| false).await;
}
#[crate::test(self)]
#[should_panic(expected = "view dropped with pending condition")]
async fn test_view_condition_panic_on_drop(cx: &mut TestAppContext) {
let (window_id, _root_view) = cx.add_window(|_| TestView::default());
let view = cx.add_view(window_id, |_| TestView::default());
let window = cx.add_window(|_| TestView::default());
let view = window.add_view(cx, |_| TestView::default());
let condition = view.condition(cx, |_, _| false);
cx.update(|_| drop(view));
@ -6305,22 +6432,21 @@ mod tests {
}
}
let (window_id, root_view) = cx.add_window(Default::default(), |_| View(0));
cx.update_window(window_id, |cx| {
let window = cx.add_window(Default::default(), |_| View(0));
let root_view = window.root(cx);
window.update(cx, |cx| {
assert_eq!(
cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 0")
);
});
let view = cx
.update_window(window_id, |cx| {
cx.refresh_windows();
cx.add_view(|_| View(0))
})
.unwrap();
let view = window.update(cx, |cx| {
cx.refresh_windows();
cx.add_view(|_| View(0))
});
cx.update_window(window_id, |cx| {
window.update(cx, |cx| {
assert_eq!(
cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 1")
@ -6333,7 +6459,7 @@ mod tests {
cx.update(|cx| cx.refresh_windows());
cx.update_window(window_id, |cx| {
window.update(cx, |cx| {
assert_eq!(
cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 2")
@ -6349,7 +6475,7 @@ mod tests {
drop(view);
});
cx.update_window(window_id, |cx| {
window.update(cx, |cx| {
assert_eq!(
cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 3")
@ -6397,7 +6523,7 @@ mod tests {
}
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({
let events = events.clone();
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)]);
let (window_2, _) = cx.add_window(|cx: &mut ViewContext<View>| {
let window_2 = cx.add_window(|cx: &mut ViewContext<View>| {
cx.observe_window_activation({
let events = events.clone();
move |this, active, _| events.borrow_mut().push((this.0, active))
@ -6420,7 +6546,7 @@ mod tests {
[("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({
let events = events.clone();
move |this, active, _| events.borrow_mut().push((this.0, active))

View file

@ -23,8 +23,10 @@ struct ElementStateRefCount {
#[derive(Default)]
pub struct RefCounts {
window_counts: HashMap<usize, usize>,
entity_counts: HashMap<usize, usize>,
element_state_counts: HashMap<ElementStateId, ElementStateRefCount>,
dropped_windows: HashSet<usize>,
dropped_models: HashSet<usize>,
dropped_views: HashSet<(usize, usize)>,
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) {
match self.entity_counts.entry(model_id) {
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) {
let count = self.entity_counts.get_mut(&model_id).unwrap();
*count -= 1;

View file

@ -6,7 +6,7 @@ use crate::{
platform::{Event, InputHandler, KeyDownEvent, Platform},
Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle,
ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle,
WindowContext,
WindowContext, WindowHandle,
};
use collections::BTreeMap;
use futures::Future;
@ -148,17 +148,18 @@ impl TestAppContext {
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
T: View,
F: FnOnce(&mut ViewContext<T>) -> T,
{
let (window_id, view) = self
let window = self
.cx
.borrow_mut()
.add_window(Default::default(), build_root_view);
self.simulate_window_activation(Some(window_id));
(window_id, view)
self.simulate_window_activation(Some(window.id()));
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>