iced_widget/
lazy.rs

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