In handle ::condition, re-poll on events as well as notifications

This commit is contained in:
Max Brunsfeld
2021-05-06 13:18:41 -07:00
parent 490fb485fd
commit 290fcb4c06

View File

@@ -12,12 +12,12 @@ use keymap::MatchResult;
use parking_lot::{Mutex, RwLock};
use pathfinder_geometry::{rect::RectF, vector::vec2f};
use platform::Event;
use postage::{sink::Sink as _, stream::Stream as _};
use postage::{mpsc, sink::Sink as _, stream::Stream as _};
use smol::prelude::*;
use std::{
any::{type_name, Any, TypeId},
cell::RefCell,
collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
collections::{HashMap, HashSet, VecDeque},
fmt::{self, Debug},
hash::{Hash, Hasher},
marker::PhantomData,
@@ -388,7 +388,6 @@ pub struct MutableAppContext {
subscriptions: HashMap<usize, Vec<Subscription>>,
model_observations: HashMap<usize, Vec<ModelObservation>>,
view_observations: HashMap<usize, Vec<ViewObservation>>,
async_observations: HashMap<usize, postage::broadcast::Sender<()>>,
window_invalidations: HashMap<usize, WindowInvalidation>,
presenters_and_platform_windows:
HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>,
@@ -430,7 +429,6 @@ impl MutableAppContext {
subscriptions: HashMap::new(),
model_observations: HashMap::new(),
view_observations: HashMap::new(),
async_observations: HashMap::new(),
window_invalidations: HashMap::new(),
presenters_and_platform_windows: HashMap::new(),
debug_elements_callbacks: HashMap::new(),
@@ -897,13 +895,11 @@ impl MutableAppContext {
self.ctx.models.remove(&model_id);
self.subscriptions.remove(&model_id);
self.model_observations.remove(&model_id);
self.async_observations.remove(&model_id);
}
for (window_id, view_id) in dropped_views {
self.subscriptions.remove(&view_id);
self.model_observations.remove(&view_id);
self.async_observations.remove(&view_id);
if let Some(window) = self.ctx.windows.get_mut(&window_id) {
self.window_invalidations
.entry(window_id)
@@ -1082,12 +1078,6 @@ impl MutableAppContext {
}
}
}
if let Entry::Occupied(mut entry) = self.async_observations.entry(observed_id) {
if entry.get_mut().blocking_send(()).is_err() {
entry.remove_entry();
}
}
}
fn notify_view_observers(&mut self, window_id: usize, view_id: usize) {
@@ -1098,7 +1088,12 @@ impl MutableAppContext {
.insert(view_id);
if let Some(observations) = self.view_observations.remove(&view_id) {
if self.ctx.models.contains_key(&view_id) {
if self
.ctx
.windows
.get(&window_id)
.map_or(false, |w| w.views.contains_key(&view_id))
{
for mut observation in observations {
let alive = if let Some(mut view) = self
.ctx
@@ -1134,12 +1129,6 @@ impl MutableAppContext {
}
}
}
if let Entry::Occupied(mut entry) = self.async_observations.entry(view_id) {
if entry.get_mut().blocking_send(()).is_err() {
entry.remove_entry();
}
}
}
fn focus(&mut self, window_id: usize, focused_id: usize) {
@@ -1780,6 +1769,10 @@ impl<'a, T: View> ViewContext<'a, T> {
self.window_id
}
pub fn view_id(&self) -> usize {
self.view_id
}
pub fn foreground(&self) -> &Rc<executor::Foreground> {
self.app.foreground_executor()
}
@@ -1855,22 +1848,11 @@ impl<'a, T: View> ViewContext<'a, T> {
F: 'static + FnMut(&mut T, ModelHandle<E>, &E::Event, &mut ViewContext<T>),
{
let emitter_handle = handle.downgrade();
self.app
.subscriptions
.entry(handle.id())
.or_default()
.push(Subscription::FromView {
window_id: self.window_id,
view_id: self.view_id,
callback: Box::new(move |view, payload, app, window_id, view_id| {
if let Some(emitter_handle) = emitter_handle.upgrade(app.as_ref()) {
let model = view.downcast_mut().expect("downcast is type safe");
let payload = payload.downcast_ref().expect("downcast is type safe");
let mut ctx = ViewContext::new(app, window_id, view_id);
callback(model, emitter_handle, payload, &mut ctx);
}
}),
});
self.subscribe(handle, move |model, payload, ctx| {
if let Some(emitter_handle) = emitter_handle.upgrade(ctx.as_ref()) {
callback(model, emitter_handle, payload, ctx);
}
});
}
pub fn subscribe_to_view<V, F>(&mut self, handle: &ViewHandle<V>, mut callback: F)
@@ -1880,7 +1862,19 @@ impl<'a, T: View> ViewContext<'a, T> {
F: 'static + FnMut(&mut T, ViewHandle<V>, &V::Event, &mut ViewContext<T>),
{
let emitter_handle = handle.downgrade();
self.subscribe(handle, move |view, payload, ctx| {
if let Some(emitter_handle) = emitter_handle.upgrade(ctx.as_ref()) {
callback(view, emitter_handle, payload, ctx);
}
});
}
pub fn subscribe<E, F>(&mut self, handle: &impl Handle<E>, mut callback: F)
where
E: Entity,
E::Event: 'static,
F: 'static + FnMut(&mut T, &E::Event, &mut ViewContext<T>),
{
self.app
.subscriptions
.entry(handle.id())
@@ -1888,13 +1882,11 @@ impl<'a, T: View> ViewContext<'a, T> {
.push(Subscription::FromView {
window_id: self.window_id,
view_id: self.view_id,
callback: Box::new(move |view, payload, app, window_id, view_id| {
if let Some(emitter_handle) = emitter_handle.upgrade(&app) {
let model = view.downcast_mut().expect("downcast is type safe");
let payload = payload.downcast_ref().expect("downcast is type safe");
let mut ctx = ViewContext::new(app, window_id, view_id);
callback(model, emitter_handle, payload, &mut ctx);
}
callback: Box::new(move |entity, payload, app, window_id, view_id| {
let entity = entity.downcast_mut().expect("downcast is type safe");
let payload = payload.downcast_ref().expect("downcast is type safe");
let mut ctx = ViewContext::new(app, window_id, view_id);
callback(entity, payload, &mut ctx);
}),
});
}
@@ -2138,12 +2130,24 @@ impl<T: Entity> ModelHandle<T> {
ctx: &TestAppContext,
mut predicate: impl FnMut(&T, &AppContext) -> bool,
) -> impl Future<Output = ()> {
let (tx, mut rx) = mpsc::channel(1024);
let mut ctx = ctx.0.borrow_mut();
let tx = ctx
.async_observations
.entry(self.id())
.or_insert_with(|| postage::broadcast::channel(128).0);
let mut rx = tx.subscribe();
self.update(&mut *ctx, |_, ctx| {
ctx.observe(self, {
let mut tx = tx.clone();
move |_, _, _| {
tx.blocking_send(()).ok();
}
});
ctx.subscribe(self, {
let mut tx = tx.clone();
move |_, _, _| {
tx.blocking_send(()).ok();
}
})
});
let ctx = ctx.weak_self.as_ref().unwrap().upgrade().unwrap();
let handle = self.downgrade();
@@ -2310,19 +2314,41 @@ impl<T: View> ViewHandle<T> {
pub fn condition(
&self,
ctx: &TestAppContext,
mut predicate: impl 'static + FnMut(&T, &AppContext) -> bool,
) -> impl 'static + Future<Output = ()> {
predicate: impl FnMut(&T, &AppContext) -> bool,
) -> impl Future<Output = ()> {
self.condition_with_duration(Duration::from_millis(500), ctx, predicate)
}
pub fn condition_with_duration(
&self,
duration: Duration,
ctx: &TestAppContext,
mut predicate: impl FnMut(&T, &AppContext) -> bool,
) -> impl Future<Output = ()> {
let (tx, mut rx) = mpsc::channel(1024);
let mut ctx = ctx.0.borrow_mut();
let tx = ctx
.async_observations
.entry(self.id())
.or_insert_with(|| postage::broadcast::channel(128).0);
let mut rx = tx.subscribe();
self.update(&mut *ctx, |_, ctx| {
ctx.observe_view(self, {
let mut tx = tx.clone();
move |_, _, _| {
tx.blocking_send(()).ok();
}
});
ctx.subscribe(self, {
let mut tx = tx.clone();
move |_, _, _| {
tx.blocking_send(()).ok();
}
})
});
let ctx = ctx.weak_self.as_ref().unwrap().upgrade().unwrap();
let handle = self.downgrade();
async move {
timeout(Duration::from_millis(200), async move {
timeout(duration, async move {
loop {
{
let ctx = ctx.borrow();
@@ -2330,7 +2356,7 @@ impl<T: View> ViewHandle<T> {
if predicate(
handle
.upgrade(ctx)
.expect("model dropped with pending condition")
.expect("view dropped with pending condition")
.read(ctx),
ctx,
) {
@@ -2340,7 +2366,7 @@ impl<T: View> ViewHandle<T> {
rx.recv()
.await
.expect("model dropped with pending condition");
.expect("view dropped with pending condition");
}
})
.await
@@ -3537,9 +3563,7 @@ mod tests {
model.update(&mut app, |model, ctx| model.inc(ctx));
assert_eq!(poll_once(&mut condition2).await, Some(()));
// Broadcast channel should be removed if no conditions remain on next notification.
model.update(&mut app, |_, ctx| ctx.notify());
app.update(|ctx| assert!(ctx.async_observations.get(&model.id()).is_none()));
});
}
@@ -3617,10 +3641,7 @@ mod tests {
view.update(&mut app, |view, ctx| view.inc(ctx));
assert_eq!(poll_once(&mut condition2).await, Some(()));
// Broadcast channel should be removed if no conditions remain on next notification.
view.update(&mut app, |_, ctx| ctx.notify());
app.update(|ctx| assert!(ctx.async_observations.get(&view.id()).is_none()));
});
}
@@ -3650,7 +3671,7 @@ mod tests {
}
#[test]
#[should_panic(expected = "model dropped with pending condition")]
#[should_panic(expected = "view dropped with pending condition")]
fn test_view_condition_panic_on_drop() {
struct View;