1use std::any::{type_name, Any, TypeId};
4use std::fmt;
5
6use glam::Vec2;
7
8use crate::dom::Dom;
9use crate::event::EventResponse;
10use crate::event::{EventInterest, WidgetEvent};
11use crate::geometry::{Constraints, FlexFit};
12use crate::input::{InputState, NavDirection};
13use crate::layout::LayoutDom;
14use crate::paint::PaintDom;
15use crate::{Flow, WidgetId};
16
17pub trait Props: fmt::Debug {}
21impl<T> Props for T where T: fmt::Debug {}
22
23#[non_exhaustive]
25#[allow(missing_docs)]
26pub struct LayoutContext<'dom> {
27 pub dom: &'dom Dom,
28 pub input: &'dom InputState,
29 pub layout: &'dom mut LayoutDom,
30}
31
32impl<'dom> LayoutContext<'dom> {
33 pub fn calculate_layout(&mut self, widget: WidgetId, constraints: Constraints) -> Vec2 {
38 self.layout
39 .calculate(self.dom, self.input, widget, constraints)
40 }
41}
42
43#[non_exhaustive]
45#[allow(missing_docs)]
46pub struct PaintContext<'dom> {
47 pub dom: &'dom Dom,
48 pub layout: &'dom LayoutDom,
49 pub paint: &'dom mut PaintDom,
50}
51
52impl<'dom> PaintContext<'dom> {
53 pub fn paint(&mut self, widget: WidgetId) {
55 self.paint.paint(self.dom, self.layout, widget);
56 }
57}
58
59#[non_exhaustive]
61#[allow(missing_docs)]
62pub struct EventContext<'dom> {
63 pub dom: &'dom Dom,
64 pub layout: &'dom LayoutDom,
65 pub input: &'dom InputState,
66}
67
68#[non_exhaustive]
70#[allow(missing_docs)]
71pub struct NavigateContext<'dom> {
72 pub dom: &'dom Dom,
73 pub layout: &'dom LayoutDom,
74 pub input: &'dom InputState,
75}
76
77pub trait Widget: 'static + fmt::Debug {
80 type Props<'a>: Props;
83
84 type Response;
88
89 fn new() -> Self;
91
92 fn update(&mut self, props: Self::Props<'_>) -> Self::Response;
94
95 fn flex(&self) -> (u32, FlexFit) {
102 (0, FlexFit::Loose)
103 }
104
105 fn flow(&self) -> Flow {
109 Flow::Inline
110 }
111
112 fn layout(&self, ctx: LayoutContext<'_>, constraints: Constraints) -> Vec2 {
119 self.default_layout(ctx, constraints)
120 }
121
122 #[inline]
126 fn default_layout(&self, mut ctx: LayoutContext<'_>, constraints: Constraints) -> Vec2 {
127 let node = ctx.dom.get_current();
128 let mut size = Vec2::ZERO;
129 for &child in &node.children {
130 let child_size = ctx.calculate_layout(child, constraints);
131 size = size.max(child_size);
132 }
133
134 constraints.constrain_min(size)
135 }
136
137 fn paint(&self, ctx: PaintContext<'_>) {
141 self.default_paint(ctx);
142 }
143
144 #[inline]
148 fn default_paint(&self, mut ctx: PaintContext<'_>) {
149 let node = ctx.dom.get_current();
150 for &child in &node.children {
151 ctx.paint(child);
152 }
153 }
154
155 fn event_interest(&self) -> EventInterest {
159 EventInterest::empty()
160 }
161
162 #[allow(unused)]
166 fn event(&mut self, ctx: EventContext<'_>, event: &WidgetEvent) -> EventResponse {
167 EventResponse::Bubble
168 }
169
170 #[allow(unused)]
173 fn navigate(&self, ctx: NavigateContext<'_>, dir: NavDirection) -> Option<WidgetId> {
174 None
175 }
176}
177
178pub trait ErasedWidget: Any + fmt::Debug {
180 fn layout(&self, ctx: LayoutContext<'_>, constraints: Constraints) -> Vec2;
182
183 fn flex(&self) -> (u32, FlexFit);
185
186 fn flow(&self) -> Flow;
188
189 fn paint(&self, ctx: PaintContext<'_>);
191
192 fn event_interest(&self) -> EventInterest;
194
195 fn event(&mut self, ctx: EventContext<'_>, event: &WidgetEvent) -> EventResponse;
197
198 fn type_name(&self) -> &'static str;
200}
201
202impl<T> ErasedWidget for T
203where
204 T: Widget,
205{
206 fn layout(&self, ctx: LayoutContext<'_>, constraints: Constraints) -> Vec2 {
207 <T as Widget>::layout(self, ctx, constraints)
208 }
209
210 fn flex(&self) -> (u32, FlexFit) {
211 <T as Widget>::flex(self)
212 }
213
214 fn flow(&self) -> Flow {
215 <T as Widget>::flow(self)
216 }
217
218 fn paint(&self, ctx: PaintContext<'_>) {
219 <T as Widget>::paint(self, ctx)
220 }
221
222 fn event_interest(&self) -> EventInterest {
223 <T as Widget>::event_interest(self)
224 }
225
226 fn event(&mut self, ctx: EventContext<'_>, event: &WidgetEvent) -> EventResponse {
227 log::debug!("Event on {}: {event:?}", type_name::<T>());
228
229 <T as Widget>::event(self, ctx, event)
230 }
231
232 fn type_name(&self) -> &'static str {
233 type_name::<T>()
234 }
235}
236
237mopmopafy!(ErasedWidget);