Skip to main content

iced_widget/
lazy.rs

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