maycoon_core/
component.rs1use 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
13pub trait Component {
17 fn build(&self, context: AppContext) -> impl Widget + 'static;
19
20 fn widget_id(&self) -> WidgetId;
22
23 fn compose(self) -> Composed<Self>
25 where
26 Self: Sized,
27 {
28 Composed {
29 component: self,
30 widget: None,
31 }
32 }
33}
34
35pub struct Composed<C: Component> {
37 component: C,
38 widget: Option<BoxedWidget>,
39}
40
41impl<C: Component> Composed<C> {
42 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}