Consolidate pending effects logic into MutableAppContext::update

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2021-11-29 14:16:19 -08:00
parent 4cc1556ca4
commit 5ec003530f

View file

@ -342,10 +342,8 @@ impl App {
fn update<T, F: FnOnce(&mut MutableAppContext) -> T>(&mut self, callback: F) -> T { fn update<T, F: FnOnce(&mut MutableAppContext) -> T>(&mut self, callback: F) -> T {
let mut state = self.0.borrow_mut(); let mut state = self.0.borrow_mut();
state.pending_flushes += 1; let result = state.update(callback);
let result = callback(&mut *state);
state.pending_notifications.clear(); state.pending_notifications.clear();
state.flush_effects();
result result
} }
} }
@ -406,11 +404,7 @@ impl TestAppContext {
T: Entity, T: Entity,
F: FnOnce(&mut ModelContext<T>) -> T, F: FnOnce(&mut ModelContext<T>) -> T,
{ {
let mut state = self.cx.borrow_mut(); self.cx.borrow_mut().add_model(build_model)
state.pending_flushes += 1;
let handle = state.add_model(build_model);
state.flush_effects();
handle
} }
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) -> (usize, ViewHandle<T>)
@ -436,11 +430,7 @@ impl TestAppContext {
T: View, T: View,
F: FnOnce(&mut ViewContext<T>) -> T, F: FnOnce(&mut ViewContext<T>) -> T,
{ {
let mut state = self.cx.borrow_mut(); self.cx.borrow_mut().add_view(window_id, build_view)
state.pending_flushes += 1;
let handle = state.add_view(window_id, build_view);
state.flush_effects();
handle
} }
pub fn add_option_view<T, F>( pub fn add_option_view<T, F>(
@ -452,11 +442,7 @@ impl TestAppContext {
T: View, T: View,
F: FnOnce(&mut ViewContext<T>) -> Option<T>, F: FnOnce(&mut ViewContext<T>) -> Option<T>,
{ {
let mut state = self.cx.borrow_mut(); self.cx.borrow_mut().add_option_view(window_id, build_view)
state.pending_flushes += 1;
let handle = state.add_option_view(window_id, build_view);
state.flush_effects();
handle
} }
pub fn read<T, F: FnOnce(&AppContext) -> T>(&self, callback: F) -> T { pub fn read<T, F: FnOnce(&AppContext) -> T>(&self, callback: F) -> T {
@ -535,11 +521,7 @@ impl AsyncAppContext {
} }
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 {
let mut state = self.0.borrow_mut(); self.0.borrow_mut().update(callback)
state.pending_flushes += 1;
let result = callback(&mut *state);
state.flush_effects();
result
} }
pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T> pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
@ -569,11 +551,7 @@ impl UpdateModel for AsyncAppContext {
handle: &ModelHandle<E>, handle: &ModelHandle<E>,
update: &mut dyn FnMut(&mut E, &mut ModelContext<E>) -> O, update: &mut dyn FnMut(&mut E, &mut ModelContext<E>) -> O,
) -> O { ) -> O {
let mut state = self.0.borrow_mut(); self.0.borrow_mut().update_model(handle, update)
state.pending_flushes += 1;
let result = state.update_model(handle, update);
state.flush_effects();
result
} }
} }
@ -607,11 +585,7 @@ impl UpdateView for AsyncAppContext {
where where
T: View, T: View,
{ {
let mut state = self.0.borrow_mut(); self.0.borrow_mut().update_view(handle, update)
state.pending_flushes += 1;
let result = state.update_view(handle, update);
state.flush_effects();
result
} }
} }
@ -636,11 +610,7 @@ impl UpdateModel for TestAppContext {
handle: &ModelHandle<T>, handle: &ModelHandle<T>,
update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> O, update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> O,
) -> O { ) -> O {
let mut state = self.cx.borrow_mut(); self.cx.borrow_mut().update_model(handle, update)
state.pending_flushes += 1;
let result = state.update_model(handle, update);
state.flush_effects();
result
} }
} }
@ -665,11 +635,7 @@ impl UpdateView for TestAppContext {
where where
T: View, T: View,
{ {
let mut state = self.cx.borrow_mut(); self.cx.borrow_mut().update_view(handle, update)
state.pending_flushes += 1;
let result = state.update_view(handle, update);
state.flush_effects();
result
} }
} }
@ -727,6 +693,7 @@ impl MutableAppContext {
foreground_platform: Rc<dyn platform::ForegroundPlatform>, foreground_platform: Rc<dyn platform::ForegroundPlatform>,
font_cache: Arc<FontCache>, font_cache: Arc<FontCache>,
asset_source: impl AssetSource, asset_source: impl AssetSource,
// entity_drop_tx:
) -> Self { ) -> Self {
Self { Self {
weak_self: None, weak_self: None,
@ -941,9 +908,9 @@ impl MutableAppContext {
.collect() .collect()
} }
pub fn update<T, F: FnOnce() -> T>(&mut self, callback: F) -> T { pub fn update<T, F: FnOnce(&mut Self) -> T>(&mut self, callback: F) -> T {
self.pending_flushes += 1; self.pending_flushes += 1;
let result = callback(); let result = callback(self);
self.flush_effects(); self.flush_effects();
result result
} }
@ -1124,46 +1091,44 @@ impl MutableAppContext {
path: &[usize], path: &[usize],
action: &dyn AnyAction, action: &dyn AnyAction,
) -> bool { ) -> bool {
self.pending_flushes += 1; self.update(|this| {
let mut halted_dispatch = false; let mut halted_dispatch = false;
for view_id in path.iter().rev() {
if let Some(mut view) = this.cx.views.remove(&(window_id, *view_id)) {
let type_id = view.as_any().type_id();
for view_id in path.iter().rev() { if let Some((name, mut handlers)) = this
if let Some(mut view) = self.cx.views.remove(&(window_id, *view_id)) { .actions
let type_id = view.as_any().type_id();
if let Some((name, mut handlers)) = self
.actions
.get_mut(&type_id)
.and_then(|h| h.remove_entry(&action.id()))
{
for handler in handlers.iter_mut().rev() {
let halt_dispatch =
handler(view.as_mut(), action, self, window_id, *view_id);
if halt_dispatch {
halted_dispatch = true;
break;
}
}
self.actions
.get_mut(&type_id) .get_mut(&type_id)
.unwrap() .and_then(|h| h.remove_entry(&action.id()))
.insert(name, handlers); {
} for handler in handlers.iter_mut().rev() {
let halt_dispatch =
handler(view.as_mut(), action, this, window_id, *view_id);
if halt_dispatch {
halted_dispatch = true;
break;
}
}
this.actions
.get_mut(&type_id)
.unwrap()
.insert(name, handlers);
}
self.cx.views.insert((window_id, *view_id), view); this.cx.views.insert((window_id, *view_id), view);
if halted_dispatch { if halted_dispatch {
break; break;
}
} }
} }
}
if !halted_dispatch { if !halted_dispatch {
self.dispatch_global_action_any(action); this.dispatch_global_action_any(action);
} }
halted_dispatch
self.flush_effects(); })
halted_dispatch
} }
pub fn dispatch_global_action<A: Action>(&mut self, action: A) { pub fn dispatch_global_action<A: Action>(&mut self, action: A) {
@ -1171,14 +1136,14 @@ impl MutableAppContext {
} }
fn dispatch_global_action_any(&mut self, action: &dyn AnyAction) { fn dispatch_global_action_any(&mut self, action: &dyn AnyAction) {
if let Some((name, mut handlers)) = self.global_actions.remove_entry(&action.id()) { self.update(|this| {
self.pending_flushes += 1; if let Some((name, mut handlers)) = this.global_actions.remove_entry(&action.id()) {
for handler in handlers.iter_mut().rev() { for handler in handlers.iter_mut().rev() {
handler(action, self); handler(action, this);
}
this.global_actions.insert(name, handlers);
} }
self.global_actions.insert(name, handlers); })
self.flush_effects();
}
} }
pub fn add_bindings<T: IntoIterator<Item = keymap::Binding>>(&mut self, bindings: T) { pub fn add_bindings<T: IntoIterator<Item = keymap::Binding>>(&mut self, bindings: T) {
@ -1230,14 +1195,14 @@ impl MutableAppContext {
T: Entity, T: Entity,
F: FnOnce(&mut ModelContext<T>) -> T, F: FnOnce(&mut ModelContext<T>) -> T,
{ {
self.pending_flushes += 1; self.update(|this| {
let model_id = post_inc(&mut self.next_entity_id); let model_id = post_inc(&mut this.next_entity_id);
let handle = ModelHandle::new(model_id, &self.cx.ref_counts); let handle = ModelHandle::new(model_id, &this.cx.ref_counts);
let mut cx = ModelContext::new(self, model_id); let mut cx = ModelContext::new(this, model_id);
let model = build_model(&mut cx); let model = build_model(&mut cx);
self.cx.models.insert(model_id, Box::new(model)); this.cx.models.insert(model_id, Box::new(model));
self.flush_effects(); handle
handle })
} }
pub fn add_window<T, F>( pub fn add_window<T, F>(
@ -1249,26 +1214,26 @@ impl MutableAppContext {
T: View, T: View,
F: FnOnce(&mut ViewContext<T>) -> T, F: FnOnce(&mut ViewContext<T>) -> T,
{ {
self.pending_flushes += 1; self.update(|this| {
let window_id = post_inc(&mut self.next_window_id); let window_id = post_inc(&mut this.next_window_id);
let root_view = self.add_view(window_id, build_root_view); let root_view = this.add_view(window_id, build_root_view);
self.cx.windows.insert( this.cx.windows.insert(
window_id, window_id,
Window { Window {
root_view: root_view.clone().into(), root_view: root_view.clone().into(),
focused_view_id: root_view.id(), focused_view_id: root_view.id(),
invalidation: None, invalidation: None,
}, },
); );
self.open_platform_window(window_id, window_options); this.open_platform_window(window_id, window_options);
root_view.update(self, |view, cx| { root_view.update(this, |view, cx| {
view.on_focus(cx); view.on_focus(cx);
cx.notify(); cx.notify();
}); });
self.flush_effects();
(window_id, root_view) (window_id, root_view)
})
} }
pub fn remove_window(&mut self, window_id: usize) { pub fn remove_window(&mut self, window_id: usize) {
@ -1377,25 +1342,25 @@ impl MutableAppContext {
T: View, T: View,
F: FnOnce(&mut ViewContext<T>) -> Option<T>, F: FnOnce(&mut ViewContext<T>) -> Option<T>,
{ {
let view_id = post_inc(&mut self.next_entity_id); self.update(|this| {
self.pending_flushes += 1; let view_id = post_inc(&mut this.next_entity_id);
let handle = ViewHandle::new(window_id, view_id, &self.cx.ref_counts); let handle = ViewHandle::new(window_id, view_id, &this.cx.ref_counts);
let mut cx = ViewContext::new(self, window_id, view_id); let mut cx = ViewContext::new(this, window_id, view_id);
let handle = if let Some(view) = build_view(&mut cx) { let handle = if let Some(view) = build_view(&mut cx) {
self.cx.views.insert((window_id, view_id), Box::new(view)); this.cx.views.insert((window_id, view_id), Box::new(view));
if let Some(window) = self.cx.windows.get_mut(&window_id) { if let Some(window) = this.cx.windows.get_mut(&window_id) {
window window
.invalidation .invalidation
.get_or_insert_with(Default::default) .get_or_insert_with(Default::default)
.updated .updated
.insert(view_id); .insert(view_id);
} }
Some(handle) Some(handle)
} else { } else {
None None
}; };
self.flush_effects(); handle
handle })
} }
pub fn element_state<Tag: 'static, T: 'static + Default>( pub fn element_state<Tag: 'static, T: 'static + Default>(
@ -1647,27 +1612,25 @@ impl MutableAppContext {
return; return;
} }
self.pending_flushes += 1; self.update(|this| {
let blurred_id = this.cx.windows.get_mut(&window_id).map(|window| {
let blurred_id = window.focused_view_id;
window.focused_view_id = focused_id;
blurred_id
});
let blurred_id = self.cx.windows.get_mut(&window_id).map(|window| { if let Some(blurred_id) = blurred_id {
let blurred_id = window.focused_view_id; if let Some(mut blurred_view) = this.cx.views.remove(&(window_id, blurred_id)) {
window.focused_view_id = focused_id; blurred_view.on_blur(this, window_id, blurred_id);
blurred_id this.cx.views.insert((window_id, blurred_id), blurred_view);
}); }
if let Some(blurred_id) = blurred_id {
if let Some(mut blurred_view) = self.cx.views.remove(&(window_id, blurred_id)) {
blurred_view.on_blur(self, window_id, blurred_id);
self.cx.views.insert((window_id, blurred_id), blurred_view);
} }
}
if let Some(mut focused_view) = self.cx.views.remove(&(window_id, focused_id)) { if let Some(mut focused_view) = this.cx.views.remove(&(window_id, focused_id)) {
focused_view.on_focus(self, window_id, focused_id); focused_view.on_focus(this, window_id, focused_id);
self.cx.views.insert((window_id, focused_id), focused_view); this.cx.views.insert((window_id, focused_id), focused_view);
} }
})
self.flush_effects();
} }
pub fn spawn<F, Fut, T>(&self, f: F) -> Task<T> pub fn spawn<F, Fut, T>(&self, f: F) -> Task<T>
@ -1713,18 +1676,18 @@ impl UpdateModel for MutableAppContext {
update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> V, update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> V,
) -> V { ) -> V {
if let Some(mut model) = self.cx.models.remove(&handle.model_id) { if let Some(mut model) = self.cx.models.remove(&handle.model_id) {
self.pending_flushes += 1; self.update(|this| {
let mut cx = ModelContext::new(self, handle.model_id); let mut cx = ModelContext::new(this, handle.model_id);
let result = update( let result = update(
model model
.as_any_mut() .as_any_mut()
.downcast_mut() .downcast_mut()
.expect("downcast is type safe"), .expect("downcast is type safe"),
&mut cx, &mut cx,
); );
self.cx.models.insert(handle.model_id, model); this.cx.models.insert(handle.model_id, model);
self.flush_effects(); result
result })
} else { } else {
panic!("circular model update"); panic!("circular model update");
} }
@ -1759,25 +1722,25 @@ impl UpdateView for MutableAppContext {
where where
T: View, T: View,
{ {
self.pending_flushes += 1; self.update(|this| {
let mut view = self let mut view = this
.cx .cx
.views .views
.remove(&(handle.window_id, handle.view_id)) .remove(&(handle.window_id, handle.view_id))
.expect("circular view update"); .expect("circular view update");
let mut cx = ViewContext::new(self, handle.window_id, handle.view_id); let mut cx = ViewContext::new(this, handle.window_id, handle.view_id);
let result = update( let result = update(
view.as_any_mut() view.as_any_mut()
.downcast_mut() .downcast_mut()
.expect("downcast is type safe"), .expect("downcast is type safe"),
&mut cx, &mut cx,
); );
self.cx this.cx
.views .views
.insert((handle.window_id, handle.view_id), view); .insert((handle.window_id, handle.view_id), view);
self.flush_effects(); result
result })
} }
} }
@ -3336,7 +3299,9 @@ struct RefCounts {
impl RefCounts { impl RefCounts {
fn inc_model(&mut self, model_id: usize) { 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.get_mut() += 1, Entry::Occupied(mut entry) => {
*entry.get_mut() += 1;
}
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
entry.insert(1); entry.insert(1);
self.dropped_models.remove(&model_id); self.dropped_models.remove(&model_id);
@ -3403,16 +3368,11 @@ impl RefCounts {
HashSet<(usize, usize)>, HashSet<(usize, usize)>,
HashSet<(TypeId, ElementStateId)>, HashSet<(TypeId, ElementStateId)>,
) { ) {
let mut dropped_models = HashSet::new(); (
let mut dropped_views = HashSet::new(); std::mem::take(&mut self.dropped_models),
let mut dropped_element_states = HashSet::new(); std::mem::take(&mut self.dropped_views),
std::mem::swap(&mut self.dropped_models, &mut dropped_models); std::mem::take(&mut self.dropped_element_states),
std::mem::swap(&mut self.dropped_views, &mut dropped_views); )
std::mem::swap(
&mut self.dropped_element_states,
&mut dropped_element_states,
);
(dropped_models, dropped_views, dropped_element_states)
} }
} }
@ -3719,7 +3679,7 @@ mod tests {
assert!(!*model_released.lock()); assert!(!*model_released.lock());
assert!(!*view_released.lock()); assert!(!*view_released.lock());
cx.update(move || { cx.update(move |_| {
drop(model); drop(model);
}); });
assert!(*model_released.lock()); assert!(*model_released.lock());
@ -3825,7 +3785,7 @@ mod tests {
cx.subscribe(&observed_model, |_, _, _, _| {}).detach(); cx.subscribe(&observed_model, |_, _, _, _| {}).detach();
}); });
cx.update(|| { cx.update(|_| {
drop(observing_view); drop(observing_view);
drop(observing_model); drop(observing_model);
}); });
@ -3917,7 +3877,7 @@ mod tests {
cx.observe(&observed_model, |_, _, _| {}).detach(); cx.observe(&observed_model, |_, _, _| {}).detach();
}); });
cx.update(|| { cx.update(|_| {
drop(observing_view); drop(observing_view);
drop(observing_model); drop(observing_model);
}); });