1use std::{
2 any::Any,
3 borrow::Cow,
4 fmt::Debug,
5 rc::Rc,
6};
7
8use freya_engine::prelude::{
9 Canvas,
10 FontCollection,
11 FontMgr,
12 SkRRect,
13 SkRect,
14};
15use rustc_hash::FxHashMap;
16use torin::{
17 prelude::{
18 Area,
19 LayoutNode,
20 Size2D,
21 },
22 scaled::Scaled,
23};
24
25use crate::{
26 data::{
27 AccessibilityData,
28 EffectData,
29 LayoutData,
30 StyleState,
31 TextStyleData,
32 TextStyleState,
33 },
34 diff_key::DiffKey,
35 event_handler::EventHandler,
36 events::{
37 data::{
38 Event,
39 KeyboardEventData,
40 MouseEventData,
41 PointerEventData,
42 SizedEventData,
43 TouchEventData,
44 WheelEventData,
45 },
46 name::EventName,
47 },
48 helpers::from_fn_standalone_borrowed_keyed,
49 layers::Layer,
50 node_id::NodeId,
51 prelude::{
52 FileEventData,
53 ImePreeditEventData,
54 MaybeExt,
55 },
56 text_cache::TextCache,
57 tree::{
58 DiffModifies,
59 Tree,
60 },
61};
62
63pub trait ElementExt: Any {
64 fn into_element(self) -> Element
65 where
66 Self: Sized + Into<Element>,
67 {
68 self.into()
69 }
70
71 fn changed(&self, _other: &Rc<dyn ElementExt>) -> bool {
72 false
73 }
74
75 fn diff(&self, _other: &Rc<dyn ElementExt>) -> DiffModifies {
76 DiffModifies::empty()
77 }
78
79 fn layout(&'_ self) -> Cow<'_, LayoutData> {
80 Cow::Owned(Default::default())
81 }
82
83 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
84 Cow::Owned(Default::default())
85 }
86
87 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
88 None
89 }
90
91 fn style(&'_ self) -> Cow<'_, StyleState> {
92 Cow::Owned(Default::default())
93 }
94
95 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
96 Cow::Owned(Default::default())
97 }
98
99 fn layer(&self) -> Layer {
100 Layer::default()
101 }
102
103 fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
104 None
105 }
106
107 fn measure(&self, _context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
108 None
109 }
110
111 fn should_hook_measurement(&self) -> bool {
112 false
113 }
114
115 fn should_measure_inner_children(&self) -> bool {
116 true
117 }
118
119 fn is_point_inside(&self, context: EventMeasurementContext) -> bool {
120 context
121 .layout_node
122 .visible_area()
123 .contains(context.cursor.to_f32())
124 }
125
126 fn clip(&self, _context: ClipContext) {}
127
128 fn render(&self, _context: RenderContext) {}
129
130 fn render_rect(&self, area: &Area, scale_factor: f32) -> SkRRect {
131 let style = self.style();
132 let corner_radius = style.corner_radius.with_scale(scale_factor);
133 SkRRect::new_rect_radii(
134 SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
135 &[
136 (corner_radius.top_left, corner_radius.top_left).into(),
137 (corner_radius.top_right, corner_radius.top_right).into(),
138 (corner_radius.bottom_right, corner_radius.bottom_right).into(),
139 (corner_radius.bottom_left, corner_radius.bottom_left).into(),
140 ],
141 )
142 }
143}
144
145#[allow(dead_code)]
146pub struct LayoutContext<'a> {
147 pub node_id: NodeId,
148 pub torin_node: &'a torin::node::Node,
149 pub area_size: &'a Size2D,
150 pub font_collection: &'a FontCollection,
151 pub font_manager: &'a FontMgr,
152 pub text_style_state: &'a TextStyleState,
153 pub fallback_fonts: &'a [Cow<'static, str>],
154 pub scale_factor: f64,
155 pub text_cache: &'a mut TextCache,
156}
157
158#[allow(dead_code)]
159pub struct RenderContext<'a> {
160 pub font_collection: &'a mut FontCollection,
161 pub canvas: &'a Canvas,
162 pub layout_node: &'a LayoutNode,
163 pub text_style_state: &'a TextStyleState,
164 pub tree: &'a Tree,
165 pub scale_factor: f64,
166}
167
168pub struct EventMeasurementContext<'a> {
169 pub cursor: ragnarok::CursorPoint,
170 pub layout_node: &'a LayoutNode,
171 pub scale_factor: f64,
172}
173
174pub struct ClipContext<'a> {
175 pub canvas: &'a Canvas,
176 pub visible_area: &'a Area,
177 pub scale_factor: f64,
178}
179
180impl<T: Any + PartialEq> ComponentProps for T {
181 fn changed(&self, other: &dyn ComponentProps) -> bool {
182 let other = (other as &dyn Any).downcast_ref::<T>().unwrap();
183 self != other
184 }
185}
186
187pub trait ComponentProps: Any {
188 fn changed(&self, other: &dyn ComponentProps) -> bool;
189}
190
191#[derive(Clone)]
192pub enum Element {
193 Component {
194 key: DiffKey,
195 comp: Rc<dyn Fn(Rc<dyn ComponentProps>) -> Element>,
196 props: Rc<dyn ComponentProps>,
197 },
198 Element {
199 key: DiffKey,
200 element: Rc<dyn ElementExt>,
201 elements: Vec<Element>,
202 },
203}
204
205impl Debug for Element {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 match self {
208 Self::Element { key, elements, .. } => {
209 f.write_str(&format!("Element {{ key: {:?} }}", key))?;
210 elements.fmt(f)
211 }
212 Self::Component { key, .. } => f.write_str(&format!("Component {{ key: {:?} }}", key)),
213 }
214 }
215}
216
217pub trait IntoElement {
218 fn into_element(self) -> Element;
219}
220
221impl<T: Into<Element>> IntoElement for T {
222 fn into_element(self) -> Element {
223 self.into()
224 }
225}
226
227pub trait App: 'static {
231 fn render(&self) -> impl IntoElement;
232}
233
234#[derive(Clone)]
236pub struct AppComponent {
237 render: Rc<dyn Fn() -> Element + 'static>,
238}
239
240impl AppComponent {
241 pub fn new(render: impl App + 'static) -> Self {
242 Self {
243 render: Rc::new(move || render.render().into_element()),
244 }
245 }
246}
247
248impl PartialEq for AppComponent {
249 fn eq(&self, _other: &Self) -> bool {
250 true
251 }
252}
253
254impl<F, E> From<F> for AppComponent
255where
256 F: Fn() -> E + 'static,
257 E: IntoElement,
258{
259 fn from(render: F) -> Self {
260 AppComponent {
261 render: Rc::new(move || render().into_element()),
262 }
263 }
264}
265
266impl Component for AppComponent {
267 fn render(&self) -> impl IntoElement {
268 (self.render)()
269 }
270}
271
272pub trait Component: ComponentKey + PartialEq + 'static {
294 fn render(&self) -> impl IntoElement;
295
296 fn render_key(&self) -> DiffKey {
297 self.default_key()
298 }
299}
300
301pub trait ComponentOwned: ComponentKey + PartialEq + 'static {
302 fn render(self) -> impl IntoElement;
303
304 fn render_key(&self) -> DiffKey {
305 self.default_key()
306 }
307}
308
309pub trait ComponentKey {
310 fn default_key(&self) -> DiffKey;
311}
312
313impl<T> Component for T
314where
315 T: ComponentOwned + Clone + PartialEq,
316{
317 fn render(&self) -> impl IntoElement {
318 <Self as ComponentOwned>::render(self.clone())
319 }
320 fn render_key(&self) -> DiffKey {
321 <Self as ComponentOwned>::render_key(self)
322 }
323}
324
325impl<T> ComponentKey for T
326where
327 T: Component,
328{
329 fn default_key(&self) -> DiffKey {
330 DiffKey::U64(Self::render as *const () as u64)
331 }
332}
333
334impl<T> MaybeExt for T where T: Component {}
335
336impl<T: Component> From<T> for Element {
337 fn from(value: T) -> Self {
338 from_fn_standalone_borrowed_keyed(value.render_key(), value, |v| v.render().into_element())
339 }
340}
341
342impl PartialEq for Element {
343 fn eq(&self, other: &Self) -> bool {
344 match (self, other) {
345 (
346 Self::Component {
347 key: key1,
348 props: props1,
349 ..
350 },
351 Self::Component {
352 key: key2,
353 props: props2,
354 ..
355 },
356 ) => key1 == key2 && !props1.changed(props2.as_ref()),
357 (
358 Self::Element {
359 key: key1,
360 element: element1,
361 elements: elements1,
362 },
363 Self::Element {
364 key: key2,
365 element: element2,
366 elements: elements2,
367 },
368 ) => key1 == key2 && !element1.changed(element2) && elements1 == elements2,
369 _ => false,
370 }
371 }
372}
373
374#[derive(Clone, PartialEq)]
375pub enum EventHandlerType {
376 Mouse(EventHandler<Event<MouseEventData>>),
377 Keyboard(EventHandler<Event<KeyboardEventData>>),
378 Sized(EventHandler<Event<SizedEventData>>),
379 Wheel(EventHandler<Event<WheelEventData>>),
380 Touch(EventHandler<Event<TouchEventData>>),
381 Pointer(EventHandler<Event<PointerEventData>>),
382 ImePreedit(EventHandler<Event<ImePreeditEventData>>),
383 File(EventHandler<Event<FileEventData>>),
384}