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}