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