agg_gui/widgets/
container.rs1use crate::color::Color;
9use crate::draw_ctx::DrawCtx;
10use crate::event::{Event, EventResult};
11use crate::geometry::{Rect, Size};
12use crate::layout_props::{HAnchor, Insets, VAnchor, WidgetBase};
13use crate::widget::Widget;
14
15#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
17#[derive(Clone, Debug)]
18pub struct ContainerProps {
19 pub background: Color,
20 pub border_color: Option<Color>,
21 pub border_width: f64,
22 pub corner_radius: f64,
23 pub inner_padding: Insets,
24 pub fit_height: bool,
29}
30
31impl Default for ContainerProps {
32 fn default() -> Self {
33 Self {
34 background: Color::rgba(0.0, 0.0, 0.0, 0.0),
35 border_color: None,
36 border_width: 1.0,
37 corner_radius: 0.0,
38 inner_padding: Insets::ZERO,
39 fit_height: false,
40 }
41 }
42}
43
44pub struct Container {
50 bounds: Rect,
51 children: Vec<Box<dyn Widget>>,
52 base: WidgetBase,
53 pub props: ContainerProps,
54}
55
56impl Container {
57 pub fn new() -> Self {
59 Self {
60 bounds: Rect::default(),
61 children: Vec::new(),
62 base: WidgetBase::new(),
63 props: ContainerProps::default(),
64 }
65 }
66
67 pub fn with_fit_height(mut self, fit: bool) -> Self {
74 self.props.fit_height = fit;
75 self
76 }
77
78 pub fn add(mut self, child: Box<dyn Widget>) -> Self {
80 self.children.push(child);
81 self
82 }
83
84 pub fn with_background(mut self, color: Color) -> Self {
85 self.props.background = color;
86 self
87 }
88
89 pub fn with_border(mut self, color: Color, width: f64) -> Self {
90 self.props.border_color = Some(color);
91 self.props.border_width = width;
92 self
93 }
94
95 pub fn with_corner_radius(mut self, r: f64) -> Self {
96 self.props.corner_radius = r;
97 self
98 }
99
100 pub fn with_padding(mut self, p: f64) -> Self {
101 self.props.inner_padding = Insets::all(p);
102 self
103 }
104
105 pub fn with_inner_padding(mut self, p: Insets) -> Self {
106 self.props.inner_padding = p;
107 self
108 }
109
110 pub fn with_margin(mut self, m: Insets) -> Self {
111 self.base.margin = m;
112 self
113 }
114 pub fn with_h_anchor(mut self, h: HAnchor) -> Self {
115 self.base.h_anchor = h;
116 self
117 }
118 pub fn with_v_anchor(mut self, v: VAnchor) -> Self {
119 self.base.v_anchor = v;
120 self
121 }
122 pub fn with_min_size(mut self, s: Size) -> Self {
123 self.base.min_size = s;
124 self
125 }
126 pub fn with_max_size(mut self, s: Size) -> Self {
127 self.base.max_size = s;
128 self
129 }
130}
131
132impl Default for Container {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138impl Widget for Container {
139 fn type_name(&self) -> &'static str {
140 "Container"
141 }
142 fn bounds(&self) -> Rect {
143 self.bounds
144 }
145 fn set_bounds(&mut self, bounds: Rect) {
146 self.bounds = bounds;
147 }
148
149 fn children(&self) -> &[Box<dyn Widget>] {
150 &self.children
151 }
152 fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
153 &mut self.children
154 }
155
156 #[cfg(feature = "reflect")]
157 fn as_reflect(&self) -> Option<&dyn bevy_reflect::Reflect> {
158 Some(&self.props)
159 }
160 #[cfg(feature = "reflect")]
161 fn as_reflect_mut(&mut self) -> Option<&mut dyn bevy_reflect::Reflect> {
162 Some(&mut self.props)
163 }
164
165 fn margin(&self) -> Insets {
166 self.base.margin
167 }
168 fn widget_base(&self) -> Option<&WidgetBase> {
169 Some(&self.base)
170 }
171 fn widget_base_mut(&mut self) -> Option<&mut WidgetBase> {
172 Some(&mut self.base)
173 }
174 fn padding(&self) -> Insets {
175 self.props.inner_padding
176 }
177 fn h_anchor(&self) -> HAnchor {
178 self.base.h_anchor
179 }
180 fn v_anchor(&self) -> VAnchor {
181 self.base.v_anchor
182 }
183 fn min_size(&self) -> Size {
184 self.base.min_size
185 }
186 fn max_size(&self) -> Size {
187 self.base.max_size
188 }
189
190 fn layout(&mut self, available: Size) -> Size {
191 let pad_l = self.props.inner_padding.left;
192 let pad_r = self.props.inner_padding.right;
193 let pad_t = self.props.inner_padding.top;
194 let pad_b = self.props.inner_padding.bottom;
195 let inner_w = (available.width - pad_l - pad_r).max(0.0);
196
197 fn layout_children(
198 children: &mut [Box<dyn Widget>],
199 inner_w: f64,
200 pad_l: f64,
201 pad_t: f64,
202 pad_b: f64,
203 height: f64,
204 ) -> f64 {
205 let start_cursor = height - pad_t;
208 let mut cursor_y = start_cursor;
209
210 for child in children.iter_mut() {
211 let m = child.margin();
214 let avail_w = (inner_w - m.left - m.right).max(0.0);
215 let avail_h = (cursor_y - pad_b - m.top - m.bottom).max(0.0);
216 let desired = child.layout(Size::new(avail_w, avail_h));
217
218 cursor_y -= m.top;
219 let child_y = cursor_y - desired.height;
220 child.set_bounds(Rect::new(
221 pad_l + m.left,
222 child_y,
223 desired.width.min(avail_w),
224 desired.height,
225 ));
226 cursor_y = child_y - m.bottom;
227 }
228
229 (start_cursor - cursor_y).max(0.0)
230 }
231
232 let consumed_h = layout_children(
233 &mut self.children,
234 inner_w,
235 pad_l,
236 pad_t,
237 pad_b,
238 available.height,
239 );
240
241 if self.props.fit_height {
246 let natural_h = (consumed_h + pad_t + pad_b).min(available.height);
247 if (available.height - natural_h).abs() > 0.5 {
251 layout_children(
252 &mut self.children,
253 inner_w,
254 pad_l,
255 pad_t,
256 pad_b,
257 natural_h,
258 );
259 }
260 Size::new(available.width, natural_h)
261 } else {
262 Size::new(available.width, available.height)
263 }
264 }
265
266 fn paint(&mut self, ctx: &mut dyn DrawCtx) {
267 let w = self.bounds.width;
268 let h = self.bounds.height;
269 let r = self.props.corner_radius;
270
271 if self.props.background.a > 0.001 {
273 ctx.set_fill_color(self.props.background);
274 ctx.begin_path();
275 ctx.rounded_rect(0.0, 0.0, w, h, r);
276 ctx.fill();
277 }
278
279 if let Some(bc) = self.props.border_color {
281 ctx.set_stroke_color(bc);
282 ctx.set_line_width(self.props.border_width);
283 ctx.begin_path();
284 let inset = self.props.border_width * 0.5;
285 ctx.rounded_rect(
286 inset,
287 inset,
288 (w - self.props.border_width).max(0.0),
289 (h - self.props.border_width).max(0.0),
290 r,
291 );
292 ctx.stroke();
293 }
294 }
295
296 fn on_event(&mut self, _event: &Event) -> EventResult {
297 EventResult::Ignored
298 }
299}