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 render_shadow(
99 canvas: &Canvas,
100 path: &mut SkPath,
101 rounded_rect: SkRRect,
102 _area: Area,
103 shadow: &Shadow,
104 corner_radius: &CornerRadius,
105 ) {
106 let mut shadow_path = PathBuilder::new();
107 let mut shadow_paint = Paint::default();
108 shadow_paint.set_anti_alias(true);
109 shadow_paint.set_color(shadow.color);
110
111 let outset: SkPoint = match shadow.position {
115 ShadowPosition::Normal => {
116 shadow_paint.set_style(PaintStyle::Fill);
117 (shadow.spread, shadow.spread).into()
118 }
119 ShadowPosition::Inset => {
120 shadow_paint.set_style(PaintStyle::Stroke);
121 shadow_paint.set_stroke_width(shadow.blur / 2.0 + shadow.spread);
122 (-shadow.spread / 2.0, -shadow.spread / 2.0).into()
123 }
124 };
125
126 if shadow.blur > 0.0 {
128 shadow_paint.set_mask_filter(SkMaskFilter::blur(
129 SkBlurStyle::Normal,
130 shadow.blur / 2.0,
131 false,
132 ));
133 }
134
135 if corner_radius.smoothing > 0.0 {
137 shadow_path.add_path(
138 &corner_radius.smoothed_path(rounded_rect.with_outset(outset)),
139 None,
140 );
141 } else {
142 shadow_path.add_rrect(rounded_rect.with_outset(outset), None, None);
143 }
144
145 shadow_path.offset((shadow.x, shadow.y));
147
148 canvas.save();
150 canvas.clip_path(
151 path,
152 match shadow.position {
153 ShadowPosition::Normal => ClipOp::Difference,
154 ShadowPosition::Inset => ClipOp::Intersect,
155 },
156 true,
157 );
158 let shadow_path = shadow_path.detach();
159 canvas.draw_path(&shadow_path, &shadow_paint);
160 canvas.restore();
161 }
162
163 pub fn render_border(
164 canvas: &Canvas,
165 rect: SkRect,
166 border: &Border,
167 corner_radius: &CornerRadius,
168 ) {
169 let mut border_paint = Paint::default();
170 border_paint.set_style(PaintStyle::Fill);
171 border_paint.set_anti_alias(true);
172 border_paint.set_color(border.fill);
173
174 match Self::border_shape(rect, corner_radius, border) {
175 BorderShape::DRRect(outer, inner) => {
176 canvas.draw_drrect(outer, inner, &border_paint);
177 }
178 BorderShape::Path(path) => {
179 canvas.draw_path(&path, &border_paint);
180 }
181 }
182 }
183
184 pub fn border_shape(
188 base_rect: SkRect,
189 base_corner_radius: &CornerRadius,
190 border: &Border,
191 ) -> BorderShape {
192 let border_alignment = border.alignment;
193 let border_width = border.width;
194
195 let (outer_rrect, outer_corner_radius) = {
199 let corner_radius = CornerRadius {
201 top_left: Self::outer_border_path_corner_radius(
202 border_alignment,
203 base_corner_radius.top_left,
204 border_width.top,
205 border_width.left,
206 ),
207 top_right: Self::outer_border_path_corner_radius(
208 border_alignment,
209 base_corner_radius.top_right,
210 border_width.top,
211 border_width.right,
212 ),
213 bottom_left: Self::outer_border_path_corner_radius(
214 border_alignment,
215 base_corner_radius.bottom_left,
216 border_width.bottom,
217 border_width.left,
218 ),
219 bottom_right: Self::outer_border_path_corner_radius(
220 border_alignment,
221 base_corner_radius.bottom_right,
222 border_width.bottom,
223 border_width.right,
224 ),
225 smoothing: base_corner_radius.smoothing,
226 };
227
228 let rrect = SkRRect::new_rect_radii(
229 {
230 let mut rect = base_rect;
231 let alignment_scale = match border_alignment {
232 BorderAlignment::Outer => 1.0,
233 BorderAlignment::Center => 0.5,
234 BorderAlignment::Inner => 0.0,
235 };
236
237 rect.left -= border_width.left * alignment_scale;
238 rect.top -= border_width.top * alignment_scale;
239 rect.right += border_width.right * alignment_scale;
240 rect.bottom += border_width.bottom * alignment_scale;
241
242 rect
243 },
244 &[
245 (corner_radius.top_left, corner_radius.top_left).into(),
246 (corner_radius.top_right, corner_radius.top_right).into(),
247 (corner_radius.bottom_right, corner_radius.bottom_right).into(),
248 (corner_radius.bottom_left, corner_radius.bottom_left).into(),
249 ],
250 );
251
252 (rrect, corner_radius)
253 };
254
255 let (inner_rrect, inner_corner_radius) = {
257 let corner_radius = CornerRadius {
259 top_left: Self::inner_border_path_corner_radius(
260 border_alignment,
261 base_corner_radius.top_left,
262 border_width.top,
263 border_width.left,
264 ),
265 top_right: Self::inner_border_path_corner_radius(
266 border_alignment,
267 base_corner_radius.top_right,
268 border_width.top,
269 border_width.right,
270 ),
271 bottom_left: Self::inner_border_path_corner_radius(
272 border_alignment,
273 base_corner_radius.bottom_left,
274 border_width.bottom,
275 border_width.left,
276 ),
277 bottom_right: Self::inner_border_path_corner_radius(
278 border_alignment,
279 base_corner_radius.bottom_right,
280 border_width.bottom,
281 border_width.right,
282 ),
283 smoothing: base_corner_radius.smoothing,
284 };
285
286 let rrect = SkRRect::new_rect_radii(
287 {
288 let mut rect = base_rect;
289 let alignment_scale = match border_alignment {
290 BorderAlignment::Outer => 0.0,
291 BorderAlignment::Center => 0.5,
292 BorderAlignment::Inner => 1.0,
293 };
294
295 rect.left += border_width.left * alignment_scale;
296 rect.top += border_width.top * alignment_scale;
297 rect.right -= border_width.right * alignment_scale;
298 rect.bottom -= border_width.bottom * alignment_scale;
299
300 rect
301 },
302 &[
303 (corner_radius.top_left, corner_radius.top_left).into(),
304 (corner_radius.top_right, corner_radius.top_right).into(),
305 (corner_radius.bottom_right, corner_radius.bottom_right).into(),
306 (corner_radius.bottom_left, corner_radius.bottom_left).into(),
307 ],
308 );
309
310 (rrect, corner_radius)
311 };
312
313 if base_corner_radius.smoothing > 0.0 {
314 let mut path = PathBuilder::new();
315 path.set_fill_type(SkPathFillType::EvenOdd);
316
317 path.add_path(&outer_corner_radius.smoothed_path(outer_rrect), None);
318
319 path.add_path(&inner_corner_radius.smoothed_path(inner_rrect), None);
320
321 let path = path.detach();
322 BorderShape::Path(path)
323 } else {
324 BorderShape::DRRect(outer_rrect, inner_rrect)
325 }
326 }
327
328 fn outer_border_path_corner_radius(
329 alignment: BorderAlignment,
330 corner_radius: f32,
331 width_1: f32,
332 width_2: f32,
333 ) -> f32 {
334 if alignment == BorderAlignment::Inner || corner_radius == 0.0 {
335 return corner_radius;
336 }
337
338 let mut offset = if width_1 == 0.0 {
339 width_2
340 } else if width_2 == 0.0 {
341 width_1
342 } else {
343 width_1.min(width_2)
344 };
345
346 if alignment == BorderAlignment::Center {
347 offset *= 0.5;
348 }
349
350 corner_radius + offset
351 }
352
353 fn inner_border_path_corner_radius(
354 alignment: BorderAlignment,
355 corner_radius: f32,
356 width_1: f32,
357 width_2: f32,
358 ) -> f32 {
359 if alignment == BorderAlignment::Outer || corner_radius == 0.0 {
360 return corner_radius;
361 }
362
363 let mut offset = if width_1 == 0.0 {
364 width_2
365 } else if width_2 == 0.0 {
366 width_1
367 } else {
368 width_1.min(width_2)
369 };
370
371 if alignment == BorderAlignment::Center {
372 offset *= 0.5;
373 }
374
375 corner_radius - offset
376 }
377}
378
379impl ElementExt for RectElement {
380 fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
381 let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
382 return false;
383 };
384
385 self != rect
386 }
387
388 fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
389 let Some(rect) = (other.as_ref() as &dyn Any).downcast_ref::<Self>() else {
390 return DiffModifies::all();
391 };
392
393 let mut diff = DiffModifies::empty();
394
395 if self.style != rect.style {
396 diff.insert(DiffModifies::STYLE);
397 }
398
399 if self.effect != rect.effect {
400 diff.insert(DiffModifies::EFFECT);
401 }
402
403 if !self.layout.self_layout_eq(&rect.layout.layout) {
404 diff.insert(DiffModifies::STYLE);
405 diff.insert(DiffModifies::LAYOUT);
406 }
407
408 if !self.layout.inner_layout_eq(&rect.layout.layout) {
409 diff.insert(DiffModifies::STYLE);
410 diff.insert(DiffModifies::INNER_LAYOUT);
411 }
412
413 if self.accessibility != rect.accessibility {
414 diff.insert(DiffModifies::ACCESSIBILITY);
415 }
416
417 if self.relative_layer != rect.relative_layer {
418 diff.insert(DiffModifies::LAYER);
419 }
420
421 if self.event_handlers != rect.event_handlers {
422 diff.insert(DiffModifies::EVENT_HANDLERS);
423 }
424
425 if self.text_style_data != rect.text_style_data {
426 diff.insert(DiffModifies::TEXT_STYLE);
427 }
428
429 diff
430 }
431
432 fn layout(&'_ self) -> Cow<'_, LayoutData> {
433 Cow::Borrowed(&self.layout)
434 }
435
436 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
437 self.effect.as_ref().map(Cow::Borrowed)
438 }
439
440 fn style(&'_ self) -> Cow<'_, StyleState> {
441 Cow::Borrowed(&self.style)
442 }
443
444 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
445 Cow::Borrowed(&self.text_style_data)
446 }
447
448 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
449 Cow::Borrowed(&self.accessibility)
450 }
451
452 fn layer(&self) -> Layer {
453 self.relative_layer
454 }
455
456 fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
457 Some(Cow::Borrowed(&self.event_handlers))
458 }
459
460 fn is_point_inside(&self, context: EventMeasurementContext) -> bool {
463 let area = context.layout_node.visible_area();
464 let cursor = context.cursor.to_f32();
465 let local_area = Area::new((0., 0.).into(), area.size);
466 let rounded_rect = self.render_rect(&local_area, context.scale_factor as f32);
467 let local_x = cursor.x - area.min_x();
468 let local_y = cursor.y - area.min_y();
469 rounded_rect.contains(SkRect::new(
470 local_x,
471 local_y,
472 local_x.next_up(),
473 local_y.next_up(),
474 ))
475 }
476
477 fn clip(&self, context: ClipContext) {
478 let area = context.visible_area;
479
480 let rounded_rect = self.render_rect(area, context.scale_factor as f32);
481
482 context
483 .canvas
484 .clip_rrect(rounded_rect, ClipOp::Intersect, true);
485 }
486
487 fn render(&self, context: RenderContext) {
488 let style = self.style();
489
490 let area = context.layout_node.visible_area();
491 let corner_radius = style.corner_radius.with_scale(context.scale_factor as f32);
492
493 let mut path = PathBuilder::new();
494 let mut paint = Paint::default();
495 paint.set_anti_alias(true);
496 paint.set_style(PaintStyle::Fill);
497 style.background.apply_to_paint(&mut paint, area);
498
499 let rounded_rect = self.render_rect(&area, context.scale_factor as f32);
501 if corner_radius.smoothing > 0.0 {
502 path.add_path(&corner_radius.smoothed_path(rounded_rect), None);
503 } else {
504 path.add_rrect(rounded_rect, None, None);
505 }
506
507 let mut path = path.detach();
508 context.canvas.draw_path(&path, &paint);
509
510 for shadow in style.shadows.iter() {
512 if shadow.color != Color::TRANSPARENT {
513 let shadow = shadow.with_scale(context.scale_factor as f32);
514
515 Self::render_shadow(
516 context.canvas,
517 &mut path,
518 rounded_rect,
519 area,
520 &shadow,
521 &corner_radius,
522 );
523 }
524 }
525
526 for border in style.borders.iter() {
528 if border.is_visible() {
529 let border = border.with_scale(context.scale_factor as f32);
530 let rect = *rounded_rect.rect();
531 Self::render_border(context.canvas, rect, &border, &corner_radius);
532 }
533 }
534 }
535}
536
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 FxHashMap<EventName, EventHandlerType> {
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 empty() -> Self {
639 Self {
640 element: RectElement::default(),
641 elements: Vec::default(),
642 key: DiffKey::None,
643 }
644 }
645
646 pub fn try_downcast(element: &dyn ElementExt) -> Option<RectElement> {
647 (element as &dyn Any).downcast_ref::<RectElement>().cloned()
648 }
649
650 pub fn color(mut self, color: impl Into<Color>) -> Self {
651 self.element.text_style_data.color = Some(Fill::Color(color.into()));
652 self
653 }
654
655 pub fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
656 self.element.text_style_data.font_size = Some(font_size.into());
657 self
658 }
659
660 pub fn overflow<S: Into<Overflow>>(mut self, overflow: S) -> Self {
661 self.element
662 .effect
663 .get_or_insert_with(Default::default)
664 .overflow = overflow.into();
665 self
666 }
667
668 pub fn rotate<R: Into<Option<f32>>>(mut self, rotation: R) -> Self {
669 self.element
670 .effect
671 .get_or_insert_with(Default::default)
672 .rotation = rotation.into();
673 self
674 }
675
676 pub fn scale(mut self, scale: impl Into<Scale>) -> Self {
677 self.element
678 .effect
679 .get_or_insert_with(Default::default)
680 .scale = Some(scale.into());
681 self
682 }
683
684 pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
685 self.element
686 .effect
687 .get_or_insert_with(Default::default)
688 .opacity = Some(opacity.into());
689 self
690 }
691
692 pub fn blur(mut self, blur: impl Into<f32>) -> Self {
693 self.element
694 .effect
695 .get_or_insert_with(Default::default)
696 .blur = Some(blur.into());
697 self
698 }
699}