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