maycoon_core/
component.rs

1use crate::app::context::AppContext;
2use crate::app::info::AppInfo;
3use crate::app::update::Update;
4use crate::layout::{LayoutNode, LayoutStyle, StyleNode};
5use crate::signal::MaybeSignal;
6use crate::vgi::Scene;
7use crate::widget::{BoxedWidget, Widget, WidgetChildExt, WidgetChildrenExt, WidgetLayoutExt};
8use maycoon_theme::id::WidgetId;
9use maycoon_theme::theme::Theme;
10use std::fmt::Debug;
11use std::ops::{Deref, DerefMut};
12
13/// A trait for creating a [Widget] from simple functions.
14///
15/// Simplifies the creation of your own widgets.
16pub trait Component {
17    /// Builds the inner widget.
18    fn build(&self, context: AppContext) -> impl Widget + 'static;
19
20    /// The id of this widget/component.
21    fn widget_id(&self) -> WidgetId;
22
23    /// Composes this component into a [Widget] using [ComposedWidget].
24    fn compose(self) -> Composed<Self>
25    where
26        Self: Sized,
27    {
28        Composed {
29            component: self,
30            widget: None,
31        }
32    }
33}
34
35/// A [Widget] created from a [Component].
36pub struct Composed<C: Component> {
37    component: C,
38    widget: Option<BoxedWidget>,
39}
40
41impl<C: Component> Composed<C> {
42    /// Creates a new [Composed] widget from a [Component].
43    pub fn new(component: C) -> Self {
44        Self {
45            component,
46            widget: None,
47        }
48    }
49}
50
51impl<C: Component> Widget for Composed<C> {
52    fn render(
53        &mut self,
54        scene: &mut dyn Scene,
55        theme: &mut dyn Theme,
56        layout_node: &LayoutNode,
57        info: &AppInfo,
58        context: AppContext,
59    ) {
60        if let Some(widget) = &mut self.widget {
61            widget.render(scene, theme, layout_node, info, context)
62        } else {
63            self.widget = Some(Box::new(self.component.build(context.clone())));
64        }
65    }
66
67    fn layout_style(&self) -> StyleNode {
68        if let Some(widget) = &self.widget {
69            widget.layout_style()
70        } else {
71            StyleNode {
72                style: LayoutStyle::default(),
73                children: Vec::new(),
74            }
75        }
76    }
77
78    fn update(&mut self, layout: &LayoutNode, context: AppContext, info: &AppInfo) -> Update {
79        if let Some(widget) = &mut self.widget {
80            widget.update(layout, context, info)
81        } else {
82            self.widget = Some(Box::new(self.component.build(context.clone())));
83            Update::FORCE
84        }
85    }
86
87    fn widget_id(&self) -> WidgetId {
88        self.component.widget_id()
89    }
90}
91
92impl<C: Component + WidgetChildrenExt> WidgetChildrenExt for Composed<C> {
93    fn set_children(&mut self, children: Vec<BoxedWidget>) {
94        self.component.set_children(children)
95    }
96
97    fn with_children(self, children: Vec<BoxedWidget>) -> Self
98    where
99        Self: Sized,
100    {
101        self.component.with_children(children).compose()
102    }
103
104    fn add_child(&mut self, child: impl Widget + 'static) {
105        self.component.add_child(child)
106    }
107
108    fn with_child(self, child: impl Widget + 'static) -> Self
109    where
110        Self: Sized,
111    {
112        self.component.with_child(child).compose()
113    }
114}
115
116impl<C: Component + WidgetChildExt> WidgetChildExt for Composed<C> {
117    fn set_child(&mut self, child: impl Widget + 'static) {
118        self.component.set_child(child)
119    }
120
121    fn with_child(self, child: impl Widget + 'static) -> Self
122    where
123        Self: Sized,
124    {
125        self.component.with_child(child).compose()
126    }
127}
128
129impl<C: Component + WidgetLayoutExt> WidgetLayoutExt for Composed<C> {
130    fn set_layout_style(&mut self, layout_style: impl Into<MaybeSignal<LayoutStyle>>) {
131        self.component.set_layout_style(layout_style)
132    }
133
134    fn with_layout_style(self, layout_style: impl Into<MaybeSignal<LayoutStyle>>) -> Self
135    where
136        Self: Sized,
137    {
138        self.component.with_layout_style(layout_style).compose()
139    }
140}
141
142impl<C: Component + Clone> Clone for Composed<C> {
143    fn clone(&self) -> Self {
144        Self {
145            component: self.component.clone(),
146            widget: None,
147        }
148    }
149}
150
151impl<C: Component + Debug> Debug for Composed<C> {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        f.debug_struct("ComposedWidget")
154            .field("component", &self.component)
155            .field("widget", &self.widget.as_ref().map(|_| "?".to_string()))
156            .finish()
157    }
158}
159
160impl<C: Component> Deref for Composed<C> {
161    type Target = C;
162
163    fn deref(&self) -> &Self::Target {
164        &self.component
165    }
166}
167
168impl<C: Component> DerefMut for Composed<C> {
169    fn deref_mut(&mut self) -> &mut Self::Target {
170        &mut self.component
171    }
172}