Coalesce followed view updates only within one frame
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
4435d9b106
commit
880eaa268b
2 changed files with 115 additions and 27 deletions
|
@ -1224,7 +1224,17 @@ impl MutableAppContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn defer(&mut self, callback: Box<dyn FnOnce(&mut MutableAppContext)>) {
|
fn defer(&mut self, callback: Box<dyn FnOnce(&mut MutableAppContext)>) {
|
||||||
self.pending_effects.push_back(Effect::Deferred(callback))
|
self.pending_effects.push_back(Effect::Deferred {
|
||||||
|
callback,
|
||||||
|
after_window_update: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn after_window_update(&mut self, callback: impl 'static + FnOnce(&mut MutableAppContext)) {
|
||||||
|
self.pending_effects.push_back(Effect::Deferred {
|
||||||
|
callback: Box::new(callback),
|
||||||
|
after_window_update: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn notify_model(&mut self, model_id: usize) {
|
pub(crate) fn notify_model(&mut self, model_id: usize) {
|
||||||
|
@ -1640,6 +1650,7 @@ impl MutableAppContext {
|
||||||
|
|
||||||
fn flush_effects(&mut self) {
|
fn flush_effects(&mut self) {
|
||||||
self.pending_flushes = self.pending_flushes.saturating_sub(1);
|
self.pending_flushes = self.pending_flushes.saturating_sub(1);
|
||||||
|
let mut after_window_update_callbacks = Vec::new();
|
||||||
|
|
||||||
if !self.flushing_effects && self.pending_flushes == 0 {
|
if !self.flushing_effects && self.pending_flushes == 0 {
|
||||||
self.flushing_effects = true;
|
self.flushing_effects = true;
|
||||||
|
@ -1675,7 +1686,16 @@ impl MutableAppContext {
|
||||||
Effect::ViewNotification { window_id, view_id } => {
|
Effect::ViewNotification { window_id, view_id } => {
|
||||||
self.notify_view_observers(window_id, view_id)
|
self.notify_view_observers(window_id, view_id)
|
||||||
}
|
}
|
||||||
Effect::Deferred(callback) => callback(self),
|
Effect::Deferred {
|
||||||
|
callback,
|
||||||
|
after_window_update,
|
||||||
|
} => {
|
||||||
|
if after_window_update {
|
||||||
|
after_window_update_callbacks.push(callback);
|
||||||
|
} else {
|
||||||
|
callback(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
Effect::ModelRelease { model_id, model } => {
|
Effect::ModelRelease { model_id, model } => {
|
||||||
self.notify_release_observers(model_id, model.as_any())
|
self.notify_release_observers(model_id, model.as_any())
|
||||||
}
|
}
|
||||||
|
@ -1706,14 +1726,20 @@ impl MutableAppContext {
|
||||||
self.update_windows();
|
self.update_windows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.pending_effects.is_empty() {
|
||||||
|
for callback in after_window_update_callbacks.drain(..) {
|
||||||
|
callback(self);
|
||||||
|
}
|
||||||
|
|
||||||
if self.pending_effects.is_empty() {
|
if self.pending_effects.is_empty() {
|
||||||
self.flushing_effects = false;
|
self.flushing_effects = false;
|
||||||
self.pending_notifications.clear();
|
self.pending_notifications.clear();
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
refreshing = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshing = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2347,7 +2373,10 @@ pub enum Effect {
|
||||||
window_id: usize,
|
window_id: usize,
|
||||||
view_id: usize,
|
view_id: usize,
|
||||||
},
|
},
|
||||||
Deferred(Box<dyn FnOnce(&mut MutableAppContext)>),
|
Deferred {
|
||||||
|
callback: Box<dyn FnOnce(&mut MutableAppContext)>,
|
||||||
|
after_window_update: bool,
|
||||||
|
},
|
||||||
ModelRelease {
|
ModelRelease {
|
||||||
model_id: usize,
|
model_id: usize,
|
||||||
model: Box<dyn AnyModel>,
|
model: Box<dyn AnyModel>,
|
||||||
|
@ -2413,7 +2442,7 @@ impl Debug for Effect {
|
||||||
.field("window_id", window_id)
|
.field("window_id", window_id)
|
||||||
.field("view_id", view_id)
|
.field("view_id", view_id)
|
||||||
.finish(),
|
.finish(),
|
||||||
Effect::Deferred(_) => f.debug_struct("Effect::Deferred").finish(),
|
Effect::Deferred { .. } => f.debug_struct("Effect::Deferred").finish(),
|
||||||
Effect::ModelRelease { model_id, .. } => f
|
Effect::ModelRelease { model_id, .. } => f
|
||||||
.debug_struct("Effect::ModelRelease")
|
.debug_struct("Effect::ModelRelease")
|
||||||
.field("model_id", model_id)
|
.field("model_id", model_id)
|
||||||
|
@ -2945,6 +2974,18 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn after_window_update(
|
||||||
|
&mut self,
|
||||||
|
callback: impl 'static + FnOnce(&mut T, &mut ViewContext<T>),
|
||||||
|
) {
|
||||||
|
let handle = self.handle();
|
||||||
|
self.app.after_window_update(move |cx| {
|
||||||
|
handle.update(cx, |view, cx| {
|
||||||
|
callback(view, cx);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn propagate_action(&mut self) {
|
pub fn propagate_action(&mut self) {
|
||||||
self.app.halt_action_dispatch = false;
|
self.app.halt_action_dispatch = false;
|
||||||
}
|
}
|
||||||
|
@ -4424,7 +4465,7 @@ mod tests {
|
||||||
use smol::future::poll_once;
|
use smol::future::poll_once;
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
sync::atomic::{AtomicUsize, Ordering::SeqCst},
|
sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
|
@ -4622,6 +4663,58 @@ mod tests {
|
||||||
assert_eq!(*events.borrow(), ["before notify"]);
|
assert_eq!(*events.borrow(), ["before notify"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[crate::test(self)]
|
||||||
|
fn test_defer_and_after_window_update(cx: &mut MutableAppContext) {
|
||||||
|
struct View {
|
||||||
|
render_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity for View {
|
||||||
|
type Event = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::View for View {
|
||||||
|
fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
|
||||||
|
post_inc(&mut self.render_count);
|
||||||
|
Empty::new().boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ui_name() -> &'static str {
|
||||||
|
"View"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, view) = cx.add_window(Default::default(), |_| 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| {
|
||||||
|
assert_eq!(this.render_count, 1);
|
||||||
|
cx.defer({
|
||||||
|
let called_defer = called_defer.clone();
|
||||||
|
move |this, _| {
|
||||||
|
assert_eq!(this.render_count, 1);
|
||||||
|
called_defer.store(true, SeqCst);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cx.after_window_update({
|
||||||
|
let called_after_window_update = called_after_window_update.clone();
|
||||||
|
move |this, cx| {
|
||||||
|
assert_eq!(this.render_count, 2);
|
||||||
|
called_after_window_update.store(true, SeqCst);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert!(!called_defer.load(SeqCst));
|
||||||
|
assert!(!called_after_window_update.load(SeqCst));
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(called_defer.load(SeqCst));
|
||||||
|
assert!(called_after_window_update.load(SeqCst));
|
||||||
|
assert_eq!(view.read(cx).render_count, 3);
|
||||||
|
}
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
fn test_view_handles(cx: &mut MutableAppContext) {
|
fn test_view_handles(cx: &mut MutableAppContext) {
|
||||||
struct View {
|
struct View {
|
||||||
|
|
|
@ -458,26 +458,21 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
|
||||||
&& !pending_update_scheduled.load(SeqCst)
|
&& !pending_update_scheduled.load(SeqCst)
|
||||||
{
|
{
|
||||||
pending_update_scheduled.store(true, SeqCst);
|
pending_update_scheduled.store(true, SeqCst);
|
||||||
cx.spawn({
|
cx.after_window_update({
|
||||||
let pending_update = pending_update.clone();
|
let pending_update = pending_update.clone();
|
||||||
let pending_update_scheduled = pending_update_scheduled.clone();
|
let pending_update_scheduled = pending_update_scheduled.clone();
|
||||||
move |this, mut cx| async move {
|
move |this, cx| {
|
||||||
this.update(&mut cx, |this, cx| {
|
|
||||||
pending_update_scheduled.store(false, SeqCst);
|
pending_update_scheduled.store(false, SeqCst);
|
||||||
this.update_followers(
|
this.update_followers(
|
||||||
proto::update_followers::Variant::UpdateView(
|
proto::update_followers::Variant::UpdateView(proto::UpdateView {
|
||||||
proto::UpdateView {
|
|
||||||
id: item.id() as u64,
|
id: item.id() as u64,
|
||||||
variant: pending_update.borrow_mut().take(),
|
variant: pending_update.borrow_mut().take(),
|
||||||
leader_id: leader_id.map(|id| id.0),
|
leader_id: leader_id.map(|id| id.0),
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.detach();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue