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 diff(&mut self, tree: &mut Tree) {
111        let current = tree
112            .state
113            .downcast_mut::<Internal<Message, Theme, Renderer>>();
114
115        let new_hash = {
116            let mut hasher = FxHasher::default();
117            self.dependency.hash(&mut hasher);
118
119            hasher.finish()
120        };
121
122        if current.hash != new_hash {
123            current.hash = new_hash;
124
125            let element = (self.view)(&self.dependency).into();
126            current.element = Rc::new(RefCell::new(Some(element)));
127        }
128
129        (*self.element.borrow_mut()) = Some(current.element.clone());
130
131        self.with_element_mut(|element| {
132            tree.diff_children(std::slice::from_mut(&mut element.as_widget_mut()));
133        });
134    }
135
136    fn size(&self) -> Size<Length> {
137        self.with_element(|element| element.as_widget().size())
138    }
139
140    fn layout(
141        &mut self,
142        tree: &mut Tree,
143        renderer: &Renderer,
144        limits: &layout::Limits,
145    ) -> layout::Node {
146        self.with_element_mut(|element| {
147            element
148                .as_widget_mut()
149                .layout(&mut tree.children[0], renderer, limits)
150        })
151    }
152
153    fn operate(
154        &mut self,
155        tree: &mut Tree,
156        layout: Layout<'_>,
157        renderer: &Renderer,
158        operation: &mut dyn widget::Operation,
159    ) {
160        self.with_element_mut(|element| {
161            element
162                .as_widget_mut()
163                .operate(&mut tree.children[0], layout, renderer, operation);
164        });
165    }
166
167    fn update(
168        &mut self,
169        tree: &mut Tree,
170        event: &Event,
171        layout: Layout<'_>,
172        cursor: mouse::Cursor,
173        renderer: &Renderer,
174        shell: &mut Shell<'_, Message>,
175        viewport: &Rectangle,
176    ) {
177        self.with_element_mut(|element| {
178            element.as_widget_mut().update(
179                &mut tree.children[0],
180                event,
181                layout,
182                cursor,
183                renderer,
184                shell,
185                viewport,
186            );
187        });
188    }
189
190    fn mouse_interaction(
191        &self,
192        tree: &Tree,
193        layout: Layout<'_>,
194        cursor: mouse::Cursor,
195        viewport: &Rectangle,
196        renderer: &Renderer,
197    ) -> mouse::Interaction {
198        self.with_element(|element| {
199            element.as_widget().mouse_interaction(
200                &tree.children[0],
201                layout,
202                cursor,
203                viewport,
204                renderer,
205            )
206        })
207    }
208
209    fn draw(
210        &self,
211        tree: &Tree,
212        renderer: &mut Renderer,
213        theme: &Theme,
214        style: &renderer::Style,
215        layout: Layout<'_>,
216        cursor: mouse::Cursor,
217        viewport: &Rectangle,
218    ) {
219        self.with_element(|element| {
220            element.as_widget().draw(
221                &tree.children[0],
222                renderer,
223                theme,
224                style,
225                layout,
226                cursor,
227                viewport,
228            );
229        });
230    }
231
232    fn overlay<'b>(
233        &'b mut self,
234        tree: &'b mut Tree,
235        layout: Layout<'b>,
236        renderer: &Renderer,
237        viewport: &Rectangle,
238        translation: Vector,
239    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
240        let overlay = InnerBuilder {
241            cell: self.element.borrow().as_ref().unwrap().clone(),
242            element: self
243                .element
244                .borrow()
245                .as_ref()
246                .unwrap()
247                .borrow_mut()
248                .take()
249                .unwrap(),
250            tree: &mut tree.children[0],
251            layout,
252            overlay_builder: |element, tree, layout| {
253                element
254                    .as_widget_mut()
255                    .overlay(tree, *layout, renderer, viewport, translation)
256                    .map(|overlay| RefCell::new(overlay::Nested::new(overlay)))
257            },
258        }
259        .build();
260
261        #[allow(clippy::redundant_closure_for_method_calls)]
262        if overlay.with_overlay(|overlay| overlay.is_some()) {
263            Some(overlay::Element::new(Box::new(Overlay(Some(overlay)))))
264        } else {
265            let heads = overlay.into_heads();
266
267            // - You may not like it, but this is what peak performance looks like
268            // - TODO: Get rid of ouroboros, for good
269            // - What?!
270            *self.element.borrow().as_ref().unwrap().borrow_mut() = Some(heads.element);
271
272            None
273        }
274    }
275}
276
277#[self_referencing]
278struct Inner<'a, Message: 'a, Theme: 'a, Renderer: 'a> {
279    cell: Rc<RefCell<Option<Element<'static, Message, Theme, Renderer>>>>,
280    element: Element<'static, Message, Theme, Renderer>,
281    tree: &'a mut Tree,
282    layout: Layout<'a>,
283
284    #[borrows(mut element, mut tree, layout)]
285    #[not_covariant]
286    overlay: Option<RefCell<overlay::Nested<'this, Message, Theme, Renderer>>>,
287}
288
289struct Overlay<'a, Message, Theme, Renderer>(Option<Inner<'a, Message, Theme, Renderer>>);
290
291impl<Message, Theme, Renderer> Drop for Overlay<'_, Message, Theme, Renderer> {
292    fn drop(&mut self) {
293        let heads = self.0.take().unwrap().into_heads();
294        (*heads.cell.borrow_mut()) = Some(heads.element);
295    }
296}
297
298impl<Message, Theme, Renderer> Overlay<'_, Message, Theme, Renderer> {
299    fn with_overlay_maybe<T>(
300        &self,
301        f: impl FnOnce(&mut overlay::Nested<'_, Message, Theme, Renderer>) -> T,
302    ) -> Option<T> {
303        self.0
304            .as_ref()
305            .unwrap()
306            .with_overlay(|overlay| overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())))
307    }
308
309    fn with_overlay_mut_maybe<T>(
310        &mut self,
311        f: impl FnOnce(&mut overlay::Nested<'_, Message, Theme, Renderer>) -> T,
312    ) -> Option<T> {
313        self.0
314            .as_mut()
315            .unwrap()
316            .with_overlay_mut(|overlay| overlay.as_mut().map(|nested| (f)(nested.get_mut())))
317    }
318}
319
320impl<Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
321    for Overlay<'_, Message, Theme, Renderer>
322where
323    Renderer: core::Renderer,
324{
325    fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
326        self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
327            .unwrap_or_default()
328    }
329
330    fn draw(
331        &self,
332        renderer: &mut Renderer,
333        theme: &Theme,
334        style: &renderer::Style,
335        layout: Layout<'_>,
336        cursor: mouse::Cursor,
337    ) {
338        let _ = self.with_overlay_maybe(|overlay| {
339            overlay.draw(renderer, theme, style, layout, cursor);
340        });
341    }
342
343    fn mouse_interaction(
344        &self,
345        layout: Layout<'_>,
346        cursor: mouse::Cursor,
347        renderer: &Renderer,
348    ) -> mouse::Interaction {
349        self.with_overlay_maybe(|overlay| overlay.mouse_interaction(layout, cursor, renderer))
350            .unwrap_or_default()
351    }
352
353    fn update(
354        &mut self,
355        event: &Event,
356        layout: Layout<'_>,
357        cursor: mouse::Cursor,
358        renderer: &Renderer,
359        shell: &mut Shell<'_, Message>,
360    ) {
361        let _ = self.with_overlay_mut_maybe(|overlay| {
362            overlay.update(event, layout, cursor, renderer, shell);
363        });
364    }
365}
366
367impl<'a, Message, Theme, Renderer, Dependency, View>
368    From<Lazy<'a, Message, Theme, Renderer, Dependency, View>>
369    for Element<'a, Message, Theme, Renderer>
370where
371    View: Into<Element<'static, Message, Theme, Renderer>> + 'static,
372    Renderer: core::Renderer + 'static,
373    Message: 'static,
374    Theme: 'static,
375    Dependency: Hash + 'a,
376{
377    fn from(lazy: Lazy<'a, Message, Theme, Renderer, Dependency, View>) -> Self {
378        Self::new(lazy)
379    }
380}