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