Merge pull request #633 from zed-industries/refresh-windows-panic

Fix edge cases when calling `refresh_windows`
This commit is contained in:
Antonio Scandurra 2022-03-16 14:58:49 +01:00 committed by GitHub
commit e8efaed1b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 91 additions and 28 deletions

View File

@ -1413,11 +1413,10 @@ impl MutableAppContext {
invalidation: None, invalidation: None,
}, },
); );
this.open_platform_window(window_id, window_options);
root_view.update(this, |view, cx| { root_view.update(this, |view, cx| {
view.on_focus(cx); view.on_focus(cx);
cx.notify();
}); });
this.open_platform_window(window_id, window_options);
(window_id, root_view) (window_id, root_view)
}) })
@ -1475,6 +1474,11 @@ impl MutableAppContext {
})); }));
} }
let scene =
presenter
.borrow_mut()
.build_scene(window.size(), window.scale_factor(), false, self);
window.present_scene(scene);
self.presenters_and_platform_windows self.presenters_and_platform_windows
.insert(window_id, (presenter.clone(), window)); .insert(window_id, (presenter.clone(), window));
} }
@ -1666,13 +1670,13 @@ impl MutableAppContext {
} }
} }
for (window_id, invalidation) in invalidations { for (window_id, mut invalidation) in invalidations {
if let Some((presenter, mut window)) = if let Some((presenter, mut window)) =
self.presenters_and_platform_windows.remove(&window_id) self.presenters_and_platform_windows.remove(&window_id)
{ {
{ {
let mut presenter = presenter.borrow_mut(); let mut presenter = presenter.borrow_mut();
presenter.invalidate(invalidation, self); presenter.invalidate(&mut invalidation, self);
let scene = let scene =
presenter.build_scene(window.size(), window.scale_factor(), false, self); presenter.build_scene(window.size(), window.scale_factor(), false, self);
window.present_scene(scene); window.present_scene(scene);
@ -1695,7 +1699,7 @@ impl MutableAppContext {
fn perform_window_refresh(&mut self) { fn perform_window_refresh(&mut self) {
let mut presenters = mem::take(&mut self.presenters_and_platform_windows); let mut presenters = mem::take(&mut self.presenters_and_platform_windows);
for (window_id, (presenter, window)) in &mut presenters { for (window_id, (presenter, window)) in &mut presenters {
let invalidation = self let mut invalidation = self
.cx .cx
.windows .windows
.get_mut(&window_id) .get_mut(&window_id)
@ -1703,7 +1707,10 @@ impl MutableAppContext {
.invalidation .invalidation
.take(); .take();
let mut presenter = presenter.borrow_mut(); let mut presenter = presenter.borrow_mut();
presenter.refresh(invalidation, self); presenter.refresh(
invalidation.as_mut().unwrap_or(&mut Default::default()),
self,
);
let scene = presenter.build_scene(window.size(), window.scale_factor(), true, self); let scene = presenter.build_scene(window.size(), window.scale_factor(), true, self);
window.present_scene(scene); window.present_scene(scene);
} }
@ -5363,4 +5370,65 @@ mod tests {
cx.update(|_| drop(view)); cx.update(|_| drop(view));
condition.await; condition.await;
} }
#[crate::test(self)]
fn test_refresh_windows(cx: &mut MutableAppContext) {
struct View(usize);
impl super::Entity for View {
type Event = ();
}
impl super::View for View {
fn ui_name() -> &'static str {
"test view"
}
fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().named(format!("render count: {}", post_inc(&mut self.0)))
}
}
let (window_id, root_view) = cx.add_window(Default::default(), |_| View(0));
let presenter = cx.presenters_and_platform_windows[&window_id].0.clone();
assert_eq!(
presenter.borrow().rendered_views[&root_view.id()].name(),
Some("render count: 0")
);
let view = cx.add_view(window_id, |cx| {
cx.refresh_windows();
View(0)
});
assert_eq!(
presenter.borrow().rendered_views[&root_view.id()].name(),
Some("render count: 1")
);
assert_eq!(
presenter.borrow().rendered_views[&view.id()].name(),
Some("render count: 0")
);
cx.update(|cx| cx.refresh_windows());
assert_eq!(
presenter.borrow().rendered_views[&root_view.id()].name(),
Some("render count: 2")
);
assert_eq!(
presenter.borrow().rendered_views[&view.id()].name(),
Some("render count: 1")
);
cx.update(|cx| {
cx.refresh_windows();
drop(view);
});
assert_eq!(
presenter.borrow().rendered_views[&root_view.id()].name(),
Some("render count: 3")
);
assert_eq!(presenter.borrow().rendered_views.len(), 1);
}
} }

View File

@ -20,7 +20,7 @@ use std::{
pub struct Presenter { pub struct Presenter {
window_id: usize, window_id: usize,
rendered_views: HashMap<usize, ElementBox>, pub(crate) rendered_views: HashMap<usize, ElementBox>,
parents: HashMap<usize, usize>, parents: HashMap<usize, usize>,
font_cache: Arc<FontCache>, font_cache: Arc<FontCache>,
text_layout_cache: TextLayoutCache, text_layout_cache: TextLayoutCache,
@ -63,39 +63,34 @@ impl Presenter {
path path
} }
pub fn invalidate(&mut self, mut invalidation: WindowInvalidation, cx: &mut MutableAppContext) { pub fn invalidate(
&mut self,
invalidation: &mut WindowInvalidation,
cx: &mut MutableAppContext,
) {
cx.start_frame(); cx.start_frame();
for view_id in invalidation.removed { for view_id in &invalidation.removed {
invalidation.updated.remove(&view_id); invalidation.updated.remove(&view_id);
self.rendered_views.remove(&view_id); self.rendered_views.remove(&view_id);
self.parents.remove(&view_id); self.parents.remove(&view_id);
} }
for view_id in invalidation.updated { for view_id in &invalidation.updated {
self.rendered_views.insert( self.rendered_views.insert(
view_id, *view_id,
cx.render_view(self.window_id, view_id, self.titlebar_height, false) cx.render_view(self.window_id, *view_id, self.titlebar_height, false)
.unwrap(), .unwrap(),
); );
} }
} }
pub fn refresh( pub fn refresh(&mut self, invalidation: &mut WindowInvalidation, cx: &mut MutableAppContext) {
&mut self, self.invalidate(invalidation, cx);
invalidation: Option<WindowInvalidation>,
cx: &mut MutableAppContext,
) {
cx.start_frame();
if let Some(invalidation) = invalidation {
for view_id in invalidation.removed {
self.rendered_views.remove(&view_id);
self.parents.remove(&view_id);
}
}
for (view_id, view) in &mut self.rendered_views { for (view_id, view) in &mut self.rendered_views {
*view = cx if !invalidation.updated.contains(view_id) {
.render_view(self.window_id, *view_id, self.titlebar_height, true) *view = cx
.unwrap(); .render_view(self.window_id, *view_id, self.titlebar_height, true)
.unwrap();
}
} }
} }