1use crate::{
2 AnyElement, App, Bounds, Element, GlobalElementId, IntoElement, LayoutId, Pixels, Window,
3};
4
5pub fn deferred(child: impl IntoElement) -> Deferred {
7 Deferred {
8 child: Some(child.into_any_element()),
9 priority: 0,
10 }
11}
12
13pub struct Deferred {
16 child: Option<AnyElement>,
17 priority: usize,
18}
19
20impl Deferred {
21 pub fn with_priority(mut self, priority: usize) -> Self {
25 self.priority = priority;
26 self
27 }
28}
29
30impl Element for Deferred {
31 type RequestLayoutState = ();
32 type PrepaintState = ();
33
34 fn id(&self) -> Option<crate::ElementId> {
35 None
36 }
37
38 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
39 None
40 }
41
42 fn request_layout(
43 &mut self,
44 _id: Option<&GlobalElementId>,
45 window: &mut Window,
46 cx: &mut App,
47 ) -> (LayoutId, ()) {
48 let layout_id = self.child.as_mut().unwrap().request_layout(window, cx);
49 (layout_id, ())
50 }
51
52 fn prepaint(
53 &mut self,
54 _id: Option<&GlobalElementId>,
55 _bounds: Bounds<Pixels>,
56 _request_layout: &mut Self::RequestLayoutState,
57 window: &mut Window,
58 _cx: &mut App,
59 ) {
60 let child = self.child.take().unwrap();
61 let element_offset = window.element_offset();
62 window.defer_draw(child, element_offset, self.priority, None)
63 }
64
65 fn paint(
66 &mut self,
67 _id: Option<&GlobalElementId>,
68 _bounds: Bounds<Pixels>,
69 _request_layout: &mut Self::RequestLayoutState,
70 _prepaint: &mut Self::PrepaintState,
71 _window: &mut Window,
72 _cx: &mut App,
73 ) {
74 }
75}
76
77impl IntoElement for Deferred {
78 type Element = Self;
79
80 fn into_element(self) -> Self::Element {
81 self
82 }
83}
84
85impl Deferred {
86 pub fn priority(mut self, priority: usize) -> Self {
89 self.priority = priority;
90 self
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use crate::{
97 Context, Entity, StyleRefinement, TestAppContext, Window, anchored, deferred, div, point,
98 prelude::*, px, size,
99 };
100
101 struct PanelView;
105
106 impl Render for PanelView {
107 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
108 div().key_context("Panel").size_full().child(
109 deferred(
110 anchored().position(point(px(10.), px(10.))).child(
111 div().key_context("Popover").w(px(200.)).h(px(200.)).child(
112 deferred(
113 anchored().position(point(px(30.), px(30.))).child(
114 div()
115 .key_context("NestedMenu")
116 .debug_selector(|| "NESTED_MENU".into())
117 .w(px(50.))
118 .h(px(50.)),
119 ),
120 )
121 .with_priority(2),
122 ),
123 ),
124 )
125 .with_priority(1),
126 )
127 }
128 }
129
130 struct RootView {
131 panel: Entity<PanelView>,
132 }
133
134 impl Render for RootView {
135 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
136 div().key_context("Root").size_full().child(
137 self.panel
138 .clone()
139 .cached(StyleRefinement::default().size_full()),
140 )
141 }
142 }
143
144 #[gpui::test]
152 fn test_nested_deferred_draws_with_reused_views(cx: &mut TestAppContext) {
153 let window = cx.open_window(size(px(800.), px(600.)), |_, cx| {
154 let panel = cx.new(|_| PanelView);
155 RootView { panel }
156 });
157 cx.run_until_parked();
158
159 let menu_bounds = window
160 .update(cx, |_, window, _| {
161 window
162 .frame_state
163 .rendered_frame
164 .debug_bounds
165 .get("NESTED_MENU")
166 .copied()
167 })
168 .unwrap()
169 .expect("NESTED_MENU debug bounds not found");
170 assert_eq!(menu_bounds.size, size(px(50.), px(50.)));
171
172 window.update(cx, |_, _, cx| cx.notify()).unwrap();
176 cx.run_until_parked();
177
178 window.update(cx, |_, _, cx| cx.notify()).unwrap();
181 cx.run_until_parked();
182
183 window
185 .update(cx, |root, _, cx| {
186 root.panel.update(cx, |_, cx| cx.notify());
187 })
188 .unwrap();
189 cx.run_until_parked();
190
191 window
192 .update(cx, |_, window, _| {
193 assert_eq!(window.frame_state.rendered_frame.deferred_draws.len(), 2);
194 assert!(
195 window
196 .frame_state
197 .rendered_frame
198 .debug_bounds
199 .contains_key("NESTED_MENU")
200 );
201 })
202 .unwrap();
203 }
204}