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