Skip to main content

iced_widget/
lazy.rs

1//! A widget that only rebuilds its contents when necessary.
2use crate::core::layout::{self, Layout};
3use crate::core::mouse;
4use crate::core::overlay;
5use crate::core::renderer;
6use crate::core::widget::tree::{self, Tree};
7use crate::core::widget::{self, Widget};
8use crate::core::{self, Event, Length, Rectangle, Shell, Size, Vector};
9
10use rustc_hash::FxHasher;
11use std::hash::{Hash, Hasher};
12
13/// A widget that only rebuilds its contents when necessary.
14pub struct Lazy<'a, W, Dependency> {
15    dependency: Dependency,
16    view: Box<dyn Fn(&Dependency) -> W + 'a>,
17    size: Size<Length>,
18}
19
20impl<'a, W, Dependency> Lazy<'a, W, Dependency>
21where
22    Dependency: Hash + 'a,
23{
24    /// Creates a new [`Lazy`] widget with the given data `Dependency` and a
25    /// closure that can turn this data into a widget tree.
26    pub fn new(dependency: Dependency, view: impl Fn(&Dependency) -> W + 'a) -> Self {
27        Self {
28            dependency,
29            view: Box::new(view),
30            size: Size::new(Length::Fit, Length::Fit),
31        }
32    }
33}
34
35struct Internal<W> {
36    element: W,
37    hash: u64,
38}
39
40impl<W, Dependency> widget::Meta for Lazy<'_, W, Dependency> {}
41
42impl<'a, W, Message, Theme, Renderer, Dependency> Widget<Message, Theme, Renderer>
43    for Lazy<'a, W, Dependency>
44where
45    W: Widget<Message, Theme, Renderer> + 'static,
46    Dependency: Hash + 'a,
47    Renderer: core::Renderer,
48{
49    fn tag(&self) -> tree::Tag {
50        struct Tag<T>(T);
51        tree::Tag::of::<Tag<W>>()
52    }
53
54    fn state(&self) -> tree::State {
55        tree::State::new(Internal {
56            element: (self.view)(&self.dependency),
57            hash: hash(&self.dependency),
58        })
59    }
60
61    fn diff(&mut self, tree: &mut Tree) {
62        let current = tree.state.downcast_mut::<Internal<W>>();
63
64        let new_hash = hash(&self.dependency);
65
66        if current.hash != new_hash {
67            current.hash = new_hash;
68            current.element = (self.view)(&self.dependency);
69        }
70
71        // The widget value is recreated every frame, so the size hint must be
72        // re-derived from the cached element on every diff
73        self.size = current.element.size();
74
75        tree::diff_children(
76            &mut tree.children,
77            std::slice::from_mut(&mut current.element),
78        );
79    }
80
81    fn size(&self) -> Size<Length> {
82        self.size
83    }
84
85    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
86        let cached = tree.state.downcast_mut::<Internal<W>>();
87
88        cached
89            .element
90            .layout(&mut tree.children[0], renderer, limits);
91
92        tree.size = tree.children[0].size;
93    }
94
95    fn operate(
96        &mut self,
97        tree: &mut Tree,
98        layout: Layout,
99        viewport: &Rectangle,
100        renderer: &Renderer,
101        operation: &mut dyn widget::Operation,
102    ) {
103        let cached = tree.state.downcast_mut::<Internal<W>>();
104
105        cached
106            .element
107            .operate(&mut tree.children[0], layout, viewport, renderer, operation);
108    }
109
110    fn update(
111        &mut self,
112        tree: &mut Tree,
113        event: &Event,
114        layout: Layout,
115        cursor: mouse::Cursor,
116        renderer: &Renderer,
117        shell: &mut Shell<'_, Message>,
118        viewport: &Rectangle,
119    ) {
120        let cached = tree.state.downcast_mut::<Internal<W>>();
121
122        cached.element.update(
123            &mut tree.children[0],
124            event,
125            layout,
126            cursor,
127            renderer,
128            shell,
129            viewport,
130        );
131    }
132
133    fn mouse_interaction(
134        &self,
135        tree: &Tree,
136        layout: Layout,
137        cursor: mouse::Cursor,
138        viewport: &Rectangle,
139        renderer: &Renderer,
140    ) -> mouse::Interaction {
141        let cached = tree.state.downcast_ref::<Internal<W>>();
142
143        cached
144            .element
145            .mouse_interaction(&tree.children[0], layout, cursor, viewport, renderer)
146    }
147
148    fn draw(
149        &self,
150        tree: &Tree,
151        renderer: &mut Renderer,
152        theme: &Theme,
153        style: &renderer::Style,
154        layout: Layout,
155        cursor: mouse::Cursor,
156        viewport: &Rectangle,
157    ) {
158        let current = tree.state.downcast_ref::<Internal<W>>();
159
160        current.element.draw(
161            &tree.children[0],
162            renderer,
163            theme,
164            style,
165            layout,
166            cursor,
167            viewport,
168        );
169    }
170
171    fn overlay<'b>(
172        &'b mut self,
173        tree: &'b mut Tree,
174        layout: Layout,
175        renderer: &Renderer,
176        viewport: &Rectangle,
177        translation: Vector,
178        window: Size,
179    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
180        let current = tree.state.downcast_mut::<Internal<W>>();
181
182        current.element.overlay(
183            &mut tree.children[0],
184            layout,
185            renderer,
186            viewport,
187            translation,
188            window,
189        )
190    }
191}
192
193fn hash(data: impl Hash) -> u64 {
194    let mut hasher = FxHasher::default();
195    data.hash(&mut hasher);
196    hasher.finish()
197}