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