1use pathfinder_geometry::rect::RectF;
2
3use crate::{
4 color::ColorU,
5 geometry::vector::{vec2f, Vector2F},
6 scene::{self, Border, Quad},
7 AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
8 SizeConstraint,
9};
10
11pub struct Container {
12 margin: Margin,
13 padding: Padding,
14 background_color: Option<ColorU>,
15 border: Border,
16 corner_radius: f32,
17 shadow: Option<Shadow>,
18 child: ElementBox,
19}
20
21impl Container {
22 pub fn new(child: ElementBox) -> Self {
23 Self {
24 margin: Margin::default(),
25 padding: Padding::default(),
26 background_color: None,
27 border: Border::default(),
28 corner_radius: 0.0,
29 shadow: None,
30 child,
31 }
32 }
33
34 pub fn with_margin_top(mut self, margin: f32) -> Self {
35 self.margin.top = margin;
36 self
37 }
38
39 pub fn with_uniform_padding(mut self, padding: f32) -> Self {
40 self.padding = Padding {
41 top: padding,
42 left: padding,
43 bottom: padding,
44 right: padding,
45 };
46 self
47 }
48
49 pub fn with_padding_right(mut self, padding: f32) -> Self {
50 self.padding.right = padding;
51 self
52 }
53
54 pub fn with_padding_bottom(mut self, padding: f32) -> Self {
55 self.padding.bottom = padding;
56 self
57 }
58
59 pub fn with_background_color(mut self, color: impl Into<ColorU>) -> Self {
60 self.background_color = Some(color.into());
61 self
62 }
63
64 pub fn with_border(mut self, border: Border) -> Self {
65 self.border = border;
66 self
67 }
68
69 pub fn with_corner_radius(mut self, radius: f32) -> Self {
70 self.corner_radius = radius;
71 self
72 }
73
74 pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into<ColorU>) -> Self {
75 self.shadow = Some(Shadow {
76 offset,
77 blur,
78 color: color.into(),
79 });
80 self
81 }
82
83 fn margin_size(&self) -> Vector2F {
84 vec2f(
85 self.margin.left + self.margin.right,
86 self.margin.top + self.margin.bottom,
87 )
88 }
89
90 fn padding_size(&self) -> Vector2F {
91 vec2f(
92 self.padding.left + self.padding.right,
93 self.padding.top + self.padding.bottom,
94 )
95 }
96
97 fn border_size(&self) -> Vector2F {
98 let mut x = 0.0;
99 if self.border.left {
100 x += self.border.width;
101 }
102 if self.border.right {
103 x += self.border.width;
104 }
105
106 let mut y = 0.0;
107 if self.border.top {
108 y += self.border.width;
109 }
110 if self.border.bottom {
111 y += self.border.width;
112 }
113
114 vec2f(x, y)
115 }
116}
117
118impl Element for Container {
119 type LayoutState = ();
120 type PaintState = ();
121
122 fn layout(
123 &mut self,
124 constraint: SizeConstraint,
125 ctx: &mut LayoutContext,
126 ) -> (Vector2F, Self::LayoutState) {
127 let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
128 let child_constraint = SizeConstraint {
129 min: (constraint.min - size_buffer).max(Vector2F::zero()),
130 max: (constraint.max - size_buffer).max(Vector2F::zero()),
131 };
132 let child_size = self.child.layout(child_constraint, ctx);
133 (child_size + size_buffer, ())
134 }
135
136 fn after_layout(
137 &mut self,
138 _: Vector2F,
139 _: &mut Self::LayoutState,
140 ctx: &mut AfterLayoutContext,
141 ) {
142 self.child.after_layout(ctx);
143 }
144
145 fn paint(
146 &mut self,
147 bounds: RectF,
148 _: &mut Self::LayoutState,
149 ctx: &mut PaintContext,
150 ) -> Self::PaintState {
151 let quad_bounds = RectF::from_points(
152 bounds.origin() + vec2f(self.margin.left, self.margin.top),
153 bounds.lower_right() - vec2f(self.margin.right, self.margin.bottom),
154 );
155
156 if let Some(shadow) = self.shadow.as_ref() {
157 ctx.scene.push_shadow(scene::Shadow {
158 bounds: quad_bounds + shadow.offset,
159 corner_radius: self.corner_radius,
160 sigma: shadow.blur,
161 color: shadow.color,
162 });
163 }
164 ctx.scene.push_quad(Quad {
165 bounds: quad_bounds,
166 background: self.background_color,
167 border: self.border,
168 corner_radius: self.corner_radius,
169 });
170
171 let child_origin = quad_bounds.origin()
172 + vec2f(self.padding.left, self.padding.top)
173 + vec2f(self.border.left_width(), self.border.top_width());
174 self.child.paint(child_origin, ctx);
175 }
176
177 fn dispatch_event(
178 &mut self,
179 event: &Event,
180 _: RectF,
181 _: &mut Self::LayoutState,
182 _: &mut Self::PaintState,
183 ctx: &mut EventContext,
184 ) -> bool {
185 self.child.dispatch_event(event, ctx)
186 }
187}
188
189#[derive(Default)]
190pub struct Margin {
191 top: f32,
192 left: f32,
193 bottom: f32,
194 right: f32,
195}
196
197#[derive(Default)]
198pub struct Padding {
199 top: f32,
200 left: f32,
201 bottom: f32,
202 right: f32,
203}
204
205#[derive(Default)]
206pub struct Shadow {
207 offset: Vector2F,
208 blur: f32,
209 color: ColorU,
210}