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