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