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 transform_origin::TransformOrigin,
49 },
50 tree::DiffModifies,
51};
52
53pub fn rect() -> Rect {
66 Rect::empty()
67}
68
69#[derive(PartialEq, Clone)]
70pub struct RectElement {
71 pub style: StyleState,
72 pub layout: LayoutData,
73 pub text_style_data: TextStyleData,
74 pub relative_layer: Layer,
75 pub event_handlers: FxHashMap<EventName, EventHandlerType>,
76 pub accessibility: AccessibilityData,
77 pub effect: Option<EffectData>,
78}
79
80impl Default for RectElement {
81 fn default() -> Self {
82 let mut accessibility = AccessibilityData::default();
83 accessibility
84 .builder
85 .set_role(accesskit::Role::GenericContainer);
86 Self {
87 style: Default::default(),
88 layout: Default::default(),
89 text_style_data: Default::default(),
90 relative_layer: Default::default(),
91 event_handlers: Default::default(),
92 accessibility,
93 effect: Default::default(),
94 }
95 }
96}
97
98impl RectElement {
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 = PathBuilder::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 None,
141 );
142 } else {
143 shadow_path.add_rrect(rounded_rect.with_outset(outset), None, 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 let shadow_path = shadow_path.detach();
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 = PathBuilder::new();
316 path.set_fill_type(SkPathFillType::EvenOdd);
317
318 path.add_path(&outer_corner_radius.smoothed_path(outer_rrect), None);
319
320 path.add_path(&inner_corner_radius.smoothed_path(inner_rrect), None);
321
322 let path = path.detach();
323 BorderShape::Path(path)
324 } else {
325 BorderShape::DRRect(outer_rrect, inner_rrect)
326 }
327 }
328
329 fn outer_border_path_corner_radius(
330 alignment: BorderAlignment,
331 corner_radius: f32,
332 width_1: f32,
333 width_2: f32,
334 ) -> f32 {
335 if alignment == BorderAlignment::Inner || corner_radius == 0.0 {
336 return corner_radius;
337 }
338
339 let mut offset = if width_1 == 0.0 {
340 width_2
341 } else if width_2 == 0.0 {
342 width_1
343 } else {
344 width_1.min(width_2)
345 };
346
347 if alignment == BorderAlignment::Center {
348 offset *= 0.5;
349 }
350
351 corner_radius + offset
352 }
353
354 fn inner_border_path_corner_radius(
355 alignment: BorderAlignment,
356 corner_radius: f32,
357 width_1: f32,
358 width_2: f32,
359 ) -> f32 {
360 if alignment == BorderAlignment::Outer || corner_radius == 0.0 {
361 return corner_radius;
362 }
363
364 let mut offset = if width_1 == 0.0 {
365 width_2
366 } else if width_2 == 0.0 {
367 width_1
368 } else {
369 width_1.min(width_2)
370 };
371
372 if alignment == BorderAlignment::Center {
373 offset *= 0.5;
374 }
375
376 corner_radius - offset
377 }
378}
379
380impl ElementExt for RectElement {
381 fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
382 let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
383 return false;
384 };
385
386 self != rect
387 }
388
389 fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
390 let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
391 return DiffModifies::all();
392 };
393
394 let mut diff = DiffModifies::empty();
395
396 if self.style != rect.style {
397 diff.insert(DiffModifies::STYLE);
398 }
399
400 if self.effect != rect.effect {
401 diff.insert(DiffModifies::EFFECT);
402 }
403
404 if !self.layout.self_layout_eq(&rect.layout.layout) {
405 diff.insert(DiffModifies::STYLE);
406 diff.insert(DiffModifies::LAYOUT);
407 }
408
409 if !self.layout.inner_layout_eq(&rect.layout.layout) {
410 diff.insert(DiffModifies::STYLE);
411 diff.insert(DiffModifies::INNER_LAYOUT);
412 }
413
414 if self.accessibility != rect.accessibility {
415 diff.insert(DiffModifies::ACCESSIBILITY);
416 }
417
418 if self.relative_layer != rect.relative_layer {
419 diff.insert(DiffModifies::LAYER);
420 }
421
422 if self.event_handlers != rect.event_handlers {
423 diff.insert(DiffModifies::EVENT_HANDLERS);
424 }
425
426 if self.text_style_data != rect.text_style_data {
427 diff.insert(DiffModifies::TEXT_STYLE);
428 }
429
430 diff
431 }
432
433 fn layout(&'_ self) -> Cow<'_, LayoutData> {
434 Cow::Borrowed(&self.layout)
435 }
436
437 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
438 self.effect.as_ref().map(Cow::Borrowed)
439 }
440
441 fn style(&'_ self) -> Cow<'_, StyleState> {
442 Cow::Borrowed(&self.style)
443 }
444
445 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
446 Cow::Borrowed(&self.text_style_data)
447 }
448
449 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
450 Cow::Borrowed(&self.accessibility)
451 }
452
453 fn layer(&self) -> Layer {
454 self.relative_layer
455 }
456
457 fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
458 Some(Cow::Borrowed(&self.event_handlers))
459 }
460
461 fn is_point_inside(&self, context: EventMeasurementContext) -> bool {
464 let area = context.layout_node.visible_area();
465 let cursor = context.cursor.to_f32();
466 let local_area = Area::new((0., 0.).into(), area.size);
467 let rounded_rect = self.render_rect(&local_area, context.scale_factor as f32);
468 let local_x = cursor.x - area.min_x();
469 let local_y = cursor.y - area.min_y();
470 rounded_rect.contains(SkRect::new(
471 local_x,
472 local_y,
473 local_x.next_up(),
474 local_y.next_up(),
475 ))
476 }
477
478 fn clip(&self, context: ClipContext) {
479 let area = context.visible_area;
480
481 let rounded_rect = self.render_rect(area, context.scale_factor as f32);
482
483 context
484 .canvas
485 .clip_rrect(rounded_rect, ClipOp::Intersect, true);
486 }
487
488 fn render(&self, context: RenderContext) {
489 let style = self.style();
490
491 let area = context.layout_node.visible_area();
492 let corner_radius = style.corner_radius.with_scale(context.scale_factor as f32);
493
494 let mut path = PathBuilder::new();
495 let mut paint = Paint::default();
496 paint.set_anti_alias(true);
497 paint.set_style(PaintStyle::Fill);
498 style.background.apply_to_paint(&mut paint, area);
499
500 let rounded_rect = self.render_rect(&area, context.scale_factor as f32);
502 if corner_radius.smoothing > 0.0 {
503 path.add_path(&corner_radius.smoothed_path(rounded_rect), None);
504 } else {
505 path.add_rrect(rounded_rect, None, None);
506 }
507
508 let mut path = path.detach();
509 context.canvas.draw_path(&path, &paint);
510
511 for shadow in style.shadows.iter() {
513 if shadow.color != Color::TRANSPARENT {
514 let shadow = shadow.with_scale(context.scale_factor as f32);
515
516 Self::render_shadow(
517 context.canvas,
518 &mut path,
519 rounded_rect,
520 area,
521 &shadow,
522 &corner_radius,
523 );
524 }
525 }
526
527 for border in style.borders.iter() {
529 if border.is_visible() {
530 let border = border.with_scale(context.scale_factor as f32);
531 let rect = *rounded_rect.rect();
532 Self::render_border(context.canvas, rect, &border, &corner_radius);
533 }
534 }
535 }
536}
537
538pub struct Rect {
539 element: RectElement,
540 elements: Vec<Element>,
541 key: DiffKey,
542}
543
544impl ChildrenExt for Rect {
545 fn get_children(&mut self) -> &mut Vec<Element> {
546 &mut self.elements
547 }
548}
549
550impl KeyExt for Rect {
551 fn write_key(&mut self) -> &mut DiffKey {
552 &mut self.key
553 }
554}
555
556impl EventHandlersExt for Rect {
557 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType> {
558 &mut self.element.event_handlers
559 }
560}
561
562impl AccessibilityExt for Rect {
563 fn get_accessibility_data(&mut self) -> &mut AccessibilityData {
564 &mut self.element.accessibility
565 }
566}
567
568impl TextStyleExt for Rect {
569 fn get_text_style_data(&mut self) -> &mut TextStyleData {
570 &mut self.element.text_style_data
571 }
572}
573
574impl StyleExt for Rect {
575 fn get_style(&mut self) -> &mut StyleState {
576 &mut self.element.style
577 }
578}
579
580impl MaybeExt for Rect {}
581
582impl LayerExt for Rect {
583 fn get_layer(&mut self) -> &mut Layer {
584 &mut self.element.relative_layer
585 }
586}
587
588impl LayoutExt for Rect {
589 fn get_layout(&mut self) -> &mut LayoutData {
590 &mut self.element.layout
591 }
592}
593
594impl ContainerExt for Rect {}
595
596impl ContainerWithContentExt for Rect {}
597
598impl ScrollableExt for Rect {
599 fn get_effect(&mut self) -> &mut EffectData {
600 if self.element.effect.is_none() {
601 self.element.effect = Some(EffectData::default())
602 }
603
604 self.element.effect.as_mut().unwrap()
605 }
606}
607
608impl InteractiveExt for Rect {
609 fn get_effect(&mut self) -> &mut EffectData {
610 if self.element.effect.is_none() {
611 self.element.effect = Some(EffectData::default())
612 }
613
614 self.element.effect.as_mut().unwrap()
615 }
616}
617
618impl EffectExt for Rect {
619 fn get_effect(&mut self) -> &mut EffectData {
620 if self.element.effect.is_none() {
621 self.element.effect = Some(EffectData::default())
622 }
623
624 self.element.effect.as_mut().unwrap()
625 }
626}
627
628impl From<Rect> for Element {
629 fn from(value: Rect) -> Self {
630 Element::Element {
631 key: value.key,
632 element: Rc::new(value.element),
633 elements: value.elements,
634 }
635 }
636}
637
638impl Rect {
639 pub fn empty() -> Self {
640 Self {
641 element: RectElement::default(),
642 elements: Vec::default(),
643 key: DiffKey::None,
644 }
645 }
646
647 pub fn try_downcast(element: &dyn ElementExt) -> Option<RectElement> {
648 (element as &dyn Any).downcast_ref::<RectElement>().cloned()
649 }
650
651 pub fn color(mut self, color: impl Into<Color>) -> Self {
652 self.element.text_style_data.color = Some(Fill::Color(color.into()));
653 self
654 }
655
656 pub fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
657 self.element.text_style_data.font_size = Some(font_size.into());
658 self
659 }
660
661 pub fn overflow<S: Into<Overflow>>(mut self, overflow: S) -> Self {
662 self.element
663 .effect
664 .get_or_insert_with(Default::default)
665 .overflow = overflow.into();
666 self
667 }
668
669 pub fn rotate<R: Into<Option<f32>>>(mut self, rotation: R) -> Self {
670 self.element
671 .effect
672 .get_or_insert_with(Default::default)
673 .rotation = rotation.into();
674 self
675 }
676
677 pub fn scale(mut self, scale: impl Into<Scale>) -> Self {
678 self.element
679 .effect
680 .get_or_insert_with(Default::default)
681 .scale = Some(scale.into());
682 self
683 }
684
685 pub fn transform_origin(mut self, transform_origin: impl Into<TransformOrigin>) -> Self {
689 self.element
690 .effect
691 .get_or_insert_with(Default::default)
692 .transform_origin = transform_origin.into();
693 self
694 }
695
696 pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
697 self.element
698 .effect
699 .get_or_insert_with(Default::default)
700 .opacity = Some(opacity.into());
701 self
702 }
703
704 pub fn blur(mut self, blur: impl Into<f32>) -> Self {
705 self.element
706 .effect
707 .get_or_insert_with(Default::default)
708 .blur = Some(blur.into());
709 self
710 }
711}