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