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