logo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use le::{Alignment, Layout};

use crate::{
    le::layout::{Rect, Vec2},
    terminal::Buffer,
    Widget, WidgetType,
};

/// Aligns the child widget within its space
///
/// `Align` will always pass through layout functions to the child.
/// `Align` will try and align based of prefered size but if it
/// is not available than Align will stretch it as required.
pub struct Align<'a> {
    /// The child widget
    pub widget: WidgetType<'a>,
    /// How to align on the x axis
    pub align_x: Alignment,
    /// How to align on the y axis
    pub align_y: Alignment,
}

impl<'a> Align<'a> {
    /// Creates a new `Align`
    pub fn new(widget: WidgetType<'a>) -> Box<Self> {
        Box::new(Self {
            widget,
            align_x: Alignment::Center,
            align_y: Alignment::Center,
        })
    }

    /// Sets the [`Align::align_x`] property
    pub fn align_x(mut self: Box<Self>, alignment: Alignment) -> Box<Self> {
        self.align_x = alignment;
        self
    }

    /// Sets the [`Align::align_y`] property
    pub fn align_y(mut self: Box<Self>, alignment: Alignment) -> Box<Self> {
        self.align_y = alignment;
        self
    }
}

impl Layout for Align<'_> {
    fn width_for_height(&self, height: usize) -> usize {
        self.widget.width_for_height(height)
    }

    fn height_for_width(&self, width: usize) -> usize {
        self.widget.height_for_width(width)
    }

    fn prefered_size(&self) -> Vec2 {
        self.widget.prefered_size()
    }
}

impl Widget for Align<'_> {
    fn render(&self, rect: Rect, buffer: &mut Buffer) {
        // Get the widget size
        let mut widget_size;
        if self.align_x == Alignment::Expand {
            widget_size = Vec2::new(
                rect.size.x,
                self.widget.height_for_width(rect.size.x),
            )
        } else if self.align_y == Alignment::Expand {
            widget_size = Vec2::new(
                self.widget.width_for_height(rect.size.y),
                rect.size.y,
            )
        } else {
            widget_size = self.widget.prefered_size_of_container(rect.size)
        }

        if widget_size.y > rect.size.y {
            widget_size.x = self.widget.width_for_height(rect.size.y);
            widget_size.y = rect.size.y;
        }

        if widget_size.x > rect.size.x {
            widget_size.y = self.widget.height_for_width(rect.size.x);
            widget_size.x = rect.size.x;
        }

        // Separate the rect into layout infos
        let li_x = rect.layout_info_x();
        let li_y = rect.layout_info_y();

        // Align them
        let al_x = self.align_x.align(li_x, widget_size.x);
        let al_y = self.align_y.align(li_y, widget_size.y);

        // Reconstruct it into a rect
        let aligned_rect = Rect::new(
            al_x.start,
            al_y.start,
            al_x.end - al_x.start,
            al_y.end - al_y.start,
        );

        self.widget.render(aligned_rect, buffer);
    }
}