agg_gui/widgets/
container.rs1use crate::color::Color;
9use crate::device_scale::device_scale;
10use crate::draw_ctx::DrawCtx;
11use crate::event::{Event, EventResult};
12use crate::geometry::{Rect, Size};
13use crate::layout_props::{HAnchor, Insets, VAnchor, WidgetBase};
14use crate::widget::Widget;
15
16pub struct Container {
22 bounds: Rect,
23 children: Vec<Box<dyn Widget>>,
24 base: WidgetBase,
25 pub background: Color,
26 pub border_color: Option<Color>,
27 pub border_width: f64,
28 pub corner_radius: f64,
29 pub inner_padding: Insets,
30 pub fit_height: bool,
35}
36
37impl Container {
38 pub fn new() -> Self {
40 Self {
41 bounds: Rect::default(),
42 children: Vec::new(),
43 base: WidgetBase::new(),
44 background: Color::rgba(0.0, 0.0, 0.0, 0.0),
45 border_color: None,
46 border_width: 1.0,
47 corner_radius: 0.0,
48 inner_padding: Insets::ZERO,
49 fit_height: false,
50 }
51 }
52
53 pub fn with_fit_height(mut self, fit: bool) -> Self {
60 self.fit_height = fit;
61 self
62 }
63
64 pub fn add(mut self, child: Box<dyn Widget>) -> Self {
66 self.children.push(child);
67 self
68 }
69
70 pub fn with_background(mut self, color: Color) -> Self {
71 self.background = color;
72 self
73 }
74
75 pub fn with_border(mut self, color: Color, width: f64) -> Self {
76 self.border_color = Some(color);
77 self.border_width = width;
78 self
79 }
80
81 pub fn with_corner_radius(mut self, r: f64) -> Self {
82 self.corner_radius = r;
83 self
84 }
85
86 pub fn with_padding(mut self, p: f64) -> Self {
87 self.inner_padding = Insets::all(p);
88 self
89 }
90
91 pub fn with_inner_padding(mut self, p: Insets) -> Self {
92 self.inner_padding = p;
93 self
94 }
95
96 pub fn with_margin(mut self, m: Insets) -> Self {
97 self.base.margin = m;
98 self
99 }
100 pub fn with_h_anchor(mut self, h: HAnchor) -> Self {
101 self.base.h_anchor = h;
102 self
103 }
104 pub fn with_v_anchor(mut self, v: VAnchor) -> Self {
105 self.base.v_anchor = v;
106 self
107 }
108 pub fn with_min_size(mut self, s: Size) -> Self {
109 self.base.min_size = s;
110 self
111 }
112 pub fn with_max_size(mut self, s: Size) -> Self {
113 self.base.max_size = s;
114 self
115 }
116}
117
118impl Default for Container {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124impl Widget for Container {
125 fn type_name(&self) -> &'static str {
126 "Container"
127 }
128 fn bounds(&self) -> Rect {
129 self.bounds
130 }
131 fn set_bounds(&mut self, bounds: Rect) {
132 self.bounds = bounds;
133 }
134
135 fn children(&self) -> &[Box<dyn Widget>] {
136 &self.children
137 }
138 fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
139 &mut self.children
140 }
141
142 fn margin(&self) -> Insets {
143 self.base.margin
144 }
145 fn h_anchor(&self) -> HAnchor {
146 self.base.h_anchor
147 }
148 fn v_anchor(&self) -> VAnchor {
149 self.base.v_anchor
150 }
151 fn min_size(&self) -> Size {
152 self.base.min_size
153 }
154 fn max_size(&self) -> Size {
155 self.base.max_size
156 }
157
158 fn layout(&mut self, available: Size) -> Size {
159 let pad_l = self.inner_padding.left;
160 let pad_r = self.inner_padding.right;
161 let pad_t = self.inner_padding.top;
162 let pad_b = self.inner_padding.bottom;
163 let inner_w = (available.width - pad_l - pad_r).max(0.0);
164
165 let scale = device_scale();
171 let start_cursor = available.height - pad_t;
172 let mut cursor_y = start_cursor;
173
174 for child in self.children.iter_mut() {
175 let m = child.margin().scale(scale);
176 let avail_w = (inner_w - m.left - m.right).max(0.0);
177 let avail_h = (cursor_y - pad_b - m.top - m.bottom).max(0.0);
178 let desired = child.layout(Size::new(avail_w, avail_h));
179
180 cursor_y -= m.top;
182 let child_y = cursor_y - desired.height;
183 let child_bounds = Rect::new(
184 pad_l + m.left,
185 child_y,
186 desired.width.min(avail_w),
187 desired.height,
188 );
189 child.set_bounds(child_bounds);
190 cursor_y = child_y - m.bottom;
192 }
193
194 if self.fit_height {
199 let consumed_h = (start_cursor - cursor_y).max(0.0);
200 let natural_h = (consumed_h + pad_t + pad_b).min(available.height);
201 Size::new(available.width, natural_h)
202 } else {
203 Size::new(available.width, available.height)
204 }
205 }
206
207 fn paint(&mut self, ctx: &mut dyn DrawCtx) {
208 let w = self.bounds.width;
209 let h = self.bounds.height;
210 let r = self.corner_radius;
211
212 if self.background.a > 0.001 {
214 ctx.set_fill_color(self.background);
215 ctx.begin_path();
216 ctx.rounded_rect(0.0, 0.0, w, h, r);
217 ctx.fill();
218 }
219
220 if let Some(bc) = self.border_color {
222 ctx.set_stroke_color(bc);
223 ctx.set_line_width(self.border_width);
224 ctx.begin_path();
225 let inset = self.border_width * 0.5;
226 ctx.rounded_rect(
227 inset,
228 inset,
229 (w - self.border_width).max(0.0),
230 (h - self.border_width).max(0.0),
231 r,
232 );
233 ctx.stroke();
234 }
235 }
236
237 fn on_event(&mut self, _event: &Event) -> EventResult {
238 EventResult::Ignored
239 }
240}