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,
},
);
this.open_platform_window(window_id, window_options);
root_view.update(this, |view, cx| {
view.on_focus(cx);
cx.notify();
});
this.open_platform_window(window_id, window_options);
(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
.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)) =
self.presenters_and_platform_windows.remove(&window_id)
{
{
let mut presenter = presenter.borrow_mut();
presenter.invalidate(invalidation, self);
presenter.invalidate(&mut invalidation, self);
let scene =
presenter.build_scene(window.size(), window.scale_factor(), false, self);
window.present_scene(scene);
@ -1695,7 +1699,7 @@ impl MutableAppContext {
fn perform_window_refresh(&mut self) {
let mut presenters = mem::take(&mut self.presenters_and_platform_windows);
for (window_id, (presenter, window)) in &mut presenters {
let invalidation = self
let mut invalidation = self
.cx
.windows
.get_mut(&window_id)
@ -1703,7 +1707,10 @@ impl MutableAppContext {
.invalidation
.take();
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);
window.present_scene(scene);
}
@ -5363,4 +5370,65 @@ mod tests {
cx.update(|_| drop(view));
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 {
window_id: usize,
rendered_views: HashMap<usize, ElementBox>,
pub(crate) rendered_views: HashMap<usize, ElementBox>,
parents: HashMap<usize, usize>,
font_cache: Arc<FontCache>,
text_layout_cache: TextLayoutCache,
@ -63,39 +63,34 @@ impl Presenter {
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();
for view_id in invalidation.removed {
for view_id in &invalidation.removed {
invalidation.updated.remove(&view_id);
self.rendered_views.remove(&view_id);
self.parents.remove(&view_id);
}
for view_id in invalidation.updated {
for view_id in &invalidation.updated {
self.rendered_views.insert(
view_id,
cx.render_view(self.window_id, view_id, self.titlebar_height, false)
*view_id,
cx.render_view(self.window_id, *view_id, self.titlebar_height, false)
.unwrap(),
);
}
}
pub fn refresh(
&mut self,
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);
}
}
pub fn refresh(&mut self, invalidation: &mut WindowInvalidation, cx: &mut MutableAppContext) {
self.invalidate(invalidation, cx);
for (view_id, view) in &mut self.rendered_views {
*view = cx
.render_view(self.window_id, *view_id, self.titlebar_height, true)
.unwrap();
if !invalidation.updated.contains(view_id) {
*view = cx
.render_view(self.window_id, *view_id, self.titlebar_height, true)
.unwrap();
}
}
}