1use crate::alignment::{self, Alignment};
3use crate::event::{self, Event};
4use crate::layout;
5use crate::mouse;
6use crate::overlay;
7use crate::renderer;
8use crate::widget::{self, Operation, Tree};
9use crate::{
10 Background, Clipboard, Color, Element, Layout, Length, Padding, Pixels,
11 Point, Rectangle, Shell, Widget,
12};
13
14pub use iced_style::container::{Appearance, StyleSheet};
15
16#[allow(missing_debug_implementations)]
20pub struct Container<'a, Message, Renderer>
21where
22 Renderer: crate::Renderer,
23 Renderer::Theme: StyleSheet,
24{
25 id: Option<Id>,
26 padding: Padding,
27 width: Length,
28 height: Length,
29 max_width: f32,
30 max_height: f32,
31 horizontal_alignment: alignment::Horizontal,
32 vertical_alignment: alignment::Vertical,
33 style: <Renderer::Theme as StyleSheet>::Style,
34 content: Element<'a, Message, Renderer>,
35}
36
37impl<'a, Message, Renderer> Container<'a, Message, Renderer>
38where
39 Renderer: crate::Renderer,
40 Renderer::Theme: StyleSheet,
41{
42 pub fn new<T>(content: T) -> Self
44 where
45 T: Into<Element<'a, Message, Renderer>>,
46 {
47 Container {
48 id: None,
49 padding: Padding::ZERO,
50 width: Length::Shrink,
51 height: Length::Shrink,
52 max_width: f32::INFINITY,
53 max_height: f32::INFINITY,
54 horizontal_alignment: alignment::Horizontal::Left,
55 vertical_alignment: alignment::Vertical::Top,
56 style: Default::default(),
57 content: content.into(),
58 }
59 }
60
61 pub fn id(mut self, id: Id) -> Self {
63 self.id = Some(id);
64 self
65 }
66
67 pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
69 self.padding = padding.into();
70 self
71 }
72
73 pub fn width(mut self, width: impl Into<Length>) -> Self {
75 self.width = width.into();
76 self
77 }
78
79 pub fn height(mut self, height: impl Into<Length>) -> Self {
81 self.height = height.into();
82 self
83 }
84
85 pub fn max_width(mut self, max_width: impl Into<Pixels>) -> Self {
87 self.max_width = max_width.into().0;
88 self
89 }
90
91 pub fn max_height(mut self, max_height: impl Into<Pixels>) -> Self {
93 self.max_height = max_height.into().0;
94 self
95 }
96
97 pub fn align_x(mut self, alignment: alignment::Horizontal) -> Self {
99 self.horizontal_alignment = alignment;
100 self
101 }
102
103 pub fn align_y(mut self, alignment: alignment::Vertical) -> Self {
105 self.vertical_alignment = alignment;
106 self
107 }
108
109 pub fn center_x(mut self) -> Self {
111 self.horizontal_alignment = alignment::Horizontal::Center;
112 self
113 }
114
115 pub fn center_y(mut self) -> Self {
117 self.vertical_alignment = alignment::Vertical::Center;
118 self
119 }
120
121 pub fn style(
123 mut self,
124 style: impl Into<<Renderer::Theme as StyleSheet>::Style>,
125 ) -> Self {
126 self.style = style.into();
127 self
128 }
129}
130
131impl<'a, Message, Renderer> Widget<Message, Renderer>
132 for Container<'a, Message, Renderer>
133where
134 Renderer: crate::Renderer,
135 Renderer::Theme: StyleSheet,
136{
137 fn children(&self) -> Vec<Tree> {
138 vec![Tree::new(&self.content)]
139 }
140
141 fn diff(&self, tree: &mut Tree) {
142 tree.diff_children(std::slice::from_ref(&self.content))
143 }
144
145 fn width(&self) -> Length {
146 self.width
147 }
148
149 fn height(&self) -> Length {
150 self.height
151 }
152
153 fn layout(
154 &self,
155 renderer: &Renderer,
156 limits: &layout::Limits,
157 ) -> layout::Node {
158 layout(
159 renderer,
160 limits,
161 self.width,
162 self.height,
163 self.max_width,
164 self.max_height,
165 self.padding,
166 self.horizontal_alignment,
167 self.vertical_alignment,
168 |renderer, limits| {
169 self.content.as_widget().layout(renderer, limits)
170 },
171 )
172 }
173
174 fn operate(
175 &self,
176 tree: &mut Tree,
177 layout: Layout<'_>,
178 renderer: &Renderer,
179 operation: &mut dyn Operation<Message>,
180 ) {
181 operation.container(
182 self.id.as_ref().map(|id| &id.0),
183 &mut |operation| {
184 self.content.as_widget().operate(
185 &mut tree.children[0],
186 layout.children().next().unwrap(),
187 renderer,
188 operation,
189 );
190 },
191 );
192 }
193
194 fn on_event(
195 &mut self,
196 tree: &mut Tree,
197 event: Event,
198 layout: Layout<'_>,
199 cursor_position: Point,
200 renderer: &Renderer,
201 clipboard: &mut dyn Clipboard,
202 shell: &mut Shell<'_, Message>,
203 ) -> event::Status {
204 self.content.as_widget_mut().on_event(
205 &mut tree.children[0],
206 event,
207 layout.children().next().unwrap(),
208 cursor_position,
209 renderer,
210 clipboard,
211 shell,
212 )
213 }
214
215 fn mouse_interaction(
216 &self,
217 tree: &Tree,
218 layout: Layout<'_>,
219 cursor_position: Point,
220 viewport: &Rectangle,
221 renderer: &Renderer,
222 ) -> mouse::Interaction {
223 self.content.as_widget().mouse_interaction(
224 &tree.children[0],
225 layout.children().next().unwrap(),
226 cursor_position,
227 viewport,
228 renderer,
229 )
230 }
231
232 fn draw(
233 &self,
234 tree: &Tree,
235 renderer: &mut Renderer,
236 theme: &Renderer::Theme,
237 renderer_style: &renderer::Style,
238 layout: Layout<'_>,
239 cursor_position: Point,
240 viewport: &Rectangle,
241 ) {
242 let style = theme.appearance(&self.style);
243
244 draw_background(renderer, &style, layout.bounds());
245
246 self.content.as_widget().draw(
247 &tree.children[0],
248 renderer,
249 theme,
250 &renderer::Style {
251 text_color: style
252 .text_color
253 .unwrap_or(renderer_style.text_color),
254 },
255 layout.children().next().unwrap(),
256 cursor_position,
257 viewport,
258 );
259 }
260
261 fn overlay<'b>(
262 &'b mut self,
263 tree: &'b mut Tree,
264 layout: Layout<'_>,
265 renderer: &Renderer,
266 ) -> Option<overlay::Element<'b, Message, Renderer>> {
267 self.content.as_widget_mut().overlay(
268 &mut tree.children[0],
269 layout.children().next().unwrap(),
270 renderer,
271 )
272 }
273}
274
275impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
276 for Element<'a, Message, Renderer>
277where
278 Message: 'a,
279 Renderer: 'a + crate::Renderer,
280 Renderer::Theme: StyleSheet,
281{
282 fn from(
283 column: Container<'a, Message, Renderer>,
284 ) -> Element<'a, Message, Renderer> {
285 Element::new(column)
286 }
287}
288
289pub fn layout<Renderer>(
291 renderer: &Renderer,
292 limits: &layout::Limits,
293 width: Length,
294 height: Length,
295 max_width: f32,
296 max_height: f32,
297 padding: Padding,
298 horizontal_alignment: alignment::Horizontal,
299 vertical_alignment: alignment::Vertical,
300 layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
301) -> layout::Node {
302 let limits = limits
303 .loose()
304 .max_width(max_width)
305 .max_height(max_height)
306 .width(width)
307 .height(height);
308
309 let mut content = layout_content(renderer, &limits.pad(padding).loose());
310 let padding = padding.fit(content.size(), limits.max());
311 let size = limits.pad(padding).resolve(content.size());
312
313 content.move_to(Point::new(padding.left, padding.top));
314 content.align(
315 Alignment::from(horizontal_alignment),
316 Alignment::from(vertical_alignment),
317 size,
318 );
319
320 layout::Node::with_children(size.pad(padding), vec![content])
321}
322
323pub fn draw_background<Renderer>(
325 renderer: &mut Renderer,
326 appearance: &Appearance,
327 bounds: Rectangle,
328) where
329 Renderer: crate::Renderer,
330{
331 if appearance.background.is_some() || appearance.border_width > 0.0 {
332 renderer.fill_quad(
333 renderer::Quad {
334 bounds,
335 border_radius: appearance.border_radius.into(),
336 border_width: appearance.border_width,
337 border_color: appearance.border_color,
338 },
339 appearance
340 .background
341 .unwrap_or(Background::Color(Color::TRANSPARENT)),
342 );
343 }
344}
345
346#[derive(Debug, Clone, PartialEq, Eq, Hash)]
348pub struct Id(widget::Id);
349
350impl Id {
351 pub fn new(id: impl Into<std::borrow::Cow<'static, str>>) -> Self {
353 Self(widget::Id::new(id))
354 }
355
356 pub fn unique() -> Self {
360 Self(widget::Id::unique())
361 }
362}
363
364impl From<Id> for widget::Id {
365 fn from(id: Id) -> Self {
366 id.0
367 }
368}