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::{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{
48    fn tag(&self) -> tree::Tag {
49        struct Tag<T>(T);
50        tree::Tag::of::<Tag<W>>()
51    }
52
53    fn state(&self) -> tree::State {
54        tree::State::new(Internal {
55            element: (self.view)(&self.dependency),
56            hash: hash(&self.dependency),
57        })
58    }
59
60    fn diff(&mut self, tree: &mut Tree) {
61        let current = tree.state.downcast_mut::<Internal<W>>();
62
63        let new_hash = hash(&self.dependency);
64
65        if current.hash != new_hash {
66            current.hash = new_hash;
67            current.element = (self.view)(&self.dependency);
68        }
69
70        // The widget value is recreated every frame, so the size hint must be
71        // re-derived from the cached element on every diff
72        self.size = current.element.size();
73
74        tree::diff_children(
75            &mut tree.children,
76            std::slice::from_mut(&mut current.element),
77        );
78    }
79
80    fn size(&self) -> Size<Length> {
81        self.size
82    }
83
84    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
85        let cached = tree.state.downcast_mut::<Internal<W>>();
86
87        cached
88            .element
89            .layout(&mut tree.children[0], renderer, limits);
90
91        tree.size = tree.children[0].size;
92    }
93
94    fn operate(
95        &mut self,
96        tree: &mut Tree,
97        layout: Layout,
98        viewport: &Rectangle,
99        renderer: &Renderer,
100        operation: &mut dyn widget::Operation,
101    ) {
102        let cached = tree.state.downcast_mut::<Internal<W>>();
103
104        cached
105            .element
106            .operate(&mut tree.children[0], layout, viewport, renderer, operation);
107    }
108
109    fn update(
110        &mut self,
111        tree: &mut Tree,
112        event: &Event,
113        layout: Layout,
114        cursor: mouse::Cursor,
115        renderer: &Renderer,
116        shell: &mut Shell<'_, Message>,
117        viewport: &Rectangle,
118    ) {
119        let cached = tree.state.downcast_mut::<Internal<W>>();
120
121        cached.element.update(
122            &mut tree.children[0],
123            event,
124            layout,
125            cursor,
126            renderer,
127            shell,
128            viewport,
129        );
130    }
131
132    fn mouse_interaction(
133        &self,
134        tree: &Tree,
135        layout: Layout,
136        cursor: mouse::Cursor,
137        viewport: &Rectangle,
138        renderer: &Renderer,
139    ) -> mouse::Interaction {
140        let cached = tree.state.downcast_ref::<Internal<W>>();
141
142        cached
143            .element
144            .mouse_interaction(&tree.children[0], layout, cursor, viewport, renderer)
145    }
146
147    fn draw(
148        &self,
149        tree: &Tree,
150        renderer: &mut Renderer,
151        theme: &Theme,
152        style: &renderer::Style,
153        layout: Layout,
154        cursor: mouse::Cursor,
155        viewport: &Rectangle,
156    ) {
157        let current = tree.state.downcast_ref::<Internal<W>>();
158
159        current.element.draw(
160            &tree.children[0],
161            renderer,
162            theme,
163            style,
164            layout,
165            cursor,
166            viewport,
167        );
168    }
169
170    fn overlay<'b>(
171        &'b mut self,
172        tree: &'b mut Tree,
173        layout: Layout,
174        renderer: &Renderer,
175        viewport: &Rectangle,
176        translation: Vector,
177        window: Size,
178    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
179        let current = tree.state.downcast_mut::<Internal<W>>();
180
181        current.element.overlay(
182            &mut tree.children[0],
183            layout,
184            renderer,
185            viewport,
186            translation,
187            window,
188        )
189    }
190}
191
192fn hash(data: impl Hash) -> u64 {
193    let mut hasher = FxHasher::default();
194    data.hash(&mut hasher);
195    hasher.finish()
196}