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