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, 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<'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}