relm4/component/sync/
traits.rs

1// Copyright 2021-2022 Aaron Erhardt <aaron.erhardt@t-online.de>
2// Copyright 2022 System76 <info@system76.com>
3// SPDX-License-Identifier: MIT or Apache-2.0
4
5use std::fmt::Debug;
6
7use crate::{ComponentBuilder, ComponentParts, ComponentSender, Sender};
8
9/// The fundamental building block of a Relm4 application.
10///
11/// A `Component` is an element of an application that defines initialization, state, behavior and
12/// communication as a modular unit.
13///
14/// `Component` is powerful and flexible, but for many use-cases the [`SimpleComponent`]
15/// convenience trait will suffice. [`SimpleComponent`] enforces separation between model and view
16/// updates, and provides no-op implementations for advanced features that are not relevant for most
17/// use-cases.
18pub trait Component: Sized + 'static {
19    /// Messages which are received from commands executing in the background.
20    type CommandOutput: Debug + Send + 'static;
21
22    /// The message type that the component accepts as inputs.
23    type Input: Debug + 'static;
24
25    /// The message type that the component provides as outputs.
26    type Output: Debug + 'static;
27
28    /// The parameter used to initialize the component.
29    type Init;
30
31    /// The top-level widget of the component.
32    type Root: Debug + Clone;
33
34    /// The type that's used for storing widgets created for this component.
35    type Widgets: 'static;
36
37    /// Create a builder for this component.
38    #[must_use]
39    fn builder() -> ComponentBuilder<Self> {
40        ComponentBuilder::<Self>::default()
41    }
42
43    /// Initializes the root widget.
44    fn init_root() -> Self::Root;
45
46    /// Creates the initial model and view, docking it into the component.
47    fn init(
48        init: Self::Init,
49        root: Self::Root,
50        sender: ComponentSender<Self>,
51    ) -> ComponentParts<Self>;
52
53    /// Processes inputs received by the component.
54    #[allow(unused)]
55    fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) {}
56
57    /// Defines how the component should respond to command updates.
58    #[allow(unused)]
59    fn update_cmd(
60        &mut self,
61        message: Self::CommandOutput,
62        sender: ComponentSender<Self>,
63        root: &Self::Root,
64    ) {
65    }
66
67    /// Updates the model and view upon completion of a command.
68    ///
69    /// Overriding this method is helpful if you need access to the widgets while processing a
70    /// command output.
71    ///
72    /// The default implementation of this method calls [`update_cmd`] followed by [`update_view`].
73    /// If you override this method while using the [`component`] macro, you must remember to call
74    /// [`update_view`] in your implementation. Otherwise, the view will not reflect the updated
75    /// model.
76    ///
77    /// [`update_cmd`]: Self::update_cmd
78    /// [`update_view`]: Self::update_view
79    /// [`component`]: relm4_macros::component
80    fn update_cmd_with_view(
81        &mut self,
82        widgets: &mut Self::Widgets,
83        message: Self::CommandOutput,
84        sender: ComponentSender<Self>,
85        root: &Self::Root,
86    ) {
87        self.update_cmd(message, sender.clone(), root);
88        self.update_view(widgets, sender);
89    }
90
91    /// Updates the view after the model has been updated.
92    #[allow(unused)]
93    fn update_view(&self, widgets: &mut Self::Widgets, sender: ComponentSender<Self>) {}
94
95    /// Updates the model and view when a new input is received.
96    ///
97    /// Overriding this method is helpful if you need access to the widgets while processing an
98    /// input.
99    ///
100    /// The default implementation of this method calls [`update`] followed by [`update_view`]. If
101    /// you override this method while using the [`component`] macro, you must remember to
102    /// call [`update_view`] in your implementation. Otherwise, the view will not reflect the
103    /// updated model.
104    ///
105    /// [`update`]: Self::update
106    /// [`update_view`]: Self::update_view
107    /// [`component`]: relm4_macros::component
108    fn update_with_view(
109        &mut self,
110        widgets: &mut Self::Widgets,
111        message: Self::Input,
112        sender: ComponentSender<Self>,
113        root: &Self::Root,
114    ) {
115        self.update(message, sender.clone(), root);
116        self.update_view(widgets, sender);
117    }
118
119    /// Last method called before a component is shut down.
120    ///
121    /// This method is guaranteed to be called even when the entire application is shut down.
122    #[allow(unused)]
123    fn shutdown(&mut self, widgets: &mut Self::Widgets, output: Sender<Self::Output>) {}
124
125    /// An identifier for the component used for debug logging.
126    ///
127    /// The default implementation of this method uses the address of the component, but
128    /// implementations are free to provide more meaningful identifiers.
129    fn id(&self) -> String {
130        format!("{:p}", &self)
131    }
132}
133
134/// Elm-style variant of a [`Component`] with view updates separated from input updates.
135pub trait SimpleComponent: Sized + 'static {
136    /// The message type that the component accepts as inputs.
137    type Input: Debug + 'static;
138
139    /// The message type that the component provides as outputs.
140    type Output: Debug + 'static;
141
142    /// The parameter used to initialize the component.
143    type Init;
144
145    /// The top-level widget of the component.
146    type Root: Debug + Clone;
147
148    /// The type that's used for storing widgets created for this component.
149    type Widgets: 'static;
150
151    /// Initializes the root widget
152    fn init_root() -> Self::Root;
153
154    /// Creates the initial model and view, docking it into the component.
155    fn init(
156        init: Self::Init,
157        root: Self::Root,
158        sender: ComponentSender<Self>,
159    ) -> ComponentParts<Self>;
160
161    /// Processes inputs received by the component.
162    #[allow(unused)]
163    fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {}
164
165    /// Defines how the component should respond to command updates.
166    #[allow(unused)]
167    fn update_cmd(&mut self, input: &Sender<Self::Input>, output: Sender<Self::Output>) {}
168
169    /// Updates the view after the model has been updated.
170    #[allow(unused)]
171    fn update_view(&self, widgets: &mut Self::Widgets, sender: ComponentSender<Self>) {}
172
173    /// Last method called before a component is shut down.
174    ///
175    /// This method is guaranteed to be called even when the entire application is shut down.
176    #[allow(unused)]
177    fn shutdown(&mut self, widgets: &mut Self::Widgets, output: Sender<Self::Output>) {}
178}
179
180impl<C> Component for C
181where
182    C: SimpleComponent,
183{
184    type Init = C::Init;
185    type Input = C::Input;
186    type Output = C::Output;
187    type Root = C::Root;
188    type Widgets = C::Widgets;
189
190    type CommandOutput = ();
191
192    fn init_root() -> Self::Root {
193        C::init_root()
194    }
195
196    fn init(
197        init: Self::Init,
198        root: Self::Root,
199        sender: ComponentSender<Self>,
200    ) -> ComponentParts<Self> {
201        C::init(init, root, sender)
202    }
203
204    fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>, _root: &Self::Root) {
205        C::update(self, message, sender);
206    }
207
208    fn update_view(&self, widgets: &mut Self::Widgets, sender: ComponentSender<Self>) {
209        C::update_view(self, widgets, sender);
210    }
211
212    fn shutdown(&mut self, widgets: &mut Self::Widgets, output: Sender<Self::Output>) {
213        self.shutdown(widgets, output);
214    }
215}
216
217/// An empty, non-interactive component as a placeholder for tests.
218impl SimpleComponent for () {
219    type Input = ();
220    type Output = ();
221    type Init = ();
222    type Root = ();
223    type Widgets = ();
224
225    fn init_root() -> Self::Root {}
226
227    fn init(
228        _init: Self::Init,
229        _root: Self::Root,
230        _sender: ComponentSender<Self>,
231    ) -> ComponentParts<Self> {
232        ComponentParts {
233            model: (),
234            widgets: (),
235        }
236    }
237}