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