freya_core/
element.rs

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};
13use rustc_hash::FxHashMap;
14use torin::prelude::{
15    Area,
16    LayoutNode,
17    Size2D,
18};
19
20use crate::{
21    data::{
22        AccessibilityData,
23        EffectData,
24        LayoutData,
25        StyleState,
26        TextStyleData,
27        TextStyleState,
28    },
29    diff_key::DiffKey,
30    event_handler::EventHandler,
31    events::{
32        data::{
33            Event,
34            KeyboardEventData,
35            MouseEventData,
36            PointerEventData,
37            SizedEventData,
38            TouchEventData,
39            WheelEventData,
40        },
41        name::EventName,
42    },
43    helpers::from_fn_standalone_borrowed_keyed,
44    node_id::NodeId,
45    prelude::{
46        FileEventData,
47        ImePreeditEventData,
48    },
49    text_cache::TextCache,
50    tree::{
51        DiffModifies,
52        Tree,
53    },
54};
55
56pub trait ElementExt: Any {
57    fn into_element(self) -> Element
58    where
59        Self: Sized + Into<Element>,
60    {
61        self.into()
62    }
63
64    fn changed(&self, _other: &Rc<dyn ElementExt>) -> bool {
65        false
66    }
67
68    fn diff(&self, _other: &Rc<dyn ElementExt>) -> DiffModifies {
69        DiffModifies::empty()
70    }
71
72    fn layout(&'_ self) -> Cow<'_, LayoutData> {
73        Cow::Owned(Default::default())
74    }
75
76    fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
77        Cow::Owned(Default::default())
78    }
79
80    fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
81        None
82    }
83
84    fn style(&'_ self) -> Cow<'_, StyleState> {
85        Cow::Owned(Default::default())
86    }
87
88    fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
89        Cow::Owned(Default::default())
90    }
91
92    fn relative_layer(&self) -> i16 {
93        0
94    }
95
96    fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
97        None
98    }
99
100    fn measure(&self, _context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
101        None
102    }
103
104    fn should_hook_measurement(&self) -> bool {
105        false
106    }
107
108    fn should_measure_inner_children(&self) -> bool {
109        true
110    }
111
112    fn is_point_inside(&self, context: EventMeasurementContext) -> bool {
113        context
114            .layout_node
115            .visible_area()
116            .contains(context.cursor.to_f32())
117    }
118
119    fn clip(&self, _context: ClipContext) {}
120
121    fn render(&self, _context: RenderContext) {}
122}
123
124#[allow(dead_code)]
125pub struct LayoutContext<'a> {
126    pub node_id: NodeId,
127    pub torin_node: &'a torin::node::Node,
128    pub area_size: &'a Size2D,
129    pub font_collection: &'a FontCollection,
130    pub font_manager: &'a FontMgr,
131    pub text_style_state: &'a TextStyleState,
132    pub fallback_fonts: &'a [Cow<'static, str>],
133    pub scale_factor: f64,
134    pub text_cache: &'a mut TextCache,
135}
136
137#[allow(dead_code)]
138pub struct RenderContext<'a> {
139    pub font_collection: &'a mut FontCollection,
140    pub canvas: &'a Canvas,
141    pub layout_node: &'a LayoutNode,
142    pub text_style_state: &'a TextStyleState,
143    pub tree: &'a Tree,
144    pub scale_factor: f64,
145}
146
147pub struct EventMeasurementContext<'a> {
148    pub cursor: ragnarok::CursorPoint,
149    pub layout_node: &'a LayoutNode,
150    pub scale_factor: f64,
151}
152
153pub struct ClipContext<'a> {
154    pub canvas: &'a Canvas,
155    pub visible_area: &'a Area,
156    pub scale_factor: f64,
157}
158
159impl<T: Any + PartialEq> ComponentProps for T {
160    fn changed(&self, other: &dyn ComponentProps) -> bool {
161        let other = (other as &dyn Any).downcast_ref::<T>().unwrap();
162        self != other
163    }
164}
165
166pub trait ComponentProps: Any {
167    fn changed(&self, other: &dyn ComponentProps) -> bool;
168}
169
170#[derive(Clone)]
171pub enum Element {
172    Component {
173        key: DiffKey,
174
175        comp: Rc<dyn Fn(Rc<dyn ComponentProps>) -> Element>,
176
177        props: Rc<dyn ComponentProps>,
178    },
179    Element {
180        key: DiffKey,
181        element: Rc<dyn ElementExt>,
182        elements: Vec<Element>,
183    },
184}
185
186impl Debug for Element {
187    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188        match self {
189            Self::Element { key, elements, .. } => {
190                f.write_str(&format!("Element {{ key: {:?} }}", key))?;
191                elements.fmt(f)
192            }
193            Self::Component { key, .. } => f.write_str(&format!("Component {{ key: {:?} }}", key)),
194        }
195    }
196}
197
198pub trait IntoElement {
199    fn into_element(self) -> Element;
200}
201
202impl<T: Into<Element>> IntoElement for T {
203    fn into_element(self) -> Element {
204        self.into()
205    }
206}
207
208#[derive(Clone)]
209pub struct FpRender {
210    render: Rc<dyn Fn() -> Element + 'static>,
211}
212
213impl FpRender {
214    pub fn from_render(render: impl Render + 'static) -> Self {
215        Self {
216            render: Rc::new(move || render.render().into_element()),
217        }
218    }
219}
220
221impl PartialEq for FpRender {
222    fn eq(&self, _other: &Self) -> bool {
223        true
224    }
225}
226
227impl<F, E> From<F> for FpRender
228where
229    F: Fn() -> E + 'static,
230    E: IntoElement,
231{
232    fn from(render: F) -> Self {
233        FpRender {
234            render: Rc::new(move || render().into_element()),
235        }
236    }
237}
238
239impl Render for FpRender {
240    fn render(&self) -> impl IntoElement {
241        (self.render)()
242    }
243}
244
245pub trait Render: RenderKey + 'static {
246    fn render(&self) -> impl IntoElement;
247
248    fn render_key(&self) -> DiffKey {
249        self.default_key()
250    }
251}
252
253pub trait RenderOwned: RenderKey + 'static {
254    fn render(self) -> impl IntoElement;
255
256    fn render_key(&self) -> DiffKey {
257        self.default_key()
258    }
259}
260
261pub trait RenderKey {
262    fn default_key(&self) -> DiffKey;
263}
264
265impl<T> Render for T
266where
267    T: RenderOwned + Clone,
268{
269    fn render(&self) -> impl IntoElement {
270        <Self as RenderOwned>::render(self.clone())
271    }
272    fn render_key(&self) -> DiffKey {
273        <Self as RenderOwned>::render_key(self)
274    }
275}
276
277impl<T> RenderKey for T
278where
279    T: Render,
280{
281    fn default_key(&self) -> DiffKey {
282        DiffKey::U64(Self::render as *const () as u64)
283    }
284}
285
286impl<T: Render + PartialEq> From<T> for Element {
287    fn from(value: T) -> Self {
288        from_fn_standalone_borrowed_keyed(value.render_key(), value, |v| v.render().into_element())
289    }
290}
291
292impl PartialEq for Element {
293    fn eq(&self, other: &Self) -> bool {
294        match (self, other) {
295            (
296                Self::Component {
297                    key: key1,
298                    props: props1,
299                    ..
300                },
301                Self::Component {
302                    key: key2,
303                    props: props2,
304                    ..
305                },
306            ) => key1 == key2 && !props1.changed(props2.as_ref()),
307            (
308                Self::Element {
309                    key: key1,
310                    element: element1,
311                    elements: elements1,
312                },
313                Self::Element {
314                    key: key2,
315                    element: element2,
316                    elements: elements2,
317                },
318            ) => key1 == key2 && !element1.changed(element2) && elements1 == elements2,
319            _ => false,
320        }
321    }
322}
323
324#[derive(Clone, PartialEq)]
325pub enum EventHandlerType {
326    Mouse(EventHandler<Event<MouseEventData>>),
327    Keyboard(EventHandler<Event<KeyboardEventData>>),
328    Sized(EventHandler<Event<SizedEventData>>),
329    Wheel(EventHandler<Event<WheelEventData>>),
330    Touch(EventHandler<Event<TouchEventData>>),
331    Pointer(EventHandler<Event<PointerEventData>>),
332    ImePreedit(EventHandler<Event<ImePreeditEventData>>),
333    File(EventHandler<Event<FileEventData>>),
334}