rat_widget/layout/
layout_outer.rs

1//!
2//! Constrains an area from its outside.
3//!
4use crate::layout::{DialogItem, GenericLayout, layout_dialog};
5use ratatui::layout::{Constraint, Flex, Layout, Position, Rect, Size};
6use ratatui::widgets::Padding;
7
8/// This lets you define the outer bounds of a target area.
9///
10/// The constraints are applied in this order.
11///
12/// * Constrain the left/right/top/bottom border outside the area.
13/// * Set a fixed position.
14/// * Constrain the width/height of the area.
15/// * Set a fixed size.
16///
17/// In the end it gives you the middle Rect.
18///
19#[derive(Debug, Default, Clone)]
20pub struct LayoutOuter {
21    pub left: Option<Constraint>,
22    pub top: Option<Constraint>,
23    pub right: Option<Constraint>,
24    pub bottom: Option<Constraint>,
25    pub position: Option<Position>,
26    pub width: Option<Constraint>,
27    pub height: Option<Constraint>,
28    pub size: Option<Size>,
29}
30
31impl LayoutOuter {
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    /// Margin constraint for the left side.
37    pub fn left(mut self, left: Constraint) -> Self {
38        self.left = Some(left);
39        self
40    }
41
42    /// Margin constraint for the top side.
43    pub fn top(mut self, top: Constraint) -> Self {
44        self.top = Some(top);
45        self
46    }
47
48    /// Margin constraint for the right side.
49    pub fn right(mut self, right: Constraint) -> Self {
50        self.right = Some(right);
51        self
52    }
53
54    /// Margin constraint for the bottom side.
55    pub fn bottom(mut self, bottom: Constraint) -> Self {
56        self.bottom = Some(bottom);
57        self
58    }
59
60    /// Put at a fixed position.
61    pub fn position(mut self, pos: Position) -> Self {
62        self.position = Some(pos);
63        self
64    }
65
66    /// Constraint for the width.
67    pub fn width(mut self, width: Constraint) -> Self {
68        self.width = Some(width);
69        self
70    }
71
72    /// Constraint for the height.
73    pub fn height(mut self, height: Constraint) -> Self {
74        self.height = Some(height);
75        self
76    }
77
78    /// Set at a fixed size.
79    pub fn size(mut self, size: Size) -> Self {
80        self.size = Some(size);
81        self
82    }
83
84    /// Calculate the area.
85    #[inline]
86    pub fn layout(&self, area: Rect) -> Rect {
87        let mut hor = [
88            Constraint::Length(0),
89            Constraint::Fill(1),
90            Constraint::Length(0),
91        ];
92        let mut ver = [
93            Constraint::Length(0),
94            Constraint::Fill(1),
95            Constraint::Length(0),
96        ];
97        if let Some(left) = self.left {
98            hor[0] = left;
99        }
100        if let Some(top) = self.top {
101            ver[0] = top;
102        }
103        if let Some(right) = self.right {
104            hor[2] = right;
105        }
106        if let Some(bottom) = self.bottom {
107            ver[2] = bottom;
108        }
109        if let Some(pos) = self.position {
110            ver[0] = Constraint::Length(pos.y);
111            hor[0] = Constraint::Length(pos.x);
112        }
113        if let Some(width) = self.width {
114            hor[1] = width;
115            hor[2] = Constraint::Fill(1);
116        }
117        if let Some(height) = self.height {
118            ver[1] = height;
119            ver[2] = Constraint::Fill(1);
120        }
121        if let Some(size) = self.size {
122            ver[1] = Constraint::Length(size.height);
123            ver[2] = Constraint::Fill(1);
124            hor[1] = Constraint::Length(size.width);
125            hor[2] = Constraint::Fill(1);
126        }
127
128        let h_layout = Layout::horizontal(hor).split(area);
129        let v_layout = Layout::vertical(ver).split(h_layout[1]);
130        v_layout[1]
131    }
132
133    /// Create a dialog layout with the given constraints.
134    #[inline]
135    pub fn layout_dialog<const N: usize>(
136        &self,
137        area: Rect,
138        padding: Padding,
139        buttons: [Constraint; N],
140        button_spacing: u16,
141        button_flex: Flex,
142    ) -> GenericLayout<DialogItem> {
143        let inner = self.layout(area);
144        layout_dialog(inner, padding, buttons, button_spacing, button_flex)
145    }
146}