Skip to main content

iced_widget/
component.rs

1//! Build and reuse custom widgets using The Elm Architecture.
2use crate::Action;
3use crate::core::event;
4use crate::core::layout::{self, Layout};
5use crate::core::mouse;
6use crate::core::overlay;
7use crate::core::renderer;
8use crate::core::shell;
9use crate::core::widget;
10use crate::core::widget::tree::{self, Tree};
11use crate::core::window;
12use crate::core::{Element, Event, Length, Rectangle, Shell, Size, Vector, Widget};
13
14use std::cell::{Cell, RefCell};
15
16/// A reusable, custom widget that uses The Elm Architecture.
17///
18/// A [`Component`] allows you to implement custom widgets as if they were
19/// `iced` applications with encapsulated state.
20///
21/// In other words, a [`Component`] allows you to turn `iced` applications into
22/// custom widgets and embed them without cumbersome wiring.
23///
24/// A [`Component`] produces widgets that may fire an [`Event`](Component::Event)
25/// and update the internal state of the [`Component`].
26///
27/// Additionally, a [`Component`] is capable of producing a `Message` to notify
28/// the parent application of any relevant interactions.
29///
30/// # State
31/// A component can store its state in one of two ways: either as data within the
32/// implementor of the trait, or in a type [`State`][Component::State] that is managed
33/// by the runtime and provided to the trait methods. These two approaches are not
34/// mutually exclusive and have opposite pros and cons.
35///
36/// For instance, if a piece of state is needed by multiple components that reside
37/// in different branches of the tree, then it's more convenient to let a common
38/// ancestor store it and pass it down.
39///
40/// On the other hand, if a piece of state is only needed by the component itself,
41/// you can store it as part of its internal [`State`][Component::State].
42pub trait Component<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
43    /// The internal state of this [`Component`].
44    type State: Default + 'static;
45
46    /// The type of event this [`Component`] handles internally.
47    type Event: 'static;
48
49    /// Processes an [`Event`](Component::Event) and updates the [`Component`] state accordingly.
50    ///
51    /// It can produce a `Message` for the parent application.
52    fn update(
53        &self,
54        state: &mut Self::State,
55        event: Self::Event,
56        renderer: &Renderer,
57    ) -> Option<Message>;
58
59    /// Produces the widgets of the [`Component`], which may trigger an [`Event`](Component::Event)
60    /// on user interaction.
61    fn view(&self, state: &Self::State) -> impl crate::Widget<Self::Event, Theme, Renderer> + 'a;
62
63    /// Listens to a runtime [`Event`] and performs an [`Action`] as a result.
64    ///
65    /// If the [`Action`] publishes a [`Component::Event`], it will be immediately fed
66    /// to [`update`](Self::update).
67    ///
68    /// By default, it returns [`Action::none`].
69    fn listen(
70        &self,
71        _state: &Self::State,
72        _event: &Event,
73        _bounds: Rectangle,
74        _cursor: mouse::Cursor,
75    ) -> Action<Self::Event> {
76        Action::none()
77    }
78
79    /// Returns the current [`mouse::Interaction`] of the [`Component`].
80    ///
81    /// This interaction will override any interaction produced by the [`view`](Self::view)
82    /// of the [`Component`].
83    ///
84    /// By default, it returns [`mouse::Interaction::None`].
85    fn mouse_interaction(&self, _state: &Self::State) -> mouse::Interaction {
86        mouse::Interaction::None
87    }
88
89    /// Reconciles the current [`Component`] with its internal [`State`](Self::State) persisted
90    /// in the widget tree.
91    ///
92    /// This method will be called every time the widget tree changes. You can leverage it to
93    /// detect and react to changes in the [`Component`].
94    ///
95    /// By default, it does nothing.
96    fn diff(&mut self, _state: &mut Self::State) {}
97
98    /// Run the provided [`widget::Operation`] on the [`Component`].
99    ///
100    /// By default, it does nothing.
101    fn operate(
102        &self,
103        _state: &mut Self::State,
104        _bounds: Rectangle,
105        _operation: &mut dyn widget::Operation,
106    ) {
107    }
108}
109
110/// Turns an implementor of [`Component`] into an [`Element`] that can be
111/// embedded in any application.
112pub fn component<'a, C, Message, Theme, Renderer>(
113    component: C,
114) -> impl Widget<Message, Theme, Renderer> + 'a
115where
116    C: Component<'a, Message, Theme, Renderer> + 'a,
117    C::State: 'static,
118    Message: 'a,
119    Theme: 'a,
120    Renderer: 'a,
121{
122    Instance {
123        component,
124        view: crate::space()._boxed(),
125        limits: layout::Limits::new(Size::ZERO, Size::INFINITE),
126        is_outdated: Cell::new(true),
127        has_overlay: false,
128    }
129}
130
131struct Instance<'a, C, Message, Theme, Renderer>
132where
133    C: Component<'a, Message, Theme, Renderer> + 'a,
134{
135    component: C,
136    view: Element<'a, C::Event, Theme, Renderer>,
137    limits: layout::Limits,
138    is_outdated: Cell<bool>,
139    has_overlay: bool,
140}
141
142struct Internal<State, Event> {
143    state: State,
144    events: shell::Bus<Event>,
145}
146
147impl<'a, C, Message, Theme, Renderer> widget::Meta for Instance<'a, C, Message, Theme, Renderer> where
148    C: Component<'a, Message, Theme, Renderer> + 'a
149{
150}
151
152impl<'a, C, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
153    for Instance<'a, C, Message, Theme, Renderer>
154where
155    C: Component<'a, Message, Theme, Renderer> + 'a,
156{
157    fn tag(&self) -> tree::Tag {
158        tree::Tag::of::<RefCell<Internal<C::State, C::Event>>>()
159    }
160
161    fn state(&self) -> tree::State {
162        tree::State::new(RefCell::new(Internal {
163            state: C::State::default(),
164            events: shell::Bus::<C::Event>::new(),
165        }))
166    }
167
168    fn diff(&mut self, tree: &mut Tree) {
169        let mut internal = tree
170            .state
171            .downcast_mut::<RefCell<Internal<C::State, C::Event>>>()
172            .borrow_mut();
173
174        self.component.diff(&mut internal.state);
175
176        if self.is_outdated.get() {
177            self.view = self.component.view(&internal.state)._boxed();
178            drop(internal);
179
180            tree.diff_children(std::slice::from_mut(&mut self.view));
181
182            self.is_outdated.set(false);
183            self.limits = layout::Limits::new(Size::ZERO, Size::INFINITE);
184        }
185    }
186
187    fn size(&self) -> Size<Length> {
188        self.view.size()
189    }
190
191    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
192        if &self.limits != limits {
193            self.limits = *limits;
194            self.view.layout(&mut tree.children[0], renderer, limits);
195        }
196
197        tree.size = tree.children[0].size;
198    }
199
200    fn update(
201        &mut self,
202        tree: &mut Tree,
203        event: &Event,
204        layout: Layout,
205        cursor: mouse::Cursor,
206        renderer: &Renderer,
207        shell: &mut Shell<'_, Message>,
208        viewport: &Rectangle,
209    ) {
210        let mut internal = tree
211            .state
212            .downcast_mut::<RefCell<Internal<C::State, C::Event>>>()
213            .borrow_mut();
214
215        let action = self
216            .component
217            .listen(&internal.state, event, layout.bounds(), cursor);
218
219        let (publish, redraw_request, event_status) = action.into_inner();
220
221        shell.request_redraw_at(redraw_request);
222
223        if let event::Status::Captured = event_status {
224            shell.capture_event();
225        }
226
227        if let Some(event) = publish {
228            let _ = internal.events.push(event);
229        }
230
231        let mut invalidation = shell::Invalidation::None;
232
233        if !shell.is_event_captured() {
234            let mut local_shell = shell.local(&mut internal.events);
235
236            self.view.update(
237                &mut tree.children[0],
238                event,
239                layout,
240                cursor,
241                renderer,
242                &mut local_shell,
243                viewport,
244            );
245
246            if local_shell.is_event_captured() {
247                shell.capture_event();
248            }
249
250            shell.request_redraw_at(local_shell.redraw_request());
251            shell.request_input_method(local_shell.input_method());
252            shell.clipboard_mut().merge(local_shell.clipboard_mut());
253
254            invalidation = local_shell.invalidation();
255        }
256
257        if internal.events.is_empty() && matches!(invalidation, shell::Invalidation::None) {
258            return;
259        }
260
261        let Internal { state, events } = &mut *internal;
262
263        for (event, receipt) in events.drain() {
264            if let Some(message) = self.component.update(state, event, renderer) {
265                shell.forward(message, receipt);
266            }
267        }
268
269        let previous_sizing = self.view.size();
270
271        self.view = self.component.view(state)._boxed();
272        drop(internal);
273
274        tree.diff_children(std::slice::from_mut(&mut self.view));
275
276        let previous_size = tree.size;
277
278        self.view
279            .layout(&mut tree.children[0], renderer, &self.limits);
280
281        tree.size = tree.children[0].size;
282
283        let new_sizing = self.view.size();
284
285        // We must invalidate application layout in 2 instances:
286        //
287        // 1. The size hint of the component changes. Other widgets
288        //    may change layout behavior.
289        //
290        // 2. The size hint of the component is not fluid for any axis
291        //    and the component has changed size. The new size may
292        //    push other widgets around.
293        if new_sizing != previous_sizing {
294            shell.invalidate_widgets();
295        } else if (new_sizing.width.fill_factor() == 0 || new_sizing.height.fill_factor() == 0)
296            && previous_size != tree.size
297        {
298            shell.invalidate_layout();
299        } else {
300            shell.invalidate_overlay();
301        }
302
303        self.is_outdated.set(false);
304
305        if let Event::Window(window::Event::RedrawRequested(_)) = event {
306            let mut internal = tree
307                .state
308                .downcast_mut::<RefCell<Internal<C::State, C::Event>>>()
309                .borrow_mut();
310
311            let mut local_shell = shell.local(&mut internal.events);
312
313            self.view.update(
314                &mut tree.children[0],
315                event,
316                layout,
317                cursor,
318                renderer,
319                &mut local_shell,
320                viewport,
321            );
322
323            if internal.events.is_empty() {
324                return;
325            }
326        }
327
328        shell.request_redraw();
329    }
330
331    fn draw(
332        &self,
333        tree: &Tree,
334        renderer: &mut Renderer,
335        theme: &Theme,
336        style: &renderer::Style,
337        layout: Layout,
338        cursor: mouse::Cursor,
339        viewport: &Rectangle,
340    ) {
341        self.view.draw(
342            &tree.children[0],
343            renderer,
344            theme,
345            style,
346            layout,
347            cursor,
348            viewport,
349        );
350    }
351
352    fn mouse_interaction(
353        &self,
354        tree: &Tree,
355        layout: Layout,
356        cursor: mouse::Cursor,
357        viewport: &Rectangle,
358        renderer: &Renderer,
359    ) -> mouse::Interaction {
360        let internal = tree
361            .state
362            .downcast_ref::<RefCell<Internal<C::State, C::Event>>>()
363            .borrow();
364
365        let interaction = self.component.mouse_interaction(&internal.state);
366
367        if interaction != mouse::Interaction::None {
368            return interaction;
369        }
370
371        self.view
372            .mouse_interaction(&tree.children[0], layout, cursor, viewport, renderer)
373    }
374
375    fn operate(
376        &mut self,
377        tree: &mut Tree,
378        layout: Layout,
379        viewport: &Rectangle,
380        renderer: &Renderer,
381        operation: &mut dyn widget::Operation,
382    ) {
383        {
384            let internal = tree
385                .state
386                .downcast_mut::<RefCell<Internal<C::State, C::Event>>>()
387                .get_mut();
388
389            self.component
390                .operate(&mut internal.state, layout.bounds(), operation);
391        }
392
393        self.view
394            .operate(&mut tree.children[0], layout, viewport, renderer, operation);
395    }
396
397    fn overlay<'b>(
398        &'b mut self,
399        tree: &'b mut Tree,
400        layout: Layout,
401        renderer: &Renderer,
402        viewport: &Rectangle,
403        translation: Vector,
404        window: Size,
405    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
406        let overlays = self.view.overlay(
407            &mut tree.children[0],
408            layout,
409            renderer,
410            viewport,
411            translation,
412            window,
413        );
414
415        self.has_overlay = !overlays.is_empty();
416
417        let internal = tree
418            .state
419            .downcast_ref::<RefCell<Internal<C::State, C::Event>>>();
420
421        overlays
422            .into_iter()
423            .map(|raw| {
424                overlay::Element::new(Box::new(Overlay {
425                    component: &self.component,
426                    internal,
427                    is_outdated: &self.is_outdated,
428                    raw,
429                }))
430            })
431            .collect()
432    }
433}
434
435struct Overlay<'a, 'b, C, Message, Theme, Renderer>
436where
437    C: Component<'a, Message, Theme, Renderer>,
438{
439    component: &'b C,
440    internal: &'b RefCell<Internal<C::State, C::Event>>,
441    is_outdated: &'b Cell<bool>,
442    raw: overlay::Element<'b, C::Event, Theme, Renderer>,
443}
444
445impl<'a, 'b, C, Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
446    for Overlay<'a, 'b, C, Message, Theme, Renderer>
447where
448    C: Component<'a, Message, Theme, Renderer>,
449{
450    fn update(
451        &mut self,
452        event: &Event,
453        cursor: mouse::Cursor,
454        renderer: &Renderer,
455        shell: &mut Shell<'_, Message>,
456    ) {
457        let mut internal = self.internal.borrow_mut();
458        let mut local_shell = shell.local(&mut internal.events);
459
460        self.raw
461            .as_overlay_mut()
462            .update(event, cursor, renderer, &mut local_shell);
463
464        if local_shell.is_event_captured() {
465            shell.capture_event();
466        }
467
468        shell.invalidate(local_shell.invalidation());
469        shell.request_redraw_at(local_shell.redraw_request());
470        shell.request_input_method(local_shell.input_method());
471        shell.clipboard_mut().merge(local_shell.clipboard_mut());
472
473        if internal.events.is_empty() {
474            return;
475        }
476
477        let Internal { state, events } = &mut *internal;
478
479        for (event, receipt) in events.drain() {
480            if let Some(message) = self.component.update(state, event, renderer) {
481                shell.forward(message, receipt);
482            }
483        }
484
485        self.is_outdated.set(true);
486
487        shell.invalidate_layout_with(shell::Diff::Perform);
488        shell.request_redraw();
489    }
490
491    fn draw(
492        &self,
493        renderer: &mut Renderer,
494        theme: &Theme,
495        style: &renderer::Style,
496        cursor: mouse::Cursor,
497    ) {
498        self.raw.as_overlay().draw(renderer, theme, style, cursor);
499    }
500
501    fn mouse_interaction(&self, cursor: mouse::Cursor, renderer: &Renderer) -> mouse::Interaction {
502        self.raw.as_overlay().mouse_interaction(cursor, renderer)
503    }
504
505    fn index(&self) -> f32 {
506        self.raw.as_overlay().index()
507    }
508
509    fn operate(&mut self, renderer: &Renderer, operation: &mut dyn widget::Operation) {
510        self.raw.as_overlay_mut().operate(renderer, operation);
511    }
512
513    fn overlay<'c>(
514        &'c mut self,
515        renderer: &Renderer,
516    ) -> Vec<overlay::Element<'c, Message, Theme, Renderer>> {
517        let overlays = self.raw.as_overlay_mut().overlay(renderer);
518
519        if overlays.is_empty() {
520            return Vec::new();
521        }
522
523        overlays
524            .into_iter()
525            .map(|raw| {
526                overlay::Element::new(Box::new(Overlay {
527                    component: self.component,
528                    internal: self.internal,
529                    is_outdated: self.is_outdated,
530                    raw,
531                }))
532            })
533            .collect()
534    }
535}