freya_components/
canvas.rs

1use std::{
2    any::Any,
3    borrow::Cow,
4    cell::RefCell,
5    collections::HashMap,
6    rc::Rc,
7};
8
9use freya_core::{
10    integration::*,
11    prelude::*,
12};
13use freya_engine::prelude::{
14    ClipOp,
15    Paint,
16    PaintStyle,
17    SkRect,
18};
19
20type Callback = Rc<RefCell<dyn FnMut(&mut RenderContext)>>;
21
22pub struct RenderCallback(Callback);
23
24impl RenderCallback {
25    pub fn new(callback: impl FnMut(&mut RenderContext) + 'static) -> Self {
26        Self(Rc::new(RefCell::new(callback)))
27    }
28
29    pub fn call(&self, data: &mut RenderContext) {
30        (self.0.borrow_mut())(data)
31    }
32}
33
34impl Clone for RenderCallback {
35    fn clone(&self) -> Self {
36        Self(self.0.clone())
37    }
38}
39
40impl PartialEq for RenderCallback {
41    fn eq(&self, _other: &Self) -> bool {
42        true
43    }
44}
45
46impl<H: FnMut(&mut RenderContext) + 'static> From<H> for RenderCallback {
47    fn from(value: H) -> Self {
48        RenderCallback::new(value)
49    }
50}
51
52#[derive(PartialEq, Clone)]
53pub struct CanvasElement {
54    pub layout: LayoutData,
55    pub event_handlers: FxHashMap<EventName, EventHandlerType>,
56    pub effect: Option<EffectData>,
57    pub on_render: RenderCallback,
58}
59
60impl CanvasElement {}
61
62impl ElementExt for CanvasElement {
63    fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
64        let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
65            return false;
66        };
67
68        self != rect
69    }
70
71    fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
72        let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
73            return DiffModifies::all();
74        };
75
76        let mut diff = DiffModifies::empty();
77
78        if self.effect != rect.effect {
79            diff.insert(DiffModifies::EFFECT);
80        }
81
82        if !self.layout.self_layout_eq(&rect.layout.layout) {
83            diff.insert(DiffModifies::STYLE);
84            diff.insert(DiffModifies::LAYOUT);
85        }
86
87        if !self.layout.inner_layout_eq(&rect.layout.layout) {
88            diff.insert(DiffModifies::STYLE);
89            diff.insert(DiffModifies::INNER_LAYOUT);
90        }
91
92        if self.event_handlers != rect.event_handlers {
93            diff.insert(DiffModifies::EVENT_HANDLERS);
94        }
95
96        if self.on_render != rect.on_render {
97            diff.insert(DiffModifies::STYLE);
98        }
99
100        diff
101    }
102
103    fn layout(&'_ self) -> Cow<'_, LayoutData> {
104        Cow::Borrowed(&self.layout)
105    }
106
107    fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
108        self.effect.as_ref().map(Cow::Borrowed)
109    }
110
111    fn style(&'_ self) -> Cow<'_, StyleState> {
112        Cow::Owned(StyleState::default())
113    }
114
115    fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
116        Cow::Owned(TextStyleData::default())
117    }
118
119    fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
120        Cow::Owned(AccessibilityData::default())
121    }
122
123    fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
124        Some(Cow::Borrowed(&self.event_handlers))
125    }
126
127    fn clip(&self, context: ClipContext) {
128        let area = context.visible_area;
129
130        context.canvas.clip_rect(
131            SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
132            ClipOp::Intersect,
133            true,
134        );
135    }
136
137    fn render(&self, mut context: RenderContext) {
138        let style = self.style();
139        let area = context.layout_node.visible_area();
140
141        let mut paint = Paint::default();
142        paint.set_anti_alias(true);
143        paint.set_style(PaintStyle::Fill);
144        style.background.apply_to_paint(&mut paint, area);
145
146        context.canvas.draw_rect(
147            SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
148            &paint,
149        );
150
151        context
152            .canvas
153            .scale((context.scale_factor as f32, context.scale_factor as f32));
154        context.canvas.translate((area.min_x(), area.min_y()));
155        self.on_render.call(&mut context);
156        context.canvas.restore();
157    }
158}
159
160pub struct Canvas {
161    element: CanvasElement,
162    elements: Vec<Element>,
163    key: DiffKey,
164}
165
166impl ChildrenExt for Canvas {
167    fn get_children(&mut self) -> &mut Vec<Element> {
168        &mut self.elements
169    }
170}
171
172impl KeyExt for Canvas {
173    fn write_key(&mut self) -> &mut DiffKey {
174        &mut self.key
175    }
176}
177
178impl EventHandlersExt for Canvas {
179    fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType> {
180        &mut self.element.event_handlers
181    }
182}
183
184impl MaybeExt for Canvas {}
185
186impl From<Canvas> for Element {
187    fn from(value: Canvas) -> Self {
188        Element::Element {
189            key: value.key,
190            element: Rc::new(value.element),
191            elements: value.elements,
192        }
193    }
194}
195
196/// Create a new `Canvas` element.
197///
198/// See the available methods in [Canvas].
199pub fn canvas(on_render: RenderCallback) -> Canvas {
200    Canvas::new(on_render)
201}
202
203impl Canvas {
204    pub fn new(on_render: RenderCallback) -> Self {
205        Self {
206            element: CanvasElement {
207                on_render,
208                layout: LayoutData::default(),
209                event_handlers: HashMap::default(),
210                effect: None,
211            },
212            elements: Vec::default(),
213            key: DiffKey::None,
214        }
215    }
216
217    pub fn try_downcast(element: &dyn ElementExt) -> Option<CanvasElement> {
218        (element as &dyn Any)
219            .downcast_ref::<CanvasElement>()
220            .cloned()
221    }
222}
223
224impl LayoutExt for Canvas {
225    fn get_layout(&mut self) -> &mut LayoutData {
226        &mut self.element.layout
227    }
228}
229
230impl ContainerExt for Canvas {}
231
232impl ContainerWithContentExt for Canvas {}