tuviv/widgets/
align.rs

1//! Houses the [`Align`] widget
2
3use le::{Alignment, Layout, MinimumNatural};
4
5use crate::{
6    le::layout::{Rect, Vec2},
7    terminal::Buffer,
8    Widget,
9};
10
11/// Aligns the child widget within its space
12///
13/// `Align` will always pass through layout functions to the child.
14/// `Align` will try and align based of prefered size but if it
15/// is not available than Align will stretch it as required.
16pub struct Align<T: Widget> {
17    /// The child widget
18    pub widget: T,
19    /// How to align on the x axis
20    pub align_x: Alignment,
21    /// How to align on the y axis
22    pub align_y: Alignment,
23}
24
25impl<T: Widget> Align<T> {
26    /// Creates a new `Align`
27    pub fn new(widget: T) -> Self {
28        Self {
29            widget,
30            align_x: Alignment::Center,
31            align_y: Alignment::Center,
32        }
33    }
34
35    /// Sets the [`Align::align_x`] property
36    pub fn align_x(mut self, alignment: Alignment) -> Self {
37        self.align_x = alignment;
38        self
39    }
40
41    /// Sets the [`Align::align_y`] property
42    pub fn align_y(mut self, alignment: Alignment) -> Self {
43        self.align_y = alignment;
44        self
45    }
46}
47
48impl<W: Widget> Layout for Align<W> {
49    fn width_for_height(&self, height: usize) -> MinimumNatural<usize> {
50        self.widget.width_for_height(height)
51    }
52    fn height_for_width(&self, width: usize) -> MinimumNatural<usize> {
53        self.widget.height_for_width(width)
54    }
55
56    fn prefered_size(&self) -> MinimumNatural<Vec2> {
57        self.widget.prefered_size()
58    }
59
60    fn prefered_size_of_container(
61        &self,
62        container: Vec2,
63    ) -> MinimumNatural<Vec2> {
64        self.widget.prefered_size_of_container(container)
65    }
66}
67
68impl<W: Widget> Widget for Align<W> {
69    fn render(&self, rect: Rect, buffer: &mut Buffer) {
70        // Get the widget size
71        let mut widget_size =
72            self.widget.prefered_size_of_container(rect.size).natural;
73        if self.align_x == Alignment::Expand {
74            widget_size.x = rect.size.x;
75        }
76        if self.align_y == Alignment::Expand {
77            widget_size.y = rect.size.y;
78        }
79
80        if widget_size.y > rect.size.y {
81            widget_size.x = self.widget.width_for_height(rect.size.y).natural;
82            widget_size.y = rect.size.y;
83        }
84
85        if widget_size.x > rect.size.x {
86            widget_size.y = self.widget.height_for_width(rect.size.x).natural;
87            widget_size.x = rect.size.x;
88        }
89
90        // Separate the rect into layout infos
91        let li_x = rect.layout_info_x();
92        let li_y = rect.layout_info_y();
93
94        // Align them
95        let al_x = self.align_x.align(li_x, widget_size.x);
96        let al_y = self.align_y.align(li_y, widget_size.y);
97
98        // Reconstruct it into a rect
99        let aligned_rect = Rect::new(
100            al_x.start,
101            al_y.start,
102            al_x.end - al_x.start,
103            al_y.end - al_y.start,
104        );
105
106        self.widget.render(aligned_rect, buffer);
107    }
108}