1use std::{
2 any::Any,
3 borrow::Cow,
4 cell::RefCell,
5 collections::HashMap,
6 rc::Rc,
7};
8
9use bytes::Bytes;
10use freya_engine::prelude::{
11 ClipOp,
12 LocalResourceProvider,
13 Paint,
14 SkRect,
15 svg,
16};
17use rustc_hash::FxHashMap;
18use torin::{
19 prelude::Size2D,
20 size::Size,
21};
22
23use crate::{
24 data::{
25 AccessibilityData,
26 EffectData,
27 LayoutData,
28 StyleState,
29 TextStyleData,
30 },
31 diff_key::DiffKey,
32 element::{
33 ClipContext,
34 Element,
35 ElementExt,
36 EventHandlerType,
37 LayoutContext,
38 RenderContext,
39 },
40 events::name::EventName,
41 prelude::{
42 AccessibilityExt,
43 Color,
44 ContainerExt,
45 EventHandlersExt,
46 KeyExt,
47 LayerExt,
48 LayoutExt,
49 MaybeExt,
50 },
51 tree::DiffModifies,
52};
53
54#[derive(PartialEq, Clone)]
55pub struct SvgElement {
56 pub accessibility: AccessibilityData,
57 pub layout: LayoutData,
58 pub event_handlers: FxHashMap<EventName, EventHandlerType>,
59 pub bytes: Bytes,
60 pub color: Color,
61 pub stroke: Option<Color>,
62 pub fill: Option<Color>,
63 pub effect: Option<EffectData>,
64 pub relative_layer: i16,
65}
66
67impl ElementExt for SvgElement {
68 fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
69 let Some(image) = (other.as_ref() as &dyn Any).downcast_ref::<SvgElement>() else {
70 return false;
71 };
72 self != image
73 }
74
75 fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
76 let Some(svg) = (other.as_ref() as &dyn Any).downcast_ref::<SvgElement>() else {
77 return DiffModifies::all();
78 };
79
80 let mut diff = DiffModifies::empty();
81
82 if self.accessibility != svg.accessibility {
83 diff.insert(DiffModifies::ACCESSIBILITY);
84 }
85
86 if self.relative_layer != svg.relative_layer {
87 diff.insert(DiffModifies::LAYER);
88 }
89
90 if self.layout != svg.layout || self.bytes != svg.bytes {
91 diff.insert(DiffModifies::LAYOUT);
92 }
93
94 if self.color != svg.color || self.stroke != svg.stroke {
95 diff.insert(DiffModifies::STYLE);
96 }
97
98 if self.effect != svg.effect {
99 diff.insert(DiffModifies::EFFECT);
100 }
101
102 diff
103 }
104
105 fn layout(&'_ self) -> Cow<'_, LayoutData> {
106 Cow::Borrowed(&self.layout)
107 }
108
109 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
110 self.effect.as_ref().map(Cow::Borrowed)
111 }
112
113 fn style(&'_ self) -> Cow<'_, StyleState> {
114 Cow::Owned(StyleState::default())
115 }
116
117 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
118 Cow::Owned(TextStyleData::default())
119 }
120
121 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
122 Cow::Borrowed(&self.accessibility)
123 }
124
125 fn relative_layer(&self) -> i16 {
126 self.relative_layer
127 }
128
129 fn should_measure_inner_children(&self) -> bool {
130 false
131 }
132
133 fn should_hook_measurement(&self) -> bool {
134 true
135 }
136
137 fn measure(&self, context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
138 let resource_provider = LocalResourceProvider::new(context.font_manager);
139 let svg_dom = svg::Dom::from_bytes(&self.bytes, resource_provider);
140 if let Ok(mut svg_dom) = svg_dom {
141 svg_dom.set_container_size(context.area_size.to_i32().to_tuple());
142 let mut root = svg_dom.root();
143 match self.layout.layout.width {
144 Size::Pixels(px) => {
145 root.set_width(svg::Length::new(px.get(), svg::LengthUnit::PX));
146 }
147 Size::Percentage(per) => {
148 root.set_width(svg::Length::new(per.get(), svg::LengthUnit::Percentage));
149 }
150 Size::Fill => {
151 root.set_width(svg::Length::new(100., svg::LengthUnit::Percentage));
152 }
153 _ => {}
154 }
155 match self.layout.layout.height {
156 Size::Pixels(px) => {
157 root.set_height(svg::Length::new(px.get(), svg::LengthUnit::PX));
158 }
159 Size::Percentage(per) => {
160 root.set_height(svg::Length::new(per.get(), svg::LengthUnit::Percentage));
161 }
162 Size::Fill => {
163 root.set_height(svg::Length::new(100., svg::LengthUnit::Percentage));
164 }
165 _ => {}
166 }
167 Some((
168 Size2D::new(root.width().value, root.height().value),
169 Rc::new(RefCell::new(svg_dom)),
170 ))
171 } else {
172 None
173 }
174 }
175
176 fn clip(&self, context: ClipContext) {
177 let area = context.visible_area;
178 context.canvas.clip_rect(
179 SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
180 ClipOp::Intersect,
181 true,
182 );
183 }
184
185 fn render(&self, context: RenderContext) {
186 let mut paint = Paint::default();
187 paint.set_anti_alias(true);
188
189 let svg_dom = context
190 .layout_node
191 .data
192 .as_ref()
193 .unwrap()
194 .downcast_ref::<RefCell<svg::Dom>>()
195 .unwrap();
196 let svg_dom = svg_dom.borrow();
197
198 let mut root = svg_dom.root();
199 context.canvas.save();
200 context
201 .canvas
202 .translate(context.layout_node.visible_area().origin.to_tuple());
203
204 root.set_color(self.color.into());
205 if let Some(fill) = self.fill {
206 root.set_fill(svg::Paint::from_color(fill.into()));
207 }
208 if let Some(stroke) = self.stroke {
209 root.set_stroke(svg::Paint::from_color(stroke.into()));
210 }
211 svg_dom.render(context.canvas);
212 context.canvas.restore();
213 }
214}
215
216impl From<Svg> for Element {
217 fn from(value: Svg) -> Self {
218 Element::Element {
219 key: value.key,
220 element: Rc::new(value.element),
221 elements: vec![],
222 }
223 }
224}
225
226impl KeyExt for Svg {
227 fn write_key(&mut self) -> &mut DiffKey {
228 &mut self.key
229 }
230}
231
232impl EventHandlersExt for Svg {
233 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType> {
234 &mut self.element.event_handlers
235 }
236}
237
238impl LayoutExt for Svg {
239 fn get_layout(&mut self) -> &mut LayoutData {
240 &mut self.element.layout
241 }
242}
243
244impl ContainerExt for Svg {}
245
246impl AccessibilityExt for Svg {
247 fn get_accessibility_data(&mut self) -> &mut AccessibilityData {
248 &mut self.element.accessibility
249 }
250}
251
252impl MaybeExt for Svg {}
253
254impl LayerExt for Svg {
255 fn get_layer(&mut self) -> &mut i16 {
256 &mut self.element.relative_layer
257 }
258}
259
260pub struct Svg {
261 key: DiffKey,
262 element: SvgElement,
263}
264
265pub fn svg(bytes: Bytes) -> Svg {
276 Svg {
277 key: DiffKey::None,
278 element: SvgElement {
279 accessibility: AccessibilityData::default(),
280 layout: LayoutData::default(),
281 event_handlers: HashMap::default(),
282 bytes,
283 effect: None,
284 color: Color::BLACK,
285 stroke: None,
286 fill: None,
287 relative_layer: 0,
288 },
289 }
290}
291
292impl Svg {
293 pub fn try_downcast(element: &dyn ElementExt) -> Option<SvgElement> {
294 (element as &dyn Any).downcast_ref::<SvgElement>().cloned()
295 }
296
297 pub fn color(mut self, color: impl Into<Color>) -> Self {
298 self.element.color = color.into();
299 self
300 }
301
302 pub fn fill(mut self, fill: impl Into<Color>) -> Self {
303 self.element.fill = Some(fill.into());
304 self
305 }
306
307 pub fn stroke<R: Into<Option<Color>>>(mut self, stroke: R) -> Self {
308 self.element.stroke = stroke.into();
309 self
310 }
311
312 pub fn rotate<R: Into<Option<f32>>>(mut self, rotation: R) -> Self {
313 self.element
314 .effect
315 .get_or_insert_with(Default::default)
316 .rotation = rotation.into();
317 self
318 }
319}