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    layers::Layer,
45    node_id::NodeId,
46    prelude::{
47        FileEventData,
48        ImePreeditEventData,
49        MaybeExt,
50    },
51    text_cache::TextCache,
52    tree::{
53        DiffModifies,
54        Tree,
55    },
56};
57
58pub trait ElementExt: Any {
59    fn into_element(self) -> Element
60    where
61        Self: Sized + Into<Element>,
62    {
63        self.into()
64    }
65
66    fn changed(&self, _other: &Rc<dyn ElementExt>) -> bool {
67        false
68    }
69
70    fn diff(&self, _other: &Rc<dyn ElementExt>) -> DiffModifies {
71        DiffModifies::empty()
72    }
73
74    fn layout(&'_ self) -> Cow<'_, LayoutData> {
75        Cow::Owned(Default::default())
76    }
77
78    fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
79        Cow::Owned(Default::default())
80    }
81
82    fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
83        None
84    }
85
86    fn style(&'_ self) -> Cow<'_, StyleState> {
87        Cow::Owned(Default::default())
88    }
89
90    fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
91        Cow::Owned(Default::default())
92    }
93
94    fn layer(&self) -> Layer {
95        Layer::default()
96    }
97
98    fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
99        None
100    }
101
102    fn measure(&self, _context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
103        None
104    }
105
106    fn should_hook_measurement(&self) -> bool {
107        false
108    }
109
110    fn should_measure_inner_children(&self) -> bool {
111        true
112    }
113
114    fn is_point_inside(&self, context: EventMeasurementContext) -> bool {
115        context
116            .layout_node
117            .visible_area()
118            .contains(context.cursor.to_f32())
119    }
120
121    fn clip(&self, _context: ClipContext) {}
122
123    fn render(&self, _context: RenderContext) {}
124}
125
126#[allow(dead_code)]
127pub struct LayoutContext<'a> {
128    pub node_id: NodeId,
129    pub torin_node: &'a torin::node::Node,
130    pub area_size: &'a Size2D,
131    pub font_collection: &'a FontCollection,
132    pub font_manager: &'a FontMgr,
133    pub text_style_state: &'a TextStyleState,
134    pub fallback_fonts: &'a [Cow<'static, str>],
135    pub scale_factor: f64,
136    pub text_cache: &'a mut TextCache,
137}
138
139#[allow(dead_code)]
140pub struct RenderContext<'a> {
141    pub font_collection: &'a mut FontCollection,
142    pub canvas: &'a Canvas,
143    pub layout_node: &'a LayoutNode,
144    pub text_style_state: &'a TextStyleState,
145    pub tree: &'a Tree,
146    pub scale_factor: f64,
147}
148
149pub struct EventMeasurementContext<'a> {
150    pub cursor: ragnarok::CursorPoint,
151    pub layout_node: &'a LayoutNode,
152    pub scale_factor: f64,
153}
154
155pub struct ClipContext<'a> {
156    pub canvas: &'a Canvas,
157    pub visible_area: &'a Area,
158    pub scale_factor: f64,
159}
160
161impl<T: Any + PartialEq> ComponentProps for T {
162    fn changed(&self, other: &dyn ComponentProps) -> bool {
163        let other = (other as &dyn Any).downcast_ref::<T>().unwrap();
164        self != other
165    }
166}
167
168pub trait ComponentProps: Any {
169    fn changed(&self, other: &dyn ComponentProps) -> bool;
170}
171
172#[derive(Clone)]
173pub enum Element {
174    Component {
175        key: DiffKey,
176        comp: Rc<dyn Fn(Rc<dyn ComponentProps>) -> Element>,
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/// [AppComponent] is a wrapper for [Component]s that returns true in equality checks.
209#[derive(Clone)]
210pub struct AppComponent {
211    render: Rc<dyn Fn() -> Element + 'static>,
212}
213
214impl AppComponent {
215    pub fn new(render: impl Component + 'static) -> Self {
216        Self {
217            render: Rc::new(move || render.render().into_element()),
218        }
219    }
220}
221
222impl PartialEq for AppComponent {
223    fn eq(&self, _other: &Self) -> bool {
224        true
225    }
226}
227
228impl<F, E> From<F> for AppComponent
229where
230    F: Fn() -> E + 'static,
231    E: IntoElement,
232{
233    fn from(render: F) -> Self {
234        AppComponent {
235            render: Rc::new(move || render().into_element()),
236        }
237    }
238}
239
240impl Component for AppComponent {
241    fn render(&self) -> impl IntoElement {
242        (self.render)()
243    }
244}
245
246/// Encapsulate reusable pieces of UI by using the [Component] trait.
247/// Every [Component] creates a new layer of state in the app,
248/// meaning that implementors of [Component] can make use of hooks in their [Component::render] method.
249/// ```rust, no_run
250/// # use freya::prelude::*;
251/// #[derive(PartialEq)]
252/// struct ReusableCounter {
253///     pub init_number: u8,
254/// }
255///
256/// impl Component for ReusableCounter {
257///     fn render(&self) -> impl IntoElement {
258///         let mut number = use_state(|| self.init_number);
259///         label()
260///             .on_press(move |_| {
261///                 *number.write() += 1;
262///             })
263///             .text(number.read().to_string())
264///     }
265/// }
266/// ```
267pub trait Component: ComponentKey + 'static {
268    fn render(&self) -> impl IntoElement;
269
270    fn render_key(&self) -> DiffKey {
271        self.default_key()
272    }
273}
274
275pub trait ComponentOwned: ComponentKey + 'static {
276    fn render(self) -> impl IntoElement;
277
278    fn render_key(&self) -> DiffKey {
279        self.default_key()
280    }
281}
282
283pub trait ComponentKey {
284    fn default_key(&self) -> DiffKey;
285}
286
287impl<T> Component for T
288where
289    T: ComponentOwned + Clone,
290{
291    fn render(&self) -> impl IntoElement {
292        <Self as ComponentOwned>::render(self.clone())
293    }
294    fn render_key(&self) -> DiffKey {
295        <Self as ComponentOwned>::render_key(self)
296    }
297}
298
299impl<T> ComponentKey for T
300where
301    T: Component,
302{
303    fn default_key(&self) -> DiffKey {
304        DiffKey::U64(Self::render as *const () as u64)
305    }
306}
307
308impl<T> MaybeExt for T where T: Component {}
309
310impl<T: Component + PartialEq> From<T> for Element {
311    fn from(value: T) -> Self {
312        from_fn_standalone_borrowed_keyed(value.render_key(), value, |v| v.render().into_element())
313    }
314}
315
316impl PartialEq for Element {
317    fn eq(&self, other: &Self) -> bool {
318        match (self, other) {
319            (
320                Self::Component {
321                    key: key1,
322                    props: props1,
323                    ..
324                },
325                Self::Component {
326                    key: key2,
327                    props: props2,
328                    ..
329                },
330            ) => key1 == key2 && !props1.changed(props2.as_ref()),
331            (
332                Self::Element {
333                    key: key1,
334                    element: element1,
335                    elements: elements1,
336                },
337                Self::Element {
338                    key: key2,
339                    element: element2,
340                    elements: elements2,
341                },
342            ) => key1 == key2 && !element1.changed(element2) && elements1 == elements2,
343            _ => false,
344        }
345    }
346}
347
348#[derive(Clone, PartialEq)]
349pub enum EventHandlerType {
350    Mouse(EventHandler<Event<MouseEventData>>),
351    Keyboard(EventHandler<Event<KeyboardEventData>>),
352    Sized(EventHandler<Event<SizedEventData>>),
353    Wheel(EventHandler<Event<WheelEventData>>),
354    Touch(EventHandler<Event<TouchEventData>>),
355    Pointer(EventHandler<Event<PointerEventData>>),
356    ImePreedit(EventHandler<Event<ImePreeditEventData>>),
357    File(EventHandler<Event<FileEventData>>),
358}