1use crate::core::alignment::{self, Alignment};
22use crate::core::border::{self, Border};
23use crate::core::gradient::{self, Gradient};
24use crate::core::layout;
25use crate::core::mouse;
26use crate::core::overlay;
27use crate::core::renderer;
28use crate::core::theme;
29use crate::core::widget::tree::{self, Tree};
30use crate::core::widget::{self, Operation};
31use crate::core::{
32 self, Background, Color, Event, Layout, Length, Padding, Rectangle, Shadow, Shell, Size, Theme,
33 Vector, Widget, color,
34};
35
36pub struct Container<'a, W, Theme = crate::Theme>
57where
58 Theme: Catalog,
59{
60 id: Option<widget::Id>,
61 padding: Padding,
62 width: Length,
63 height: Length,
64 horizontal_alignment: alignment::Horizontal,
65 vertical_alignment: alignment::Vertical,
66 clip: bool,
67 content: W,
68 class: Theme::Class<'a>,
69}
70
71impl<'a, W, Theme> Container<'a, W, Theme>
72where
73 Theme: Catalog,
74{
75 pub fn new(content: W) -> Self {
77 Container {
78 id: None,
79 padding: Padding::ZERO,
80 width: Length::Fit,
81 height: Length::Fit,
82 horizontal_alignment: alignment::Horizontal::Left,
83 vertical_alignment: alignment::Vertical::Top,
84 clip: false,
85 class: Theme::default(),
86 content,
87 }
88 }
89
90 pub fn id(mut self, id: impl Into<widget::Id>) -> Self {
92 self.id = Some(id.into());
93 self
94 }
95
96 pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
98 self.padding = padding.into();
99 self
100 }
101
102 pub fn width(mut self, width: impl Into<Length>) -> Self {
104 self.width = width.into();
105 self
106 }
107
108 pub fn height(mut self, height: impl Into<Length>) -> Self {
110 self.height = height.into();
111 self
112 }
113
114 pub fn center_x(self, width: impl Into<Length>) -> Self {
116 self.width(width).align_x(alignment::Horizontal::Center)
117 }
118
119 pub fn center_y(self, height: impl Into<Length>) -> Self {
121 self.height(height).align_y(alignment::Vertical::Center)
122 }
123
124 pub fn center(self, length: impl Into<Length>) -> Self {
132 let length = length.into();
133
134 self.center_x(length).center_y(length)
135 }
136
137 pub fn align_left(self, width: impl Into<Length>) -> Self {
139 self.width(width).align_x(alignment::Horizontal::Left)
140 }
141
142 pub fn align_right(self, width: impl Into<Length>) -> Self {
144 self.width(width).align_x(alignment::Horizontal::Right)
145 }
146
147 pub fn align_top(self, height: impl Into<Length>) -> Self {
149 self.height(height).align_y(alignment::Vertical::Top)
150 }
151
152 pub fn align_bottom(self, height: impl Into<Length>) -> Self {
154 self.height(height).align_y(alignment::Vertical::Bottom)
155 }
156
157 pub fn align_x(mut self, alignment: impl Into<alignment::Horizontal>) -> Self {
159 self.horizontal_alignment = alignment.into();
160 self
161 }
162
163 pub fn align_y(mut self, alignment: impl Into<alignment::Vertical>) -> Self {
165 self.vertical_alignment = alignment.into();
166 self
167 }
168
169 pub fn clip(mut self, clip: bool) -> Self {
172 self.clip = clip;
173 self
174 }
175
176 #[must_use]
178 pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self
179 where
180 Theme::Class<'a>: From<StyleFn<'a, Theme>>,
181 {
182 self.class = (Box::new(style) as StyleFn<'a, Theme>).into();
183 self
184 }
185
186 #[must_use]
188 pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
189 self.class = class.into();
190 self
191 }
192}
193
194impl<W, Theme> widget::Meta for Container<'_, W, Theme> where Theme: Catalog {}
195
196impl<W, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Container<'_, W, Theme>
197where
198 Theme: Catalog,
199 Renderer: core::Renderer,
200 W: Widget<Message, Theme, Renderer>,
201{
202 fn tag(&self) -> tree::Tag {
203 self.content.tag()
204 }
205
206 fn state(&self) -> tree::State {
207 self.content.state()
208 }
209
210 fn diff(&mut self, tree: &mut Tree) {
211 tree.diff_children(std::slice::from_mut(&mut self.content));
212
213 let size = self.content.size();
214 self.width = self.width.stack(size.width);
215 self.height = self.height.stack(size.height);
216 }
217
218 fn size(&self) -> Size<Length> {
219 Size {
220 width: self.width,
221 height: self.height,
222 }
223 }
224
225 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
226 layout(
227 tree,
228 limits,
229 self.width,
230 self.height,
231 self.padding,
232 self.horizontal_alignment,
233 self.vertical_alignment,
234 |tree, limits| {
235 self.content.layout(tree, renderer, limits);
236 tree.size
237 },
238 );
239 }
240
241 fn operate(
242 &mut self,
243 tree: &mut Tree,
244 layout: Layout,
245 viewport: &Rectangle,
246 renderer: &Renderer,
247 operation: &mut dyn Operation,
248 ) {
249 let viewport = if self.clip {
250 layout.bounds().intersection(viewport).unwrap_or_default()
251 } else {
252 *viewport
253 };
254
255 operation.container(self.id.as_ref(), layout.bounds(), &viewport);
256 operation.traverse(&mut |operation| {
257 let (layout, tree) = layout.iter_mut(&mut tree.children).next().unwrap();
258
259 self.content
260 .operate(tree, layout, &viewport, renderer, operation);
261 });
262 }
263
264 fn update(
265 &mut self,
266 tree: &mut Tree,
267 event: &Event,
268 layout: Layout,
269 cursor: mouse::Cursor,
270 renderer: &Renderer,
271 shell: &mut Shell<'_, Message>,
272 viewport: &Rectangle,
273 ) {
274 let (layout, tree) = layout.iter_mut(&mut tree.children).next().unwrap();
275
276 self.content
277 .update(tree, event, layout, cursor, renderer, shell, viewport);
278 }
279
280 fn mouse_interaction(
281 &self,
282 tree: &Tree,
283 layout: Layout,
284 cursor: mouse::Cursor,
285 viewport: &Rectangle,
286 renderer: &Renderer,
287 ) -> mouse::Interaction {
288 let (layout, tree) = layout.iter(&tree.children).next().unwrap();
289
290 self.content
291 .mouse_interaction(tree, layout, cursor, viewport, renderer)
292 }
293
294 fn draw(
295 &self,
296 tree: &Tree,
297 renderer: &mut Renderer,
298 theme: &Theme,
299 renderer_style: &renderer::Style,
300 layout: Layout,
301 cursor: mouse::Cursor,
302 viewport: &Rectangle,
303 ) {
304 let bounds = layout.bounds();
305 let style = theme.style(&self.class);
306
307 if let Some(clipped_viewport) = bounds.intersection(viewport) {
308 draw_background(renderer, &style, bounds);
309
310 let (layout, tree) = layout.iter(&tree.children).next().unwrap();
311
312 self.content.draw(
313 tree,
314 renderer,
315 theme,
316 &renderer::Style {
317 text_color: style.text_color.unwrap_or(renderer_style.text_color),
318 },
319 layout,
320 cursor,
321 if self.clip {
322 &clipped_viewport
323 } else {
324 viewport
325 },
326 );
327 }
328 }
329
330 fn overlay<'b>(
331 &'b mut self,
332 tree: &'b mut Tree,
333 layout: Layout,
334 renderer: &Renderer,
335 viewport: &Rectangle,
336 translation: Vector,
337 window: Size,
338 ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
339 let (layout, tree) = layout.iter_mut(&mut tree.children).next().unwrap();
340
341 self.content
342 .overlay(tree, layout, renderer, viewport, translation, window)
343 }
344}
345
346pub fn layout(
348 tree: &mut Tree,
349 limits: &layout::Limits,
350 width: Length,
351 height: Length,
352 padding: Padding,
353 horizontal_alignment: alignment::Horizontal,
354 vertical_alignment: alignment::Vertical,
355 layout_content: impl FnOnce(&mut Tree, &layout::Limits) -> Size,
356) {
357 let limits = limits.width(width).height(height);
358
359 layout::positioned(
360 tree,
361 &limits,
362 width,
363 height,
364 padding,
365 |tree, limits| layout_content(tree, &limits.loose()),
366 |content, container| {
367 content.align(
368 container,
369 Alignment::from(horizontal_alignment),
370 Alignment::from(vertical_alignment),
371 )
372 },
373 );
374}
375
376pub fn draw_background<Renderer>(renderer: &mut Renderer, style: &Style, bounds: Rectangle)
378where
379 Renderer: core::Renderer,
380{
381 if style.background.is_some() || style.border.width > 0.0 || style.shadow.color.a > 0.0 {
382 renderer.fill_quad(
383 renderer::Quad {
384 bounds,
385 border: style.border,
386 shadow: style.shadow,
387 snap: style.snap,
388 },
389 style
390 .background
391 .unwrap_or(Background::Color(Color::TRANSPARENT)),
392 );
393 }
394}
395
396#[derive(Debug, Clone, Copy, PartialEq)]
398pub struct Style {
399 pub text_color: Option<Color>,
401 pub background: Option<Background>,
403 pub border: Border,
405 pub shadow: Shadow,
407 pub snap: bool,
409}
410
411impl Default for Style {
412 fn default() -> Self {
413 Self {
414 text_color: None,
415 background: None,
416 border: Border::default(),
417 shadow: Shadow::default(),
418 snap: renderer::CRISP,
419 }
420 }
421}
422
423impl Style {
424 pub fn color(self, color: impl Into<Color>) -> Self {
426 Self {
427 text_color: Some(color.into()),
428 ..self
429 }
430 }
431
432 pub fn border(self, border: impl Into<Border>) -> Self {
434 Self {
435 border: border.into(),
436 ..self
437 }
438 }
439
440 pub fn background(self, background: impl Into<Background>) -> Self {
442 Self {
443 background: Some(background.into()),
444 ..self
445 }
446 }
447
448 pub fn shadow(self, shadow: impl Into<Shadow>) -> Self {
450 Self {
451 shadow: shadow.into(),
452 ..self
453 }
454 }
455}
456
457impl From<Color> for Style {
458 fn from(color: Color) -> Self {
459 Self::default().background(color)
460 }
461}
462
463impl From<Gradient> for Style {
464 fn from(gradient: Gradient) -> Self {
465 Self::default().background(gradient)
466 }
467}
468
469impl From<gradient::Linear> for Style {
470 fn from(gradient: gradient::Linear) -> Self {
471 Self::default().background(gradient)
472 }
473}
474
475pub trait Catalog {
477 type Class<'a>;
479
480 fn default<'a>() -> Self::Class<'a>;
482
483 fn style(&self, class: &Self::Class<'_>) -> Style;
485}
486
487pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme) -> Style + 'a>;
489
490impl<Theme> From<Style> for StyleFn<'_, Theme> {
491 fn from(style: Style) -> Self {
492 Box::new(move |_theme| style)
493 }
494}
495
496impl Catalog for Theme {
497 type Class<'a> = StyleFn<'a, Self>;
498
499 fn default<'a>() -> Self::Class<'a> {
500 Box::new(transparent)
501 }
502
503 fn style(&self, class: &Self::Class<'_>) -> Style {
504 class(self)
505 }
506}
507
508pub fn transparent<Theme>(_theme: &Theme) -> Style {
510 Style::default()
511}
512
513pub fn background(background: impl Into<Background>) -> Style {
515 Style::default().background(background)
516}
517
518pub fn rounded_box(theme: &Theme) -> Style {
520 let palette = theme.palette();
521
522 Style {
523 background: Some(palette.background.weak.color.into()),
524 text_color: Some(palette.background.weak.text),
525 border: border::rounded(2),
526 ..Style::default()
527 }
528}
529
530pub fn bordered_box(theme: &Theme) -> Style {
532 let palette = theme.palette();
533
534 Style {
535 background: Some(palette.background.weakest.color.into()),
536 text_color: Some(palette.background.weakest.text),
537 border: Border {
538 width: 1.0,
539 radius: 5.0.into(),
540 color: palette.background.weak.color,
541 },
542 ..Style::default()
543 }
544}
545
546pub fn dark(_theme: &Theme) -> Style {
548 style(theme::palette::Pair {
549 color: color!(0x111111),
550 text: Color::WHITE,
551 })
552}
553
554pub fn primary(theme: &Theme) -> Style {
556 let palette = theme.palette();
557
558 style(palette.primary.base)
559}
560
561pub fn secondary(theme: &Theme) -> Style {
563 let palette = theme.palette();
564
565 style(palette.secondary.base)
566}
567
568pub fn success(theme: &Theme) -> Style {
570 let palette = theme.palette();
571
572 style(palette.success.base)
573}
574
575pub fn warning(theme: &Theme) -> Style {
577 let palette = theme.palette();
578
579 style(palette.warning.base)
580}
581
582pub fn danger(theme: &Theme) -> Style {
584 let palette = theme.palette();
585
586 style(palette.danger.base)
587}
588
589fn style(pair: theme::palette::Pair) -> Style {
590 Style {
591 background: Some(pair.color.into()),
592 text_color: Some(pair.text),
593 border: border::rounded(2),
594 ..Style::default()
595 }
596}