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