diff --git a/crates/gpui/examples/popover.rs b/crates/gpui/examples/popover.rs index bd112b0e69a62c1303e9d90945e24cfb3f659b82..9d5f84a1f43462e6e49ec5b0984dbd7b1c50230a 100644 --- a/crates/gpui/examples/popover.rs +++ b/crates/gpui/examples/popover.rs @@ -56,21 +56,23 @@ impl HelloWorld { })) .when(self.secondary_open, |this| { this.child( - // GPUI can't support deferred here yet, - // it was inside another deferred element. - anchored() - .anchor(Corner::TopLeft) - .snap_to_window_with_margin(px(8.)) - .child( - popover() - .child("This is second level Popover") - .bg(gpui::white()) - .border_color(gpui::blue()) - .on_mouse_down_out(cx.listener(|this, _, _, cx| { - this.secondary_open = false; - cx.notify(); - })), - ), + // Now GPUI supports nested deferred! + deferred( + anchored() + .anchor(Corner::TopLeft) + .snap_to_window_with_margin(px(8.)) + .child( + popover() + .child("This is second level Popover with nested deferred!") + .bg(gpui::white()) + .border_color(gpui::blue()) + .on_mouse_down_out(cx.listener(|this, _, _, cx| { + this.secondary_open = false; + cx.notify(); + })), + ), + ) + .priority(2), ) }) } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 5d9ea28fcf8c7998dfada055157dc0766c33628a..8be228c38ccef26115bdfceff69cb0502c564fd7 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -2344,10 +2344,7 @@ impl Window { #[cfg(any(feature = "inspector", debug_assertions))] let inspector_element = self.prepaint_inspector(_inspector_width, cx); - let mut sorted_deferred_draws = - (0..self.next_frame.deferred_draws.len()).collect::>(); - sorted_deferred_draws.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority); - self.prepaint_deferred_draws(&sorted_deferred_draws, cx); + self.prepaint_deferred_draws(cx); let mut prompt_element = None; let mut active_drag_element = None; @@ -2376,7 +2373,7 @@ impl Window { #[cfg(any(feature = "inspector", debug_assertions))] self.paint_inspector(inspector_element, cx); - self.paint_deferred_draws(&sorted_deferred_draws, cx); + self.paint_deferred_draws(cx); if let Some(mut prompt_element) = prompt_element { prompt_element.paint(self, cx); @@ -2459,25 +2456,40 @@ impl Window { None } - fn prepaint_deferred_draws(&mut self, deferred_draw_indices: &[usize], cx: &mut App) { + fn prepaint_deferred_draws(&mut self, cx: &mut App) { assert_eq!(self.element_id_stack.len(), 0); - let mut deferred_draws = mem::take(&mut self.next_frame.deferred_draws); - for deferred_draw_ix in deferred_draw_indices { - let deferred_draw = &mut deferred_draws[*deferred_draw_ix]; - self.element_id_stack - .clone_from(&deferred_draw.element_id_stack); - self.text_style_stack - .clone_from(&deferred_draw.text_style_stack); - self.next_frame - .dispatch_tree - .set_active_node(deferred_draw.parent_node); + let mut completed_draws = Vec::new(); + + // Process deferred draws in multiple rounds to support nesting. + // Each round processes all current deferred draws, which may produce new ones. + let mut depth = 0; + loop { + // Limit maximum nesting depth to prevent infinite loops. + assert!(depth < 10, "Exceeded maximum (10) deferred depth"); + depth += 1; + let deferred_count = self.next_frame.deferred_draws.len(); + if deferred_count == 0 { + break; + } - let prepaint_start = self.prepaint_index(); - let content_mask = deferred_draw.content_mask.clone(); - if let Some(element) = deferred_draw.element.as_mut() { - self.with_rendered_view(deferred_draw.current_view, |window| { - window.with_content_mask(content_mask, |window| { + // Sort by priority for this round + let traversal_order = self.deferred_draw_traversal_order(); + let mut deferred_draws = mem::take(&mut self.next_frame.deferred_draws); + + for deferred_draw_ix in traversal_order { + let deferred_draw = &mut deferred_draws[deferred_draw_ix]; + self.element_id_stack + .clone_from(&deferred_draw.element_id_stack); + self.text_style_stack + .clone_from(&deferred_draw.text_style_stack); + self.next_frame + .dispatch_tree + .set_active_node(deferred_draw.parent_node); + + let prepaint_start = self.prepaint_index(); + if let Some(element) = deferred_draw.element.as_mut() { + self.with_rendered_view(deferred_draw.current_view, |window| { window.with_rem_size(Some(deferred_draw.rem_size), |window| { window.with_absolute_element_offset( deferred_draw.absolute_offset, @@ -2486,30 +2498,38 @@ impl Window { }, ); }); - }); - }) - } else { - self.reuse_prepaint(deferred_draw.prepaint_range.clone()); + }) + } else { + self.reuse_prepaint(deferred_draw.prepaint_range.clone()); + } + let prepaint_end = self.prepaint_index(); + deferred_draw.prepaint_range = prepaint_start..prepaint_end; } - let prepaint_end = self.prepaint_index(); - deferred_draw.prepaint_range = prepaint_start..prepaint_end; + + // Save completed draws and continue with newly added ones + completed_draws.append(&mut deferred_draws); + + self.element_id_stack.clear(); + self.text_style_stack.clear(); } - assert_eq!( - self.next_frame.deferred_draws.len(), - 0, - "cannot call defer_draw during deferred drawing" - ); - self.next_frame.deferred_draws = deferred_draws; - self.element_id_stack.clear(); - self.text_style_stack.clear(); + + // Restore all completed draws + self.next_frame.deferred_draws = completed_draws; } - fn paint_deferred_draws(&mut self, deferred_draw_indices: &[usize], cx: &mut App) { + fn paint_deferred_draws(&mut self, cx: &mut App) { assert_eq!(self.element_id_stack.len(), 0); + // Paint all deferred draws in priority order. + // Since prepaint has already processed nested deferreds, we just paint them all. + if self.next_frame.deferred_draws.len() == 0 { + return; + } + + let traversal_order = self.deferred_draw_traversal_order(); let mut deferred_draws = mem::take(&mut self.next_frame.deferred_draws); - for deferred_draw_ix in deferred_draw_indices { - let mut deferred_draw = &mut deferred_draws[*deferred_draw_ix]; + for deferred_draw_ix in traversal_order { + let mut deferred_draw = &mut deferred_draws[deferred_draw_ix]; self.element_id_stack .clone_from(&deferred_draw.element_id_stack); self.next_frame @@ -2536,6 +2556,13 @@ impl Window { self.element_id_stack.clear(); } + fn deferred_draw_traversal_order(&mut self) -> SmallVec<[usize; 8]> { + let deferred_count = self.next_frame.deferred_draws.len(); + let mut sorted_indices = (0..deferred_count).collect::>(); + sorted_indices.sort_by_key(|ix| self.next_frame.deferred_draws[*ix].priority); + sorted_indices + } + pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex { PrepaintStateIndex { hitboxes_index: self.next_frame.hitboxes.len(),