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 overdraw: Overdraw,
15 background_color: Option<ColorU>,
16 border: Border,
17 corner_radius: f32,
18 shadow: Option<Shadow>,
19 child: ElementBox,
20}
21
22impl Container {
23 pub fn new(child: ElementBox) -> Self {
24 Self {
25 margin: Margin::default(),
26 padding: Padding::default(),
27 overdraw: Overdraw::default(),
28 background_color: None,
29 border: Border::default(),
30 corner_radius: 0.0,
31 shadow: None,
32 child,
33 }
34 }
35
36 pub fn with_margin_top(mut self, margin: f32) -> Self {
37 self.margin.top = margin;
38 self
39 }
40
41 pub fn with_uniform_padding(mut self, padding: f32) -> Self {
42 self.padding = Padding {
43 top: padding,
44 left: padding,
45 bottom: padding,
46 right: padding,
47 };
48 self
49 }
50
51 pub fn with_padding_right(mut self, padding: f32) -> Self {
52 self.padding.right = padding;
53 self
54 }
55
56 pub fn with_padding_bottom(mut self, padding: f32) -> Self {
57 self.padding.bottom = padding;
58 self
59 }
60
61 pub fn with_background_color(mut self, color: impl Into<ColorU>) -> Self {
62 self.background_color = Some(color.into());
63 self
64 }
65
66 pub fn with_border(mut self, border: Border) -> Self {
67 self.border = border;
68 self
69 }
70
71 pub fn with_overdraw_bottom(mut self, overdraw: f32) -> Self {
72 self.overdraw.bottom = overdraw;
73 self
74 }
75
76 pub fn with_corner_radius(mut self, radius: f32) -> Self {
77 self.corner_radius = radius;
78 self
79 }
80
81 pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into<ColorU>) -> Self {
82 self.shadow = Some(Shadow {
83 offset,
84 blur,
85 color: color.into(),
86 });
87 self
88 }
89
90 fn margin_size(&self) -> Vector2F {
91 vec2f(
92 self.margin.left + self.margin.right,
93 self.margin.top + self.margin.bottom,
94 )
95 }
96
97 fn padding_size(&self) -> Vector2F {
98 vec2f(
99 self.padding.left + self.padding.right,
100 self.padding.top + self.padding.bottom,
101 )
102 }
103
104 fn border_size(&self) -> Vector2F {
105 let mut x = 0.0;
106 if self.border.left {
107 x += self.border.width;
108 }
109 if self.border.right {
110 x += self.border.width;
111 }
112
113 let mut y = 0.0;
114 if self.border.top {
115 y += self.border.width;
116 }
117 if self.border.bottom {
118 y += self.border.width;
119 }
120
121 vec2f(x, y)
122 }
123}
124
125impl Element for Container {
126 type LayoutState = ();
127 type PaintState = ();
128
129 fn layout(
130 &mut self,
131 constraint: SizeConstraint,
132 ctx: &mut LayoutContext,
133 ) -> (Vector2F, Self::LayoutState) {
134 let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
135 let child_constraint = SizeConstraint {
136 min: (constraint.min - size_buffer).max(Vector2F::zero()),
137 max: (constraint.max - size_buffer).max(Vector2F::zero()),
138 };
139 let child_size = self.child.layout(child_constraint, ctx);
140 (child_size + size_buffer, ())
141 }
142
143 fn after_layout(
144 &mut self,
145 _: Vector2F,
146 _: &mut Self::LayoutState,
147 ctx: &mut AfterLayoutContext,
148 ) {
149 self.child.after_layout(ctx);
150 }
151
152 fn paint(
153 &mut self,
154 bounds: RectF,
155 _: &mut Self::LayoutState,
156 ctx: &mut PaintContext,
157 ) -> Self::PaintState {
158 let quad_bounds = RectF::from_points(
159 bounds.origin() + vec2f(self.margin.left, self.margin.top),
160 bounds.lower_right() - vec2f(self.margin.right, self.margin.bottom),
161 );
162
163 if let Some(shadow) = self.shadow.as_ref() {
164 ctx.scene.push_shadow(scene::Shadow {
165 bounds: quad_bounds + shadow.offset,
166 corner_radius: self.corner_radius,
167 sigma: shadow.blur,
168 color: shadow.color,
169 });
170 }
171 ctx.scene.push_quad(Quad {
172 bounds: quad_bounds,
173 background: self.background_color,
174 border: self.border,
175 corner_radius: self.corner_radius,
176 });
177
178 let child_origin = quad_bounds.origin() + vec2f(self.padding.left, self.padding.top);
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 Overdraw {
212 top: f32,
213 left: f32,
214 bottom: f32,
215 right: f32,
216}
217
218#[derive(Default)]
219pub struct Shadow {
220 offset: Vector2F,
221 blur: f32,
222 color: ColorU,
223}