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 PostMeasure,
21 Size2D,
22 },
23 scaled::Scaled,
24 torin::Torin,
25};
26
27use crate::{
28 data::{
29 AccessibilityData,
30 EffectData,
31 LayoutData,
32 StyleState,
33 TextStyleData,
34 TextStyleState,
35 },
36 diff_key::DiffKey,
37 event_handler::EventHandler,
38 events::{
39 data::{
40 Event,
41 KeyboardEventData,
42 MouseEventData,
43 PointerEventData,
44 SizedEventData,
45 TouchEventData,
46 VisibleEventData,
47 WheelEventData,
48 },
49 name::EventName,
50 },
51 layers::Layer,
52 node_id::NodeId,
53 prelude::{
54 FileEventData,
55 ImePreeditEventData,
56 MaybeExt,
57 },
58 text_cache::TextCache,
59 tree::{
60 DiffModifies,
61 Tree,
62 },
63};
64
65pub trait ElementExt: Any {
66 fn into_element(self) -> Element
67 where
68 Self: Sized + Into<Element>,
69 {
70 self.into()
71 }
72
73 fn changed(&self, _other: &Rc<dyn ElementExt>) -> bool {
74 false
75 }
76
77 fn diff(&self, _other: &Rc<dyn ElementExt>) -> DiffModifies {
78 DiffModifies::empty()
79 }
80
81 fn layout(&'_ self) -> Cow<'_, LayoutData> {
82 Cow::Owned(Default::default())
83 }
84
85 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
86 Cow::Owned(Default::default())
87 }
88
89 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
90 None
91 }
92
93 fn style(&'_ self) -> Cow<'_, StyleState> {
94 Cow::Owned(Default::default())
95 }
96
97 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
98 Cow::Owned(Default::default())
99 }
100
101 fn layer(&self) -> Layer {
102 Layer::default()
103 }
104
105 fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
106 None
107 }
108
109 fn measure(&self, _context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
110 None
111 }
112
113 fn should_hook_measurement(&self) -> bool {
114 false
115 }
116
117 fn should_measure_inner_children(&self) -> bool {
118 true
119 }
120
121 fn needs_post_measure(&self) -> bool {
123 false
124 }
125
126 fn post_measure(&self, _context: PostMeasureContext) -> PostMeasure<NodeId> {
128 PostMeasure::default()
129 }
130
131 fn is_point_inside(&self, context: EventMeasurementContext) -> bool {
132 context
133 .layout_node
134 .visible_area()
135 .contains(context.cursor.to_f32())
136 }
137
138 fn clip(&self, _context: ClipContext) {}
139
140 fn render(&self, _context: RenderContext) {}
141
142 fn render_rect(&self, area: &Area, scale_factor: f32) -> SkRRect {
143 let style = self.style();
144 let corner_radius = style.corner_radius.with_scale(scale_factor);
145 SkRRect::new_rect_radii(
146 SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
147 &[
148 (corner_radius.top_left, corner_radius.top_left).into(),
149 (corner_radius.top_right, corner_radius.top_right).into(),
150 (corner_radius.bottom_right, corner_radius.bottom_right).into(),
151 (corner_radius.bottom_left, corner_radius.bottom_left).into(),
152 ],
153 )
154 }
155}
156
157#[allow(dead_code)]
158pub struct LayoutContext<'a> {
159 pub node_id: NodeId,
160 pub torin_node: &'a torin::node::Node,
161 pub area_size: &'a Size2D,
162 pub font_collection: &'a mut FontCollection,
163 pub font_manager: &'a FontMgr,
164 pub text_style_state: &'a TextStyleState,
165 pub fallback_fonts: &'a [Cow<'static, str>],
166 pub scale_factor: f64,
167 pub text_cache: &'a mut TextCache,
168}
169
170#[allow(dead_code)]
171pub struct RenderContext<'a> {
172 pub font_collection: &'a mut FontCollection,
173 pub canvas: &'a Canvas,
174 pub layout_node: &'a LayoutNode,
175 pub text_style_state: &'a TextStyleState,
176 pub tree: &'a Tree,
177 pub scale_factor: f64,
178}
179
180pub struct EventMeasurementContext<'a> {
181 pub cursor: ragnarok::CursorPoint,
182 pub layout_node: &'a LayoutNode,
183 pub scale_factor: f64,
184}
185
186pub struct PostMeasureContext<'a> {
187 pub node_layout: &'a LayoutNode,
188 pub children: &'a [NodeId],
189 pub layout: &'a Torin<NodeId>,
190 pub font_collection: &'a mut FontCollection,
191 pub text_style_state: &'a TextStyleState,
192 pub fallback_fonts: &'a [Cow<'static, str>],
193 pub scale_factor: f64,
194}
195
196pub struct ClipContext<'a> {
197 pub canvas: &'a Canvas,
198 pub visible_area: &'a Area,
199 pub scale_factor: f64,
200}
201
202impl<T: Any + PartialEq> ComponentProps for T {
203 fn changed(&self, other: &dyn ComponentProps) -> bool {
204 let other = (other as &dyn Any).downcast_ref::<T>().unwrap();
205 self != other
206 }
207}
208
209pub trait ComponentProps: Any {
210 fn changed(&self, other: &dyn ComponentProps) -> bool;
211}
212
213#[derive(Clone)]
214pub enum Element {
215 Component {
216 key: DiffKey,
217 comp: Rc<dyn Fn(Rc<dyn ComponentProps>) -> Element>,
218 props: Rc<dyn ComponentProps>,
219 },
220 Element {
221 key: DiffKey,
222 element: Rc<dyn ElementExt>,
223 elements: Vec<Element>,
224 },
225}
226
227impl Debug for Element {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 match self {
230 Self::Element { key, elements, .. } => {
231 f.write_str(&format!("Element {{ key: {:?} }}", key))?;
232 elements.fmt(f)
233 }
234 Self::Component { key, .. } => f.write_str(&format!("Component {{ key: {:?} }}", key)),
235 }
236 }
237}
238
239pub trait IntoElement {
240 fn into_element(self) -> Element;
241}
242
243impl<T: Into<Element>> IntoElement for T {
244 fn into_element(self) -> Element {
245 self.into()
246 }
247}
248
249pub trait App: 'static {
253 fn render(&self) -> impl IntoElement;
254}
255
256#[derive(Clone)]
258pub struct AppComponent {
259 render: Rc<dyn Fn() -> Element + 'static>,
260}
261
262impl AppComponent {
263 pub fn new(render: impl App + 'static) -> Self {
264 Self {
265 render: Rc::new(move || render.render().into_element()),
266 }
267 }
268}
269
270impl PartialEq for AppComponent {
271 fn eq(&self, _other: &Self) -> bool {
272 true
273 }
274}
275
276#[cfg(feature = "hotreload")]
277impl<F, E> From<F> for AppComponent
278where
279 F: Fn() -> E + Clone + 'static,
280 E: IntoElement,
281{
282 fn from(render: F) -> Self {
283 AppComponent {
284 render: Rc::new(move || {
285 crate::hotreload::subsecond::HotFn::current(render.clone())
286 .call(())
287 .into_element()
288 }),
289 }
290 }
291}
292
293#[cfg(not(feature = "hotreload"))]
294impl<F, E> From<F> for AppComponent
295where
296 F: Fn() -> E + 'static,
297 E: IntoElement,
298{
299 fn from(render: F) -> Self {
300 AppComponent {
301 render: Rc::new(move || render().into_element()),
302 }
303 }
304}
305
306impl Component for AppComponent {
307 fn render(&self) -> impl IntoElement {
308 (self.render)()
309 }
310}
311
312pub trait Component: ComponentKey + PartialEq + 'static {
334 fn render(&self) -> impl IntoElement;
335
336 fn render_key(&self) -> DiffKey {
337 self.default_key()
338 }
339}
340
341pub trait ComponentOwned: ComponentKey + PartialEq + 'static {
342 fn render(self) -> impl IntoElement;
343
344 fn render_key(&self) -> DiffKey {
345 self.default_key()
346 }
347}
348
349pub trait ComponentKey {
350 fn default_key(&self) -> DiffKey;
351}
352
353impl<T> Component for T
354where
355 T: ComponentOwned + Clone + PartialEq,
356{
357 fn render(&self) -> impl IntoElement {
358 <Self as ComponentOwned>::render(self.clone())
359 }
360 fn render_key(&self) -> DiffKey {
361 <Self as ComponentOwned>::render_key(self)
362 }
363}
364
365impl<T> ComponentKey for T
366where
367 T: Component,
368{
369 fn default_key(&self) -> DiffKey {
370 DiffKey::DefaultU64(Self::render as *const () as u64)
371 }
372}
373
374impl<T> MaybeExt for T where T: Component {}
375
376impl<T: Component> From<T> for Element {
377 fn from(value: T) -> Self {
378 let key = value.render_key();
379 Element::Component {
380 key,
381 #[cfg(feature = "hotreload")]
382 comp: Rc::new(move |props| {
383 let props = (&*props as &dyn Any).downcast_ref::<T>().unwrap();
384 crate::hotreload::subsecond::HotFn::current(|v: &T| v.render().into_element())
385 .call((props,))
386 }),
387 #[cfg(not(feature = "hotreload"))]
388 comp: Rc::new(move |props| {
389 let props = (&*props as &dyn Any).downcast_ref::<T>().unwrap();
390 props.render().into_element()
391 }),
392 props: Rc::new(value),
393 }
394 }
395}
396
397impl PartialEq for Element {
398 fn eq(&self, other: &Self) -> bool {
399 match (self, other) {
400 (
401 Self::Component {
402 key: key1,
403 props: props1,
404 ..
405 },
406 Self::Component {
407 key: key2,
408 props: props2,
409 ..
410 },
411 ) => key1 == key2 && !props1.changed(props2.as_ref()),
412 (
413 Self::Element {
414 key: key1,
415 element: element1,
416 elements: elements1,
417 },
418 Self::Element {
419 key: key2,
420 element: element2,
421 elements: elements2,
422 },
423 ) => key1 == key2 && !element1.changed(element2) && elements1 == elements2,
424 _ => false,
425 }
426 }
427}
428
429#[derive(Clone, PartialEq)]
430pub enum EventHandlerType {
431 Mouse(EventHandler<Event<MouseEventData>>),
432 Keyboard(EventHandler<Event<KeyboardEventData>>),
433 Sized(EventHandler<Event<SizedEventData>>),
434 Visible(EventHandler<Event<VisibleEventData>>),
435 Wheel(EventHandler<Event<WheelEventData>>),
436 Touch(EventHandler<Event<TouchEventData>>),
437 Pointer(EventHandler<Event<PointerEventData>>),
438 ImePreedit(EventHandler<Event<ImePreeditEventData>>),
439 File(EventHandler<Event<FileEventData>>),
440}