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