Skip to main content

radicle_tui/ui/
ext.rs

1use ratatui::buffer::Buffer;
2use ratatui::layout::{Position, Rect};
3use ratatui::style::Style;
4use ratatui::symbols;
5use ratatui::widgets::{BorderType, Borders, Widget};
6
7pub struct HeaderBlock {
8    /// Visible borders
9    borders: Borders,
10    /// Border style
11    border_style: Style,
12    /// Type of the border. The default is plain lines but one can choose to have rounded corners
13    /// or doubled lines instead.
14    border_type: BorderType,
15    /// Widget style
16    style: Style,
17}
18
19impl Default for HeaderBlock {
20    fn default() -> HeaderBlock {
21        HeaderBlock {
22            borders: Borders::NONE,
23            border_style: Default::default(),
24            border_type: BorderType::Rounded,
25            style: Default::default(),
26        }
27    }
28}
29
30impl HeaderBlock {
31    pub fn border_style(mut self, style: Style) -> HeaderBlock {
32        self.border_style = style;
33        self
34    }
35
36    pub fn style(mut self, style: Style) -> HeaderBlock {
37        self.style = style;
38        self
39    }
40
41    pub fn borders(mut self, flag: Borders) -> HeaderBlock {
42        self.borders = flag;
43        self
44    }
45
46    pub fn border_type(mut self, border_type: BorderType) -> HeaderBlock {
47        self.border_type = border_type;
48        self
49    }
50}
51
52impl Widget for HeaderBlock {
53    fn render(self, area: Rect, buf: &mut Buffer) {
54        if area.area() == 0 {
55            return;
56        }
57        buf.set_style(area, self.style);
58        let symbols = BorderType::to_border_set(self.border_type);
59
60        // Sides
61        if self.borders.intersects(Borders::LEFT) {
62            for y in area.top()..area.bottom() {
63                if let Some(cell) = buf.cell_mut(Position::new(area.left(), y)) {
64                    cell.set_symbol(symbols.vertical_left)
65                        .set_style(self.border_style);
66                }
67            }
68        }
69        if self.borders.intersects(Borders::TOP) {
70            for x in area.left()..area.right() {
71                if let Some(cell) = buf.cell_mut(Position::new(x, area.top())) {
72                    cell.set_symbol(symbols.horizontal_top)
73                        .set_style(self.border_style);
74                }
75            }
76        }
77        if self.borders.intersects(Borders::RIGHT) {
78            let x = area.right() - 1;
79            for y in area.top()..area.bottom() {
80                if let Some(cell) = buf.cell_mut(Position::new(x, y)) {
81                    cell.set_symbol(symbols.vertical_right)
82                        .set_style(self.border_style);
83                }
84            }
85        }
86        if self.borders.intersects(Borders::BOTTOM) {
87            let y = area.bottom() - 1;
88            for x in area.left()..area.right() {
89                if let Some(cell) = buf.cell_mut(Position::new(x, y)) {
90                    cell.set_symbol(symbols.horizontal_bottom)
91                        .set_style(self.border_style);
92                }
93            }
94        }
95
96        // Corners
97        if self.borders.contains(Borders::RIGHT | Borders::BOTTOM) {
98            if let Some(cell) = buf.cell_mut(Position::new(area.right() - 1, area.bottom() - 1)) {
99                cell.set_symbol(symbols::line::VERTICAL_LEFT)
100                    .set_style(self.border_style);
101            }
102        }
103        if self.borders.contains(Borders::RIGHT | Borders::TOP) {
104            if let Some(cell) = buf.cell_mut(Position::new(area.right() - 1, area.top())) {
105                cell.set_symbol(symbols.top_right)
106                    .set_style(self.border_style);
107            }
108        }
109        if self.borders.contains(Borders::LEFT | Borders::BOTTOM) {
110            if let Some(cell) = buf.cell_mut(Position::new(area.left(), area.bottom() - 1)) {
111                cell.set_symbol(symbols::line::VERTICAL_RIGHT)
112                    .set_style(self.border_style);
113            }
114        }
115        if self.borders.contains(Borders::LEFT | Borders::TOP) {
116            if let Some(cell) = buf.cell_mut(Position::new(area.left(), area.top())) {
117                cell.set_symbol(symbols.top_left)
118                    .set_style(self.border_style);
119            }
120        }
121    }
122}
123
124#[derive(Clone)]
125pub enum FooterBlockType {
126    Single { top: bool },
127    Begin,
128    End,
129    Repeat,
130}
131
132pub struct FooterBlock {
133    /// Visible borders
134    borders: Borders,
135    /// Border style
136    border_style: Style,
137    /// Type of the border. The default is plain lines but one can choose to have rounded corners
138    /// or doubled lines instead.
139    border_type: BorderType,
140    /// Type of the footer block. The default is single.
141    block_type: FooterBlockType,
142    /// Widget style
143    style: Style,
144}
145
146impl Default for FooterBlock {
147    fn default() -> Self {
148        Self {
149            block_type: FooterBlockType::Single { top: true },
150            borders: Self::borders(FooterBlockType::Single { top: true }),
151            border_style: Default::default(),
152            border_type: BorderType::Rounded,
153            style: Default::default(),
154        }
155    }
156}
157
158impl FooterBlock {
159    pub fn border_style(mut self, style: Style) -> Self {
160        self.border_style = style;
161        self
162    }
163
164    pub fn style(mut self, style: Style) -> Self {
165        self.style = style;
166        self
167    }
168
169    pub fn block_type(mut self, block_type: FooterBlockType) -> Self {
170        self.block_type = block_type.clone();
171        self.borders = Self::borders(block_type);
172        self
173    }
174
175    pub fn border_type(mut self, border_type: BorderType) -> Self {
176        self.border_type = border_type;
177        self
178    }
179
180    fn borders(block_type: FooterBlockType) -> Borders {
181        match block_type {
182            FooterBlockType::Single { top } => {
183                if top {
184                    Borders::ALL
185                } else {
186                    Borders::LEFT | Borders::RIGHT | Borders::BOTTOM
187                }
188            }
189            FooterBlockType::Begin => Borders::ALL,
190            FooterBlockType::End | FooterBlockType::Repeat => {
191                Borders::TOP | Borders::RIGHT | Borders::BOTTOM
192            }
193        }
194    }
195}
196
197impl Widget for FooterBlock {
198    fn render(self, area: Rect, buf: &mut Buffer) {
199        if area.area() == 0 {
200            return;
201        }
202        buf.set_style(area, self.style);
203        let symbols = BorderType::to_border_set(self.border_type);
204
205        // Sides
206        if self.borders.intersects(Borders::LEFT) {
207            for y in area.top()..area.bottom() {
208                if let Some(cell) = buf.cell_mut(Position::new(area.left(), y)) {
209                    cell.set_symbol(symbols.vertical_left)
210                        .set_style(self.border_style);
211                }
212            }
213        }
214        if self.borders.intersects(Borders::TOP) {
215            for x in area.left()..area.right() {
216                if let Some(cell) = buf.cell_mut(Position::new(x, area.top())) {
217                    cell.set_symbol(symbols.horizontal_top)
218                        .set_style(self.border_style);
219                }
220            }
221        }
222        if self.borders.intersects(Borders::RIGHT) {
223            let x = area.right() - 1;
224            for y in area.top()..area.bottom() {
225                if let Some(cell) = buf.cell_mut(Position::new(x, y)) {
226                    cell.set_symbol(symbols.vertical_right)
227                        .set_style(self.border_style);
228                }
229            }
230        }
231        if self.borders.intersects(Borders::BOTTOM) {
232            let y = area.bottom() - 1;
233            for x in area.left()..area.right() {
234                if let Some(cell) = buf.cell_mut(Position::new(x, y)) {
235                    cell.set_symbol(symbols.horizontal_bottom)
236                        .set_style(self.border_style);
237                }
238            }
239        }
240
241        // Corners
242        if self.borders.contains(Borders::RIGHT | Borders::BOTTOM) {
243            let symbol = match self.block_type {
244                FooterBlockType::Begin | FooterBlockType::Repeat => symbols::line::HORIZONTAL_UP,
245                _ => symbols.bottom_right,
246            };
247            if let Some(cell) = buf.cell_mut(Position::new(area.right() - 1, area.bottom() - 1)) {
248                cell.set_symbol(symbol).set_style(self.border_style);
249            }
250        }
251        if self.borders.contains(Borders::RIGHT | Borders::TOP) {
252            let symbol = match self.block_type {
253                FooterBlockType::Begin | FooterBlockType::Repeat => symbols::line::HORIZONTAL_DOWN,
254                _ => symbols::line::VERTICAL_LEFT,
255            };
256            if let Some(cell) = buf.cell_mut(Position::new(area.right() - 1, area.top())) {
257                cell.set_symbol(symbol).set_style(self.border_style);
258            }
259        }
260        if self.borders.contains(Borders::LEFT | Borders::BOTTOM) {
261            if let Some(cell) = buf.cell_mut(Position::new(area.left(), area.bottom() - 1)) {
262                cell.set_symbol(symbols.bottom_left)
263                    .set_style(self.border_style);
264            }
265        }
266        if self.borders.contains(Borders::LEFT | Borders::TOP) {
267            if let Some(cell) = buf.cell_mut(Position::new(area.left(), area.top())) {
268                cell.set_symbol(symbols::line::VERTICAL_RIGHT)
269                    .set_style(self.border_style);
270            }
271        }
272    }
273}