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    #[inline(always)]
25    fn compose(self) -> Composed<Self>
26    where
27        Self: Sized,
28    {
29        Composed {
30            component: self,
31            widget: None,
32        }
33    }
34}
35
36/// A [Widget] created from a [Component].
37pub struct Composed<C: Component> {
38    component: C,
39    widget: Option<BoxedWidget>,
40}
41
42impl<C: Component> Composed<C> {
43    /// Creates a new [Composed] widget from a [Component].
44    #[inline(always)]
45    pub fn new(component: C) -> Self {
46        Self {
47            component,
48            widget: None,
49        }
50    }
51}
52
53impl<C: Component> Widget for Composed<C> {
54    #[inline(always)]
55    fn render(
56        &mut self,
57        scene: &mut dyn Scene,
58        theme: &mut dyn Theme,
59        layout_node: &LayoutNode,
60        info: &AppInfo,
61        context: AppContext,
62    ) {
63        if let Some(widget) = &mut self.widget {
64            widget.render(scene, theme, layout_node, info, context)
65        } else {
66            self.widget = Some(Box::new(self.component.build(context.clone())));
67        }
68    }
69
70    #[inline(always)]
71    fn layout_style(&self) -> StyleNode {
72        if let Some(widget) = &self.widget {
73            widget.layout_style()
74        } else {
75            StyleNode {
76                style: LayoutStyle::default(),
77                children: Vec::new(),
78            }
79        }
80    }
81
82    #[inline(always)]
83    fn update(&mut self, layout: &LayoutNode, context: AppContext, info: &AppInfo) -> Update {
84        if let Some(widget) = &mut self.widget {
85            widget.update(layout, context, info)
86        } else {
87            self.widget = Some(Box::new(self.component.build(context.clone())));
88            Update::FORCE
89        }
90    }
91
92    #[inline(always)]
93    fn widget_id(&self) -> WidgetId {
94        self.component.widget_id()
95    }
96}
97
98impl<C: Component + WidgetChildrenExt> WidgetChildrenExt for Composed<C> {
99    #[inline(always)]
100    fn set_children(&mut self, children: Vec<BoxedWidget>) {
101        self.component.set_children(children)
102    }
103
104    #[inline(always)]
105    fn with_children(self, children: Vec<BoxedWidget>) -> Self
106    where
107        Self: Sized,
108    {
109        self.component.with_children(children).compose()
110    }
111
112    #[inline(always)]
113    fn add_child(&mut self, child: impl Widget + 'static) {
114        self.component.add_child(child)
115    }
116
117    #[inline(always)]
118    fn with_child(self, child: impl Widget + 'static) -> Self
119    where
120        Self: Sized,
121    {
122        self.component.with_child(child).compose()
123    }
124}
125
126impl<C: Component + WidgetChildExt> WidgetChildExt for Composed<C> {
127    #[inline(always)]
128    fn set_child(&mut self, child: impl Widget + 'static) {
129        self.component.set_child(child)
130    }
131
132    #[inline(always)]
133    fn with_child(self, child: impl Widget + 'static) -> Self
134    where
135        Self: Sized,
136    {
137        self.component.with_child(child).compose()
138    }
139}
140
141impl<C: Component + WidgetLayoutExt> WidgetLayoutExt for Composed<C> {
142    #[inline(always)]
143    fn set_layout_style(&mut self, layout_style: impl Into<MaybeSignal<LayoutStyle>>) {
144        self.component.set_layout_style(layout_style)
145    }
146
147    #[inline(always)]
148    fn with_layout_style(self, layout_style: impl Into<MaybeSignal<LayoutStyle>>) -> Self
149    where
150        Self: Sized,
151    {
152        self.component.with_layout_style(layout_style).compose()
153    }
154}
155
156impl<C: Component + Clone> Clone for Composed<C> {
157    #[inline(always)]
158    fn clone(&self) -> Self {
159        Self {
160            component: self.component.clone(),
161            widget: None,
162        }
163    }
164}
165
166impl<C: Component + Debug> Debug for Composed<C> {
167    #[inline(always)]
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        f.debug_struct("ComposedWidget")
170            .field("component", &self.component)
171            .field("widget", &self.widget.as_ref().map(|_| "?".to_string()))
172            .finish()
173    }
174}
175
176impl<C: Component> Deref for Composed<C> {
177    type Target = C;
178
179    #[inline(always)]
180    fn deref(&self) -> &Self::Target {
181        &self.component
182    }
183}
184
185impl<C: Component> DerefMut for Composed<C> {
186    #[inline(always)]
187    fn deref_mut(&mut self) -> &mut Self::Target {
188        &mut self.component
189    }
190}