1#![allow(clippy::await_holding_refcell_ref, clippy::type_complexity)]
2pub(crate) mod helpers;
3
4pub mod component;
5
6#[allow(deprecated)]
7pub use component::Component;
8
9mod cache;
10
11use crate::core::Element;
12use crate::core::layout::{self, Layout};
13use crate::core::mouse;
14use crate::core::overlay;
15use crate::core::renderer;
16use crate::core::widget::tree::{self, Tree};
17use crate::core::widget::{self, Widget};
18use crate::core::{
19 self, Clipboard, Event, Length, Rectangle, Shell, Size, Vector,
20};
21use crate::runtime::overlay::Nested;
22
23use ouroboros::self_referencing;
24use rustc_hash::FxHasher;
25use std::cell::RefCell;
26use std::hash::{Hash, Hasher as H};
27use std::rc::Rc;
28
29#[cfg(feature = "lazy")]
31#[allow(missing_debug_implementations)]
32pub struct Lazy<'a, Message, Theme, Renderer, Dependency, View> {
33 dependency: Dependency,
34 view: Box<dyn Fn(&Dependency) -> View + 'a>,
35 element: RefCell<
36 Option<Rc<RefCell<Option<Element<'static, Message, Theme, Renderer>>>>>,
37 >,
38}
39
40impl<'a, Message, Theme, Renderer, Dependency, View>
41 Lazy<'a, Message, Theme, Renderer, Dependency, View>
42where
43 Dependency: Hash + 'a,
44 View: Into<Element<'static, Message, Theme, Renderer>>,
45{
46 pub fn new(
49 dependency: Dependency,
50 view: impl Fn(&Dependency) -> View + 'a,
51 ) -> Self {
52 Self {
53 dependency,
54 view: Box::new(view),
55 element: RefCell::new(None),
56 }
57 }
58
59 fn with_element<T>(
60 &self,
61 f: impl FnOnce(&Element<'_, Message, Theme, Renderer>) -> T,
62 ) -> T {
63 f(self
64 .element
65 .borrow()
66 .as_ref()
67 .unwrap()
68 .borrow()
69 .as_ref()
70 .unwrap())
71 }
72
73 fn with_element_mut<T>(
74 &self,
75 f: impl FnOnce(&mut Element<'_, Message, Theme, Renderer>) -> T,
76 ) -> T {
77 f(self
78 .element
79 .borrow()
80 .as_ref()
81 .unwrap()
82 .borrow_mut()
83 .as_mut()
84 .unwrap())
85 }
86}
87
88struct Internal<Message, Theme, Renderer> {
89 element: Rc<RefCell<Option<Element<'static, Message, Theme, Renderer>>>>,
90 hash: u64,
91}
92
93impl<'a, Message, Theme, Renderer, Dependency, View>
94 Widget<Message, Theme, Renderer>
95 for Lazy<'a, Message, Theme, Renderer, Dependency, View>
96where
97 View: Into<Element<'static, Message, Theme, Renderer>> + 'static,
98 Dependency: Hash + 'a,
99 Message: 'static,
100 Theme: 'static,
101 Renderer: core::Renderer + 'static,
102{
103 fn tag(&self) -> tree::Tag {
104 struct Tag<T>(T);
105 tree::Tag::of::<Tag<View>>()
106 }
107
108 fn state(&self) -> tree::State {
109 let hash = {
110 let mut hasher = FxHasher::default();
111 self.dependency.hash(&mut hasher);
112
113 hasher.finish()
114 };
115
116 let element =
117 Rc::new(RefCell::new(Some((self.view)(&self.dependency).into())));
118
119 (*self.element.borrow_mut()) = Some(element.clone());
120
121 tree::State::new(Internal { element, hash })
122 }
123
124 fn children(&self) -> Vec<Tree> {
125 self.with_element(|element| vec![Tree::new(element.as_widget())])
126 }
127
128 fn diff(&self, tree: &mut Tree) {
129 let current = tree
130 .state
131 .downcast_mut::<Internal<Message, Theme, Renderer>>();
132
133 let new_hash = {
134 let mut hasher = FxHasher::default();
135 self.dependency.hash(&mut hasher);
136
137 hasher.finish()
138 };
139
140 if current.hash != new_hash {
141 current.hash = new_hash;
142
143 let element = (self.view)(&self.dependency).into();
144 current.element = Rc::new(RefCell::new(Some(element)));
145
146 (*self.element.borrow_mut()) = Some(current.element.clone());
147 self.with_element(|element| {
148 tree.diff_children(std::slice::from_ref(&element.as_widget()));
149 });
150 } else {
151 (*self.element.borrow_mut()) = Some(current.element.clone());
152 }
153 }
154
155 fn size(&self) -> Size<Length> {
156 self.with_element(|element| element.as_widget().size())
157 }
158
159 fn size_hint(&self) -> Size<Length> {
160 Size {
161 width: Length::Shrink,
162 height: Length::Shrink,
163 }
164 }
165
166 fn layout(
167 &mut self,
168 tree: &mut Tree,
169 renderer: &Renderer,
170 limits: &layout::Limits,
171 ) -> layout::Node {
172 self.with_element_mut(|element| {
173 element.as_widget_mut().layout(
174 &mut tree.children[0],
175 renderer,
176 limits,
177 )
178 })
179 }
180
181 fn operate(
182 &mut self,
183 tree: &mut Tree,
184 layout: Layout<'_>,
185 renderer: &Renderer,
186 operation: &mut dyn widget::Operation,
187 ) {
188 self.with_element_mut(|element| {
189 element.as_widget_mut().operate(
190 &mut tree.children[0],
191 layout,
192 renderer,
193 operation,
194 );
195 });
196 }
197
198 fn update(
199 &mut self,
200 tree: &mut Tree,
201 event: &Event,
202 layout: Layout<'_>,
203 cursor: mouse::Cursor,
204 renderer: &Renderer,
205 clipboard: &mut dyn Clipboard,
206 shell: &mut Shell<'_, Message>,
207 viewport: &Rectangle,
208 ) {
209 self.with_element_mut(|element| {
210 element.as_widget_mut().update(
211 &mut tree.children[0],
212 event,
213 layout,
214 cursor,
215 renderer,
216 clipboard,
217 shell,
218 viewport,
219 );
220 });
221 }
222
223 fn mouse_interaction(
224 &self,
225 tree: &Tree,
226 layout: Layout<'_>,
227 cursor: mouse::Cursor,
228 viewport: &Rectangle,
229 renderer: &Renderer,
230 ) -> mouse::Interaction {
231 self.with_element(|element| {
232 element.as_widget().mouse_interaction(
233 &tree.children[0],
234 layout,
235 cursor,
236 viewport,
237 renderer,
238 )
239 })
240 }
241
242 fn draw(
243 &self,
244 tree: &Tree,
245 renderer: &mut Renderer,
246 theme: &Theme,
247 style: &renderer::Style,
248 layout: Layout<'_>,
249 cursor: mouse::Cursor,
250 viewport: &Rectangle,
251 ) {
252 self.with_element(|element| {
253 element.as_widget().draw(
254 &tree.children[0],
255 renderer,
256 theme,
257 style,
258 layout,
259 cursor,
260 viewport,
261 );
262 });
263 }
264
265 fn overlay<'b>(
266 &'b mut self,
267 tree: &'b mut Tree,
268 layout: Layout<'b>,
269 renderer: &Renderer,
270 viewport: &Rectangle,
271 translation: Vector,
272 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
273 let overlay = InnerBuilder {
274 cell: self.element.borrow().as_ref().unwrap().clone(),
275 element: self
276 .element
277 .borrow()
278 .as_ref()
279 .unwrap()
280 .borrow_mut()
281 .take()
282 .unwrap(),
283 tree: &mut tree.children[0],
284 layout,
285 overlay_builder: |element, tree, layout| {
286 element
287 .as_widget_mut()
288 .overlay(tree, *layout, renderer, viewport, translation)
289 .map(|overlay| RefCell::new(Nested::new(overlay)))
290 },
291 }
292 .build();
293
294 #[allow(clippy::redundant_closure_for_method_calls)]
295 if overlay.with_overlay(|overlay| overlay.is_some()) {
296 Some(overlay::Element::new(Box::new(Overlay(Some(overlay)))))
297 } else {
298 let heads = overlay.into_heads();
299
300 *self.element.borrow().as_ref().unwrap().borrow_mut() =
304 Some(heads.element);
305
306 None
307 }
308 }
309}
310
311#[self_referencing]
312struct Inner<'a, Message: 'a, Theme: 'a, Renderer: 'a> {
313 cell: Rc<RefCell<Option<Element<'static, Message, Theme, Renderer>>>>,
314 element: Element<'static, Message, Theme, Renderer>,
315 tree: &'a mut Tree,
316 layout: Layout<'a>,
317
318 #[borrows(mut element, mut tree, layout)]
319 #[not_covariant]
320 overlay: Option<RefCell<Nested<'this, Message, Theme, Renderer>>>,
321}
322
323struct Overlay<'a, Message, Theme, Renderer>(
324 Option<Inner<'a, Message, Theme, Renderer>>,
325);
326
327impl<Message, Theme, Renderer> Drop for Overlay<'_, Message, Theme, Renderer> {
328 fn drop(&mut self) {
329 let heads = self.0.take().unwrap().into_heads();
330 (*heads.cell.borrow_mut()) = Some(heads.element);
331 }
332}
333
334impl<Message, Theme, Renderer> Overlay<'_, Message, Theme, Renderer> {
335 fn with_overlay_maybe<T>(
336 &self,
337 f: impl FnOnce(&mut Nested<'_, Message, Theme, Renderer>) -> T,
338 ) -> Option<T> {
339 self.0.as_ref().unwrap().with_overlay(|overlay| {
340 overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut()))
341 })
342 }
343
344 fn with_overlay_mut_maybe<T>(
345 &mut self,
346 f: impl FnOnce(&mut Nested<'_, Message, Theme, Renderer>) -> T,
347 ) -> Option<T> {
348 self.0.as_mut().unwrap().with_overlay_mut(|overlay| {
349 overlay.as_mut().map(|nested| (f)(nested.get_mut()))
350 })
351 }
352}
353
354impl<Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
355 for Overlay<'_, Message, Theme, Renderer>
356where
357 Renderer: core::Renderer,
358{
359 fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
360 self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
361 .unwrap_or_default()
362 }
363
364 fn draw(
365 &self,
366 renderer: &mut Renderer,
367 theme: &Theme,
368 style: &renderer::Style,
369 layout: Layout<'_>,
370 cursor: mouse::Cursor,
371 ) {
372 let _ = self.with_overlay_maybe(|overlay| {
373 overlay.draw(renderer, theme, style, layout, cursor);
374 });
375 }
376
377 fn mouse_interaction(
378 &self,
379 layout: Layout<'_>,
380 cursor: mouse::Cursor,
381 renderer: &Renderer,
382 ) -> mouse::Interaction {
383 self.with_overlay_maybe(|overlay| {
384 overlay.mouse_interaction(layout, cursor, renderer)
385 })
386 .unwrap_or_default()
387 }
388
389 fn update(
390 &mut self,
391 event: &Event,
392 layout: Layout<'_>,
393 cursor: mouse::Cursor,
394 renderer: &Renderer,
395 clipboard: &mut dyn Clipboard,
396 shell: &mut Shell<'_, Message>,
397 ) {
398 let _ = self.with_overlay_mut_maybe(|overlay| {
399 overlay.update(event, layout, cursor, renderer, clipboard, shell);
400 });
401 }
402}
403
404impl<'a, Message, Theme, Renderer, Dependency, View>
405 From<Lazy<'a, Message, Theme, Renderer, Dependency, View>>
406 for Element<'a, Message, Theme, Renderer>
407where
408 View: Into<Element<'static, Message, Theme, Renderer>> + 'static,
409 Renderer: core::Renderer + 'static,
410 Message: 'static,
411 Theme: 'static,
412 Dependency: Hash + 'a,
413{
414 fn from(
415 lazy: Lazy<'a, Message, Theme, Renderer, Dependency, View>,
416 ) -> Self {
417 Self::new(lazy)
418 }
419}