diff --git a/crates/gpui/src/elements/img.rs b/crates/gpui/src/elements/img.rs index 19eae28982d97bb487dfb3ecb0360b27dcb045e4..961429b826c56c99f20428ea2ffd2bd169bb7049 100644 --- a/crates/gpui/src/elements/img.rs +++ b/crates/gpui/src/elements/img.rs @@ -353,15 +353,11 @@ impl Element for Img { } } } else { - let parent_view_id = window.parent_view_id(); + let current_view = window.current_view(); let task = window.spawn(cx, |mut cx| async move { cx.background_executor().timer(LOADING_DELAY).await; - cx.update(move |window, cx| { - if let Some(parent_view_id) = parent_view_id { - cx.notify(parent_view_id); - } else { - window.refresh(); - } + cx.update(move |_, cx| { + cx.notify(current_view); }) .ok(); }); diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs index 6056988e32b7e015701c864ddf6eb283af722179..db6789e88c9b291d71a359bf2d6a2ffc08aaed11 100644 --- a/crates/gpui/src/key_dispatch.rs +++ b/crates/gpui/src/key_dispatch.rs @@ -219,10 +219,6 @@ impl DispatchTree { self.focusable_node_ids.insert(focus_id, node_id); } - pub fn parent_view_id(&self) -> Option { - self.view_stack.last().copied() - } - pub fn set_view_id(&mut self, view_id: EntityId) { if self.view_stack.last().copied() != Some(view_id) { let node_id = *self.node_stack.last().unwrap(); diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index 9997e0713bb7a875db6d5b33e15b0b6bd227ede0..86d6def7cc77c719d758c9e2eb2fadb73443f382 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -155,9 +155,11 @@ impl Element for AnyView { let layout_id = window.request_layout(root_style, None, cx); (layout_id, None) } else { - let mut element = (self.render)(self, window, cx); - let layout_id = element.request_layout(window, cx); - (layout_id, Some(element)) + window.with_rendered_view(self.entity_id(), |window| { + let mut element = (self.render)(self, window, cx); + let layout_id = element.request_layout(window, cx); + (layout_id, Some(element)) + }) } } @@ -197,12 +199,16 @@ impl Element for AnyView { let refreshing = mem::replace(&mut window.refreshing, true); let prepaint_start = window.prepaint_index(); - let (mut element, accessed_entities) = cx.detect_accessed_entities(|cx| { - let mut element = (self.render)(self, window, cx); - element.layout_as_root(bounds.size.into(), window, cx); - element.prepaint_at(bounds.origin, window, cx); - element - }); + let (mut element, accessed_entities) = + window.with_rendered_view(self.entity_id(), |window| { + cx.detect_accessed_entities(|cx| { + let mut element = (self.render)(self, window, cx); + element.layout_as_root(bounds.size.into(), window, cx); + element.prepaint_at(bounds.origin, window, cx); + element + }) + }); + let prepaint_end = window.prepaint_index(); window.refreshing = refreshing; @@ -223,7 +229,10 @@ impl Element for AnyView { ) } else { let mut element = element.take().unwrap(); - element.prepaint(window, cx); + window.with_rendered_view(self.entity_id(), |window| { + element.prepaint(window, cx); + }); + Some(element) } } @@ -247,7 +256,9 @@ impl Element for AnyView { if let Some(element) = element { let refreshing = mem::replace(&mut window.refreshing, true); - element.paint(window, cx); + window.with_rendered_view(self.entity_id(), |window| { + element.paint(window, cx); + }); window.refreshing = refreshing; } else { window.reuse_paint(element_state.paint_range.clone()); diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index dcbe302ac9a5eb43b139f1de27d2187085ba28ea..e522ea85957b9f64193c937e65d5160f5ad39d31 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -612,6 +612,7 @@ pub struct Window { pub(crate) root: Option, pub(crate) element_id_stack: SmallVec<[ElementId; 32]>, pub(crate) text_style_stack: Vec, + pub(crate) rendered_entity_stack: Vec, pub(crate) element_offset_stack: Vec>, pub(crate) element_opacity: Option, pub(crate) content_mask_stack: Vec>, @@ -895,6 +896,7 @@ impl Window { root: None, element_id_stack: SmallVec::default(), text_style_stack: Vec::new(), + rendered_entity_stack: Vec::new(), element_offset_stack: Vec::new(), content_mask_stack: Vec::new(), element_opacity: None, @@ -971,27 +973,6 @@ impl ContentMask { } impl Window { - /// Indicate that a view has changed, which will invoke any observers and also mark the window as dirty. - /// If this view or any of its ancestors are *cached*, notifying it will cause it or its ancestors to be redrawn. - /// Note that this method will always cause a redraw, the entire window is refreshed if view_id is None. - pub(crate) fn notify( - &mut self, - notify_effect: bool, - entity_id: Option, - cx: &mut App, - ) { - let Some(view_id) = entity_id else { - self.refresh(); - return; - }; - - self.mark_view_dirty(view_id); - - if notify_effect { - self.invalidator.invalidate_view(view_id, cx); - } - } - fn mark_view_dirty(&mut self, view_id: EntityId) { // Mark ancestor views as dirty. If already in the `dirty_views` set, then all its ancestors // should already be dirty. @@ -1300,8 +1281,8 @@ impl Window { /// /// If called from within a view, it will notify that view on the next frame. Otherwise, it will refresh the entire window. pub fn request_animation_frame(&self) { - let parent_id = self.parent_view_id(); - self.on_next_frame(move |window, cx| window.notify(true, parent_id, cx)); + let entity = self.current_view(); + self.on_next_frame(move |_, cx| cx.notify(entity)); } /// Spawn the future returned by the given closure on the application thread pool. @@ -1534,6 +1515,7 @@ impl Window { pub fn draw(&mut self, cx: &mut App) { self.invalidate_entities(); cx.entities.clear_accessed(); + debug_assert!(self.rendered_entity_stack.is_empty()); self.invalidator.set_dirty(false); self.requested_autoscroll = None; @@ -1596,6 +1578,7 @@ impl Window { .retain(&(), |listener| listener(&event, self, cx)); } + debug_assert!(self.rendered_entity_stack.is_empty()); self.record_entities_accessed(cx); self.reset_cursor_style(cx); self.refreshing = false; @@ -2074,14 +2057,14 @@ impl Window { let (task, is_first) = cx.fetch_asset::(source); task.clone().now_or_never().or_else(|| { if is_first { - let parent_id = self.parent_view_id(); + let entity = self.current_view(); self.spawn(cx, { let task = task.clone(); |mut cx| async move { task.await; - cx.on_next_frame(move |window, cx| { - window.notify(true, parent_id, cx); + cx.on_next_frame(move |_, cx| { + cx.notify(entity); }); } }) @@ -2690,12 +2673,12 @@ impl Window { Ok(()) } - #[must_use] /// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which /// layout is being requested, along with the layout ids of any children. This method is called during /// calls to the [`Element::request_layout`] trait method and enables any element to participate in layout. /// /// This method should only be called as part of the request_layout or prepaint phase of element drawing. + #[must_use] pub fn request_layout( &mut self, style: Style, @@ -2826,9 +2809,21 @@ impl Window { self.next_frame.dispatch_tree.set_view_id(view_id); } - /// Get the last view id for the current element - pub fn parent_view_id(&self) -> Option { - self.next_frame.dispatch_tree.parent_view_id() + /// Get the entity ID for the currently rendering view + pub fn current_view(&self) -> EntityId { + self.invalidator.debug_assert_paint_or_prepaint(); + self.rendered_entity_stack.last().copied().unwrap() + } + + pub(crate) fn with_rendered_view( + &mut self, + id: EntityId, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + self.rendered_entity_stack.push(id); + let result = f(self); + self.rendered_entity_stack.pop(); + result } /// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the