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