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