Skip to main content

winio_elm/
child.rs

1use std::{
2    fmt::Debug,
3    ops::{Deref, DerefMut},
4};
5
6#[cfg(feature = "primitive")]
7use inherit_methods_macro::inherit_methods;
8use smallvec::SmallVec;
9#[cfg(feature = "handle")]
10use winio_handle::{
11    AsContainer, AsWidget, AsWindow, BorrowedContainer, BorrowedWidget, BorrowedWindow,
12};
13#[cfg(feature = "primitive")]
14use winio_primitive::{Failable, Layoutable, Point, Rect, Size};
15
16use super::ComponentMessage;
17use crate::{BoxComponent, Component, ComponentSender, Root};
18
19/// Helper to embed one component into another. It handles different types of
20/// messages and events.
21pub struct Child<T: Component> {
22    model: T,
23    sender: ComponentSender<T>,
24    msg_cache: SmallVec<[T::Message; 1]>,
25}
26
27impl<T: Component> Child<T> {
28    /// Create and initialize the child component.
29    pub async fn init<'a>(init: impl Into<T::Init<'a>>) -> Result<Self, T::Error> {
30        let sender = ComponentSender::new();
31        let model = T::init(init.into(), &sender).await?;
32        Ok(Self::new(model, sender))
33    }
34
35    pub(crate) fn new(model: T, sender: ComponentSender<T>) -> Self {
36        Self {
37            model,
38            sender,
39            msg_cache: SmallVec::new(),
40        }
41    }
42
43    /// Start to receive and interp the events of the child component.
44    ///
45    /// Define a root component `MainModel`, and it contains a
46    /// `window: Child<Window>`. The message of `MainModel` is defined as
47    /// ```ignore
48    /// enum MainMessage {
49    ///     Noop,
50    ///     Close,
51    /// }
52    /// ```
53    /// In the `MainModel::start`, you should write
54    /// ```ignore
55    /// async fn start(&mut self, sender: &ComponentSender<Self>) -> ! {
56    ///     start! {
57    ///         sender, default: MainMessage::Noop,
58    ///         self.window => {
59    ///             WindowEvent::Close => MainMessage::Close,
60    ///         },
61    ///         // ...other children
62    ///     }
63    /// }
64    /// ```
65    /// It is equivalent to
66    /// ```ignore
67    /// async fn start(&mut self, sender: &ComponentSender<Self>) -> ! {
68    ///     let fut_window = self.window.start(
69    ///         sender,
70    ///         |e| match e {
71    ///             WindowEvent::Close => Some(MainMessage::Close),
72    ///             // ignore other events
73    ///             _ => None,
74    ///         },
75    ///         // you should always propagate internal messages
76    ///         || MainMessage::Noop,
77    ///     );
78    ///     // ...other children
79    ///     futures_util::join!(fut_window, /* ... */);
80    /// }
81    /// ```
82    pub async fn start<C: Component>(
83        &mut self,
84        sender: &ComponentSender<C>,
85        mut f: impl FnMut(T::Event) -> Option<C::Message>,
86        mut propagate: impl FnMut() -> C::Message,
87    ) -> ! {
88        let fut_start = self.model.start(&self.sender);
89        let fut_forward = async {
90            loop {
91                self.sender.wait().await;
92                for msg in self.sender.fetch_all() {
93                    match msg {
94                        ComponentMessage::Message(msg) => {
95                            self.msg_cache.push(msg);
96                            sender.post(propagate());
97                        }
98                        ComponentMessage::Event(e) => {
99                            if let Some(m) = f(e) {
100                                sender.post(m);
101                            }
102                        }
103                    }
104                }
105            }
106        };
107        futures_util::future::join(fut_start, fut_forward).await.0
108    }
109
110    /// Post message to the child component.
111    pub fn post(&mut self, message: T::Message) {
112        self.sender.post(message);
113    }
114
115    /// Emit message to the child component.
116    pub async fn emit(&mut self, message: T::Message) -> Result<bool, T::Error> {
117        self.model.update(message, &self.sender).await
118    }
119
120    /// Respond to the child message.
121    pub async fn update(&mut self) -> Result<bool, T::Error> {
122        let mut need_render = self.model.update_children().await?;
123        for message in self.msg_cache.drain(..) {
124            need_render |= self.model.update(message, &self.sender).await?;
125        }
126        Ok(need_render)
127    }
128
129    /// Render the child component.
130    pub fn render(&mut self) -> Result<(), T::Error> {
131        self.model.render(&self.sender)?;
132        self.model.render_children()
133    }
134
135    /// Get the sender of the child component.
136    pub fn sender(&self) -> &ComponentSender<T> {
137        &self.sender
138    }
139
140    /// Try to convert the child component into a root component.
141    ///
142    /// It clears the inner message cache and updates the child component if
143    /// needed.
144    pub async fn try_into_root(mut self) -> Result<Root<T>, T::Error> {
145        if self.update().await? {
146            self.render()?;
147        }
148        Ok(Root::new(self.model, self.sender))
149    }
150}
151
152impl<T: Component + 'static> Child<T> {
153    /// Box the component.
154    pub fn into_boxed(self) -> Child<BoxComponent<T::Message, T::Event, T::Error>> {
155        let sender = ComponentSender(self.sender.0);
156        Child {
157            model: BoxComponent::new(self.model),
158            sender,
159            msg_cache: self.msg_cache,
160        }
161    }
162}
163
164impl<T: Component> Deref for Child<T> {
165    type Target = T;
166
167    fn deref(&self) -> &Self::Target {
168        &self.model
169    }
170}
171
172impl<T: Component> DerefMut for Child<T> {
173    fn deref_mut(&mut self) -> &mut Self::Target {
174        &mut self.model
175    }
176}
177
178#[cfg(feature = "handle")]
179impl<T: AsWindow + Component> AsWindow for Child<T> {
180    fn as_window(&self) -> BorrowedWindow<'_> {
181        self.model.as_window()
182    }
183}
184
185#[cfg(feature = "handle")]
186impl<T: AsWidget + Component> AsWidget for Child<T> {
187    fn as_widget(&self) -> BorrowedWidget<'_> {
188        self.model.as_widget()
189    }
190}
191
192#[cfg(feature = "handle")]
193impl<T: AsContainer + Component> AsContainer for Child<T> {
194    fn as_container(&self) -> BorrowedContainer<'_> {
195        self.model.as_container()
196    }
197}
198
199impl<T: Component + Debug> Debug for Child<T> {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        f.debug_struct("Child").field("model", &self.model).finish()
202    }
203}
204
205#[cfg(feature = "primitive")]
206impl<T: Component + Failable> Failable for Child<T> {
207    type Error = <T as Failable>::Error;
208}
209
210#[cfg(feature = "primitive")]
211#[inherit_methods(from = "self.model")]
212impl<T: Component + Layoutable> Layoutable for Child<T> {
213    fn loc(&self) -> Result<Point, Self::Error>;
214
215    fn set_loc(&mut self, p: Point) -> Result<(), Self::Error>;
216
217    fn size(&self) -> Result<Size, Self::Error>;
218
219    fn set_size(&mut self, s: Size) -> Result<(), Self::Error>;
220
221    fn rect(&self) -> Result<Rect, Self::Error>;
222
223    fn set_rect(&mut self, r: Rect) -> Result<(), Self::Error>;
224
225    fn preferred_size(&self) -> Result<Size, Self::Error>;
226
227    fn min_size(&self) -> Result<Size, Self::Error>;
228}