Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/elements/debug.rs
1//! Module containing the definition of [`DebugElement`].
2 
3use super::{
4 AfterLayoutContext, AppContext, Element, EventContext, LayoutContext, PaintContext, Point,
5 SizeConstraint,
6};
7use crate::event::DispatchedEvent;
8use crate::scene::{Border, Dash};
9use pathfinder_color::ColorU;
10use pathfinder_geometry::{rect::RectF, vector::Vector2F};
11 
12/// A debug element that draws a dashed around its child. Intended for quick visual debugging.
13pub struct DebugElement {
14 child: Box<dyn Element>,
15 color: ColorU,
16 border_width: f32,
17 dash: Dash,
18 size: Option<Vector2F>,
19 origin: Option<Point>,
20}
21 
22/// Options for configuration of the [`DebugElement`].
23#[derive(Default)]
24pub struct DebugOptions {
25 /// The color to use for the border. Defaults to red.
26 pub color_override: Option<ColorU>,
27 /// The width of the border. Defaults to 2.0.
28 pub border_width_override: Option<f32>,
29 /// The dash pattern to use for the border. Defaults to a 4.0 dash and 2.0 gap.
30 pub dash_override: Option<Dash>,
31}
32 
33impl DebugElement {
34 pub fn new(child: Box<dyn Element>) -> Self {
35 Self::new_with_options(child, DebugOptions::default())
36 }
37 
38 pub fn new_with_options(child: Box<dyn Element>, options: DebugOptions) -> Self {
39 Self {
40 child,
41 color: options
42 .color_override
43 .unwrap_or(ColorU::new(255, 0, 0, 255)),
44 border_width: options.border_width_override.unwrap_or(2.0),
45 dash: options.dash_override.unwrap_or(Dash {
46 dash_length: 4.0,
47 gap_length: 2.0,
48 force_consistent_gap_length: true,
49 }),
50 size: None,
51 origin: None,
52 }
53 }
54}
55 
56impl Element for DebugElement {
57 fn layout(
58 &mut self,
59 constraint: SizeConstraint,
60 ctx: &mut LayoutContext,
61 app: &AppContext,
62 ) -> Vector2F {
63 let child_size = self.child.layout(constraint, ctx, app);
64 self.size = Some(child_size);
65 child_size
66 }
67 
68 fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &AppContext) {
69 self.child.after_layout(ctx, app);
70 }
71 
72 fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
73 self.origin = Some(Point::from_vec2f(origin, ctx.scene.z_index()));
74 
75 self.child.paint(origin, ctx, app);
76 
77 let size = self.size.unwrap_or(Vector2F::zero());
78 
79 // Draw a dashed border around the child without including the size of the
80 // border in the overall size of the element.
81 ctx.scene
82 .draw_rect_with_hit_recording(RectF::new(origin, size))
83 .with_border(
84 Border::all(self.border_width)
85 .with_border_color(self.color)
86 .with_dashed_border(self.dash),
87 );
88 }
89 
90 fn dispatch_event(
91 &mut self,
92 event: &DispatchedEvent,
93 ctx: &mut EventContext,
94 app: &AppContext,
95 ) -> bool {
96 self.child.dispatch_event(event, ctx, app)
97 }
98 
99 fn size(&self) -> Option<Vector2F> {
100 self.size
101 }
102 
103 fn origin(&self) -> Option<Point> {
104 self.origin
105 }
106}
107 
108pub trait Debug {
109 fn debug(self) -> Box<dyn Element>;
110 
111 fn debug_with_options(self, options: DebugOptions) -> Box<dyn Element>;
112}
113 
114impl Debug for Box<dyn Element> {
115 fn debug(self) -> Box<dyn Element> {
116 DebugElement::new(self).finish()
117 }
118 
119 fn debug_with_options(self, options: DebugOptions) -> Box<dyn Element> {
120 Box::new(DebugElement::new_with_options(self, options))
121 }
122}
123