Skip to main content

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, 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        shell: &mut Shell<'_, Message>,
187        viewport: &Rectangle,
188    ) {
189        self.with_element_mut(|element| {
190            element.as_widget_mut().update(
191                &mut tree.children[0],
192                event,
193                layout,
194                cursor,
195                renderer,
196                shell,
197                viewport,
198            );
199        });
200    }
201
202    fn mouse_interaction(
203        &self,
204        tree: &Tree,
205        layout: Layout<'_>,
206        cursor: mouse::Cursor,
207        viewport: &Rectangle,
208        renderer: &Renderer,
209    ) -> mouse::Interaction {
210        self.with_element(|element| {
211            element.as_widget().mouse_interaction(
212                &tree.children[0],
213                layout,
214                cursor,
215                viewport,
216                renderer,
217            )
218        })
219    }
220
221    fn draw(
222        &self,
223        tree: &Tree,
224        renderer: &mut Renderer,
225        theme: &Theme,
226        style: &renderer::Style,
227        layout: Layout<'_>,
228        cursor: mouse::Cursor,
229        viewport: &Rectangle,
230    ) {
231        self.with_element(|element| {
232            element.as_widget().draw(
233                &tree.children[0],
234                renderer,
235                theme,
236                style,
237                layout,
238                cursor,
239                viewport,
240            );
241        });
242    }
243
244    fn overlay<'b>(
245        &'b mut self,
246        tree: &'b mut Tree,
247        layout: Layout<'b>,
248        renderer: &Renderer,
249        viewport: &Rectangle,
250        translation: Vector,
251    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
252        let overlay = InnerBuilder {
253            cell: self.element.borrow().as_ref().unwrap().clone(),
254            element: self
255                .element
256                .borrow()
257                .as_ref()
258                .unwrap()
259                .borrow_mut()
260                .take()
261                .unwrap(),
262            tree: &mut tree.children[0],
263            layout,
264            overlay_builder: |element, tree, layout| {
265                element
266                    .as_widget_mut()
267                    .overlay(tree, *layout, renderer, viewport, translation)
268                    .map(|overlay| RefCell::new(overlay::Nested::new(overlay)))
269            },
270        }
271        .build();
272
273        #[allow(clippy::redundant_closure_for_method_calls)]
274        if overlay.with_overlay(|overlay| overlay.is_some()) {
275            Some(overlay::Element::new(Box::new(Overlay(Some(overlay)))))
276        } else {
277            let heads = overlay.into_heads();
278
279            // - You may not like it, but this is what peak performance looks like
280            // - TODO: Get rid of ouroboros, for good
281            // - What?!
282            *self.element.borrow().as_ref().unwrap().borrow_mut() = Some(heads.element);
283
284            None
285        }
286    }
287}
288
289#[self_referencing]
290struct Inner<'a, Message: 'a, Theme: 'a, Renderer: 'a> {
291    cell: Rc<RefCell<Option<Element<'static, Message, Theme, Renderer>>>>,
292    element: Element<'static, Message, Theme, Renderer>,
293    tree: &'a mut Tree,
294    layout: Layout<'a>,
295
296    #[borrows(mut element, mut tree, layout)]
297    #[not_covariant]
298    overlay: Option<RefCell<overlay::Nested<'this, Message, Theme, Renderer>>>,
299}
300
301struct Overlay<'a, Message, Theme, Renderer>(Option<Inner<'a, Message, Theme, Renderer>>);
302
303impl<Message, Theme, Renderer> Drop for Overlay<'_, Message, Theme, Renderer> {
304    fn drop(&mut self) {
305        let heads = self.0.take().unwrap().into_heads();
306        (*heads.cell.borrow_mut()) = Some(heads.element);
307    }
308}
309
310impl<Message, Theme, Renderer> Overlay<'_, Message, Theme, Renderer> {
311    fn with_overlay_maybe<T>(
312        &self,
313        f: impl FnOnce(&mut overlay::Nested<'_, Message, Theme, Renderer>) -> T,
314    ) -> Option<T> {
315        self.0
316            .as_ref()
317            .unwrap()
318            .with_overlay(|overlay| overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())))
319    }
320
321    fn with_overlay_mut_maybe<T>(
322        &mut self,
323        f: impl FnOnce(&mut overlay::Nested<'_, Message, Theme, Renderer>) -> T,
324    ) -> Option<T> {
325        self.0
326            .as_mut()
327            .unwrap()
328            .with_overlay_mut(|overlay| overlay.as_mut().map(|nested| (f)(nested.get_mut())))
329    }
330}
331
332impl<Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
333    for Overlay<'_, Message, Theme, Renderer>
334where
335    Renderer: core::Renderer,
336{
337    fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
338        self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
339            .unwrap_or_default()
340    }
341
342    fn draw(
343        &self,
344        renderer: &mut Renderer,
345        theme: &Theme,
346        style: &renderer::Style,
347        layout: Layout<'_>,
348        cursor: mouse::Cursor,
349    ) {
350        let _ = self.with_overlay_maybe(|overlay| {
351            overlay.draw(renderer, theme, style, layout, cursor);
352        });
353    }
354
355    fn mouse_interaction(
356        &self,
357        layout: Layout<'_>,
358        cursor: mouse::Cursor,
359        renderer: &Renderer,
360    ) -> mouse::Interaction {
361        self.with_overlay_maybe(|overlay| overlay.mouse_interaction(layout, cursor, renderer))
362            .unwrap_or_default()
363    }
364
365    fn update(
366        &mut self,
367        event: &Event,
368        layout: Layout<'_>,
369        cursor: mouse::Cursor,
370        renderer: &Renderer,
371        shell: &mut Shell<'_, Message>,
372    ) {
373        let _ = self.with_overlay_mut_maybe(|overlay| {
374            overlay.update(event, layout, cursor, renderer, shell);
375        });
376    }
377}
378
379impl<'a, Message, Theme, Renderer, Dependency, View>
380    From<Lazy<'a, Message, Theme, Renderer, Dependency, View>>
381    for Element<'a, Message, Theme, Renderer>
382where
383    View: Into<Element<'static, Message, Theme, Renderer>> + 'static,
384    Renderer: core::Renderer + 'static,
385    Message: 'static,
386    Theme: 'static,
387    Dependency: Hash + 'a,
388{
389    fn from(lazy: Lazy<'a, Message, Theme, Renderer, Dependency, View>) -> Self {
390        Self::new(lazy)
391    }
392}