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