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