1use std::{
4 any::Any,
5 borrow::Cow,
6 rc::Rc,
7};
8
9use freya_engine::prelude::{
10 Canvas,
11 ClipOp,
12 Paint,
13 PaintStyle,
14 PathBuilder,
15 SkBlurStyle,
16 SkMaskFilter,
17 SkPath,
18 SkPathFillType,
19 SkPoint,
20 SkRRect,
21 SkRect,
22};
23use rustc_hash::FxHashMap;
24use torin::{
25 prelude::Area,
26 scaled::Scaled,
27};
28
29use crate::{
30 diff_key::DiffKey,
31 element::{
32 ClipContext,
33 ElementExt,
34 EventHandlerType,
35 EventMeasurementContext,
36 RenderContext,
37 },
38 events::name::EventName,
39 layers::Layer,
40 prelude::*,
41 style::{
42 font_size::FontSize,
43 scale::Scale,
44 shadow::{
45 Shadow,
46 ShadowPosition,
47 },
48 transform_origin::TransformOrigin,
49 },
50 tree::DiffModifies,
51};
52
53pub fn rect() -> Rect {
66 Rect {
67 element: RectElement::default(),
68 elements: Vec::default(),
69 key: DiffKey::None,
70 }
71}
72
73#[derive(PartialEq, Clone)]
74pub struct RectElement {
75 pub style: StyleState,
76 pub layout: LayoutData,
77 pub text_style_data: TextStyleData,
78 pub relative_layer: Layer,
79 pub event_handlers: FxHashMap<EventName, EventHandlerType>,
80 pub accessibility: AccessibilityData,
81 pub effect: Option<EffectData>,
82}
83
84impl Default for RectElement {
85 fn default() -> Self {
86 let mut accessibility = AccessibilityData::default();
87 accessibility
88 .builder
89 .set_role(accesskit::Role::GenericContainer);
90 Self {
91 style: Default::default(),
92 layout: Default::default(),
93 text_style_data: Default::default(),
94 relative_layer: Default::default(),
95 event_handlers: Default::default(),
96 accessibility,
97 effect: Default::default(),
98 }
99 }
100}
101
102impl RectElement {
103 pub fn render_shadow(
104 canvas: &Canvas,
105 path: &mut SkPath,
106 rounded_rect: SkRRect,
107 _area: Area,
108 shadow: &Shadow,
109 corner_radius: &CornerRadius,
110 ) {
111 let mut shadow_path = PathBuilder::new();
112 let mut shadow_paint = Paint::default();
113 shadow_paint.set_anti_alias(true);
114 shadow_paint.set_color(shadow.color);
115
116 let outset: SkPoint = match shadow.position {
120 ShadowPosition::Normal => {
121 shadow_paint.set_style(PaintStyle::Fill);
122 (shadow.spread, shadow.spread).into()
123 }
124 ShadowPosition::Inset => {
125 shadow_paint.set_style(PaintStyle::Stroke);
126 shadow_paint.set_stroke_width(shadow.blur / 2.0 + shadow.spread);
127 (-shadow.spread / 2.0, -shadow.spread / 2.0).into()
128 }
129 };
130
131 if shadow.blur > 0.0 {
133 shadow_paint.set_mask_filter(SkMaskFilter::blur(
134 SkBlurStyle::Normal,
135 shadow.blur / 2.0,
136 false,
137 ));
138 }
139
140 if corner_radius.smoothing > 0.0 {
142 shadow_path.add_path(
143 &corner_radius.smoothed_path(rounded_rect.with_outset(outset)),
144 None,
145 );
146 } else {
147 shadow_path.add_rrect(rounded_rect.with_outset(outset), None, None);
148 }
149
150 shadow_path.offset((shadow.x, shadow.y));
152
153 canvas.save();
155 canvas.clip_path(
156 path,
157 match shadow.position {
158 ShadowPosition::Normal => ClipOp::Difference,
159 ShadowPosition::Inset => ClipOp::Intersect,
160 },
161 true,
162 );
163 let shadow_path = shadow_path.detach();
164 canvas.draw_path(&shadow_path, &shadow_paint);
165 canvas.restore();
166 }
167
168 pub fn render_border(
169 canvas: &Canvas,
170 rect: SkRect,
171 border: &Border,
172 corner_radius: &CornerRadius,
173 ) {
174 let mut border_paint = Paint::default();
175 border_paint.set_style(PaintStyle::Fill);
176 border_paint.set_anti_alias(true);
177 border_paint.set_color(border.fill);
178
179 match Self::border_shape(rect, corner_radius, border) {
180 BorderShape::DRRect(outer, inner) => {
181 canvas.draw_drrect(outer, inner, &border_paint);
182 }
183 BorderShape::Path(path) => {
184 canvas.draw_path(&path, &border_paint);
185 }
186 }
187 }
188
189 pub fn border_shape(
193 base_rect: SkRect,
194 base_corner_radius: &CornerRadius,
195 border: &Border,
196 ) -> BorderShape {
197 let border_alignment = border.alignment;
198 let border_width = border.width;
199
200 let (outer_rrect, outer_corner_radius) = {
204 let corner_radius = CornerRadius {
206 top_left: Self::outer_border_path_corner_radius(
207 border_alignment,
208 base_corner_radius.top_left,
209 border_width.top,
210 border_width.left,
211 ),
212 top_right: Self::outer_border_path_corner_radius(
213 border_alignment,
214 base_corner_radius.top_right,
215 border_width.top,
216 border_width.right,
217 ),
218 bottom_left: Self::outer_border_path_corner_radius(
219 border_alignment,
220 base_corner_radius.bottom_left,
221 border_width.bottom,
222 border_width.left,
223 ),
224 bottom_right: Self::outer_border_path_corner_radius(
225 border_alignment,
226 base_corner_radius.bottom_right,
227 border_width.bottom,
228 border_width.right,
229 ),
230 smoothing: base_corner_radius.smoothing,
231 };
232
233 let rrect = SkRRect::new_rect_radii(
234 {
235 let mut rect = base_rect;
236 let alignment_scale = match border_alignment {
237 BorderAlignment::Outer => 1.0,
238 BorderAlignment::Center => 0.5,
239 BorderAlignment::Inner => 0.0,
240 };
241
242 rect.left -= border_width.left * alignment_scale;
243 rect.top -= border_width.top * alignment_scale;
244 rect.right += border_width.right * alignment_scale;
245 rect.bottom += border_width.bottom * alignment_scale;
246
247 rect
248 },
249 &[
250 (corner_radius.top_left, corner_radius.top_left).into(),
251 (corner_radius.top_right, corner_radius.top_right).into(),
252 (corner_radius.bottom_right, corner_radius.bottom_right).into(),
253 (corner_radius.bottom_left, corner_radius.bottom_left).into(),
254 ],
255 );
256
257 (rrect, corner_radius)
258 };
259
260 let (inner_rrect, inner_corner_radius) = {
262 let corner_radius = CornerRadius {
264 top_left: Self::inner_border_path_corner_radius(
265 border_alignment,
266 base_corner_radius.top_left,
267 border_width.top,
268 border_width.left,
269 ),
270 top_right: Self::inner_border_path_corner_radius(
271 border_alignment,
272 base_corner_radius.top_right,
273 border_width.top,
274 border_width.right,
275 ),
276 bottom_left: Self::inner_border_path_corner_radius(
277 border_alignment,
278 base_corner_radius.bottom_left,
279 border_width.bottom,
280 border_width.left,
281 ),
282 bottom_right: Self::inner_border_path_corner_radius(
283 border_alignment,
284 base_corner_radius.bottom_right,
285 border_width.bottom,
286 border_width.right,
287 ),
288 smoothing: base_corner_radius.smoothing,
289 };
290
291 let rrect = SkRRect::new_rect_radii(
292 {
293 let mut rect = base_rect;
294 let alignment_scale = match border_alignment {
295 BorderAlignment::Outer => 0.0,
296 BorderAlignment::Center => 0.5,
297 BorderAlignment::Inner => 1.0,
298 };
299
300 rect.left += border_width.left * alignment_scale;
301 rect.top += border_width.top * alignment_scale;
302 rect.right -= border_width.right * alignment_scale;
303 rect.bottom -= border_width.bottom * alignment_scale;
304
305 rect
306 },
307 &[
308 (corner_radius.top_left, corner_radius.top_left).into(),
309 (corner_radius.top_right, corner_radius.top_right).into(),
310 (corner_radius.bottom_right, corner_radius.bottom_right).into(),
311 (corner_radius.bottom_left, corner_radius.bottom_left).into(),
312 ],
313 );
314
315 (rrect, corner_radius)
316 };
317
318 if base_corner_radius.smoothing > 0.0 {
319 let mut path = PathBuilder::new();
320 path.set_fill_type(SkPathFillType::EvenOdd);
321
322 path.add_path(&outer_corner_radius.smoothed_path(outer_rrect), None);
323
324 path.add_path(&inner_corner_radius.smoothed_path(inner_rrect), None);
325
326 let path = path.detach();
327 BorderShape::Path(path)
328 } else {
329 BorderShape::DRRect(outer_rrect, inner_rrect)
330 }
331 }
332
333 fn outer_border_path_corner_radius(
334 alignment: BorderAlignment,
335 corner_radius: f32,
336 width_1: f32,
337 width_2: f32,
338 ) -> f32 {
339 if alignment == BorderAlignment::Inner || corner_radius == 0.0 {
340 return corner_radius;
341 }
342
343 let mut offset = if width_1 == 0.0 {
344 width_2
345 } else if width_2 == 0.0 {
346 width_1
347 } else {
348 width_1.min(width_2)
349 };
350
351 if alignment == BorderAlignment::Center {
352 offset *= 0.5;
353 }
354
355 corner_radius + offset
356 }
357
358 fn inner_border_path_corner_radius(
359 alignment: BorderAlignment,
360 corner_radius: f32,
361 width_1: f32,
362 width_2: f32,
363 ) -> f32 {
364 if alignment == BorderAlignment::Outer || corner_radius == 0.0 {
365 return corner_radius;
366 }
367
368 let mut offset = if width_1 == 0.0 {
369 width_2
370 } else if width_2 == 0.0 {
371 width_1
372 } else {
373 width_1.min(width_2)
374 };
375
376 if alignment == BorderAlignment::Center {
377 offset *= 0.5;
378 }
379
380 corner_radius - offset
381 }
382}
383
384impl ElementExt for RectElement {
385 fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
386 let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
387 return false;
388 };
389
390 self != rect
391 }
392
393 fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
394 let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
395 return DiffModifies::all();
396 };
397
398 let mut diff = DiffModifies::empty();
399
400 if self.style != rect.style {
401 diff.insert(DiffModifies::STYLE);
402 }
403
404 if self.effect != rect.effect {
405 diff.insert(DiffModifies::EFFECT);
406 }
407
408 if !self.layout.self_layout_eq(&rect.layout.layout) {
409 diff.insert(DiffModifies::STYLE);
410 diff.insert(DiffModifies::LAYOUT);
411 }
412
413 if !self.layout.inner_layout_eq(&rect.layout.layout) {
414 diff.insert(DiffModifies::STYLE);
415 diff.insert(DiffModifies::INNER_LAYOUT);
416 }
417
418 if self.accessibility != rect.accessibility {
419 diff.insert(DiffModifies::ACCESSIBILITY);
420 }
421
422 if self.relative_layer != rect.relative_layer {
423 diff.insert(DiffModifies::LAYER);
424 }
425
426 if self.event_handlers != rect.event_handlers {
427 diff.insert(DiffModifies::EVENT_HANDLERS);
428 }
429
430 if self.text_style_data != rect.text_style_data {
431 diff.insert(DiffModifies::TEXT_STYLE);
432 }
433
434 diff
435 }
436
437 fn layout(&'_ self) -> Cow<'_, LayoutData> {
438 Cow::Borrowed(&self.layout)
439 }
440
441 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
442 self.effect.as_ref().map(Cow::Borrowed)
443 }
444
445 fn style(&'_ self) -> Cow<'_, StyleState> {
446 Cow::Borrowed(&self.style)
447 }
448
449 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
450 Cow::Borrowed(&self.text_style_data)
451 }
452
453 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
454 Cow::Borrowed(&self.accessibility)
455 }
456
457 fn layer(&self) -> Layer {
458 self.relative_layer
459 }
460
461 fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
462 Some(Cow::Borrowed(&self.event_handlers))
463 }
464
465 fn is_point_inside(&self, context: EventMeasurementContext) -> bool {
468 let area = context.layout_node.visible_area();
469 let cursor = context.cursor.to_f32();
470 let local_area = Area::new((0., 0.).into(), area.size);
471 let rounded_rect = self.render_rect(&local_area, context.scale_factor as f32);
472 let local_x = cursor.x - area.min_x();
473 let local_y = cursor.y - area.min_y();
474 rounded_rect.contains(SkRect::new(
475 local_x,
476 local_y,
477 local_x.next_up(),
478 local_y.next_up(),
479 ))
480 }
481
482 fn clip(&self, context: ClipContext) {
483 let area = context.visible_area;
484
485 let rounded_rect = self.render_rect(area, context.scale_factor as f32);
486
487 context
488 .canvas
489 .clip_rrect(rounded_rect, ClipOp::Intersect, true);
490 }
491
492 fn render(&self, context: RenderContext) {
493 let style = self.style();
494
495 let area = context.layout_node.visible_area();
496 let corner_radius = style.corner_radius.with_scale(context.scale_factor as f32);
497
498 let mut path = PathBuilder::new();
499 let mut paint = Paint::default();
500 paint.set_anti_alias(true);
501 paint.set_style(PaintStyle::Fill);
502 style.background.apply_to_paint(&mut paint, area);
503
504 let rounded_rect = self.render_rect(&area, context.scale_factor as f32);
506 if corner_radius.smoothing > 0.0 {
507 path.add_path(&corner_radius.smoothed_path(rounded_rect), None);
508 } else {
509 path.add_rrect(rounded_rect, None, None);
510 }
511
512 let mut path = path.detach();
513 context.canvas.draw_path(&path, &paint);
514
515 for shadow in style.shadows.iter() {
517 if shadow.color != Color::TRANSPARENT {
518 let shadow = shadow.with_scale(context.scale_factor as f32);
519
520 Self::render_shadow(
521 context.canvas,
522 &mut path,
523 rounded_rect,
524 area,
525 &shadow,
526 &corner_radius,
527 );
528 }
529 }
530
531 for border in style.borders.iter() {
533 if border.is_visible() {
534 let border = border.with_scale(context.scale_factor as f32);
535 let rect = *rounded_rect.rect();
536 Self::render_border(context.canvas, rect, &border, &corner_radius);
537 }
538 }
539 }
540}
541
542pub struct Rect {
543 element: RectElement,
544 elements: Vec<Element>,
545 key: DiffKey,
546}
547
548impl ChildrenExt for Rect {
549 fn get_children(&mut self) -> &mut Vec<Element> {
550 &mut self.elements
551 }
552}
553
554impl KeyExt for Rect {
555 fn write_key(&mut self) -> &mut DiffKey {
556 &mut self.key
557 }
558}
559
560impl EventHandlersExt for Rect {
561 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType> {
562 &mut self.element.event_handlers
563 }
564}
565
566impl AccessibilityExt for Rect {
567 fn get_accessibility_data(&mut self) -> &mut AccessibilityData {
568 &mut self.element.accessibility
569 }
570}
571
572impl TextStyleExt for Rect {
573 fn get_text_style_data(&mut self) -> &mut TextStyleData {
574 &mut self.element.text_style_data
575 }
576}
577
578impl StyleExt for Rect {
579 fn get_style(&mut self) -> &mut StyleState {
580 &mut self.element.style
581 }
582}
583
584impl MaybeExt for Rect {}
585
586impl LayerExt for Rect {
587 fn get_layer(&mut self) -> &mut Layer {
588 &mut self.element.relative_layer
589 }
590}
591
592impl LayoutExt for Rect {
593 fn get_layout(&mut self) -> &mut LayoutData {
594 &mut self.element.layout
595 }
596}
597
598impl ContainerExt for Rect {}
599
600impl ContainerWithContentExt for Rect {}
601
602impl ScrollableExt for Rect {
603 fn get_effect(&mut self) -> &mut EffectData {
604 if self.element.effect.is_none() {
605 self.element.effect = Some(EffectData::default())
606 }
607
608 self.element.effect.as_mut().unwrap()
609 }
610}
611
612impl InteractiveExt for Rect {
613 fn get_effect(&mut self) -> &mut EffectData {
614 if self.element.effect.is_none() {
615 self.element.effect = Some(EffectData::default())
616 }
617
618 self.element.effect.as_mut().unwrap()
619 }
620}
621
622impl EffectExt for Rect {
623 fn get_effect(&mut self) -> &mut EffectData {
624 if self.element.effect.is_none() {
625 self.element.effect = Some(EffectData::default())
626 }
627
628 self.element.effect.as_mut().unwrap()
629 }
630}
631
632impl From<Rect> for Element {
633 fn from(value: Rect) -> Self {
634 Element::Element {
635 key: value.key,
636 element: Rc::new(value.element),
637 elements: value.elements,
638 }
639 }
640}
641
642impl Rect {
643 pub fn try_downcast(element: &dyn ElementExt) -> Option<RectElement> {
644 (element as &dyn Any).downcast_ref::<RectElement>().cloned()
645 }
646
647 pub fn color(mut self, color: impl Into<Color>) -> Self {
649 self.element.text_style_data.color = Some(Fill::Color(color.into()));
650 self
651 }
652
653 pub fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
655 self.element.text_style_data.font_size = Some(font_size.into());
656 self
657 }
658
659 pub fn overflow<S: Into<Overflow>>(mut self, overflow: S) -> Self {
661 self.element
662 .effect
663 .get_or_insert_with(Default::default)
664 .overflow = overflow.into();
665 self
666 }
667
668 pub fn rotate<R: Into<Option<f32>>>(mut self, rotation: R) -> Self {
670 self.element
671 .effect
672 .get_or_insert_with(Default::default)
673 .rotation = rotation.into();
674 self
675 }
676
677 pub fn scale(mut self, scale: impl Into<Scale>) -> Self {
679 self.element
680 .effect
681 .get_or_insert_with(Default::default)
682 .scale = Some(scale.into());
683 self
684 }
685
686 pub fn transform_origin(mut self, transform_origin: impl Into<TransformOrigin>) -> Self {
690 self.element
691 .effect
692 .get_or_insert_with(Default::default)
693 .transform_origin = transform_origin.into();
694 self
695 }
696
697 pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
699 self.element
700 .effect
701 .get_or_insert_with(Default::default)
702 .opacity = Some(opacity.into());
703 self
704 }
705
706 pub fn blur(mut self, blur: impl Into<f32>) -> Self {
708 self.element
709 .effect
710 .get_or_insert_with(Default::default)
711 .blur = Some(blur.into());
712 self
713 }
714}