retroglyph_widgets/widget/
box_border.rs1use retroglyph_core::{Backend, Rect, Style, Terminal};
3
4use super::Widget;
5use crate::Theme;
6use crate::draw::{BL, BR, H, TL, TR, V};
7
8#[derive(Clone, Copy, Debug, Default)]
14pub struct BoxBorder {
15 style: Style,
16}
17
18impl BoxBorder {
19 #[must_use]
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 #[must_use]
27 pub const fn style(mut self, style: Style) -> Self {
28 self.style = style;
29 self
30 }
31
32 #[must_use]
47 pub fn theme(mut self, theme: Theme) -> Self {
48 self.style = Style::new().fg(theme.border).bg(theme.panel_bg);
49 self
50 }
51}
52
53impl<B: Backend> Widget<B> for BoxBorder {
54 fn render(self, area: Rect, term: &mut Terminal<B>) {
55 if area.width() < 2 || area.height() < 2 {
56 return;
57 }
58
59 let x0 = area.left();
60 let y0 = area.top();
61 let x1 = area.right().saturating_sub(1);
62 let y1 = area.bottom().saturating_sub(1);
63
64 term.reset_style()
65 .fg(self.style.foreground())
66 .bg(self.style.background());
67
68 term.put(x0, y0, TL);
70 term.put(x1, y0, TR);
71 term.put(x0, y1, BL);
72 term.put(x1, y1, BR);
73
74 for x in (x0 + 1)..x1 {
76 term.put(x, y0, H);
77 term.put(x, y1, H);
78 }
79
80 for y in (y0 + 1)..y1 {
82 term.put(x0, y, V);
83 term.put(x1, y, V);
84 }
85
86 term.reset_style();
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use retroglyph_core::{Color, Headless};
93
94 use super::*;
95
96 #[test]
97 fn draws_corners_and_edges() {
98 let area = Rect::new(0, 0, 5, 3);
99 let mut term = Terminal::new(Headless::new(5, 3));
100 BoxBorder::new()
101 .style(Style::new().fg(Color::WHITE))
102 .render(area, &mut term);
103
104 assert_eq!(term.grid().get(0, 0).glyph(), TL);
105 assert_eq!(term.grid().get(4, 0).glyph(), TR);
106 assert_eq!(term.grid().get(0, 2).glyph(), BL);
107 assert_eq!(term.grid().get(4, 2).glyph(), BR);
108 assert_eq!(term.grid().get(2, 0).glyph(), H);
109 assert_eq!(term.grid().get(0, 1).glyph(), V);
110 assert_eq!(term.grid().get(2, 1).glyph(), ' ');
112 }
113
114 #[test]
115 fn too_small_is_a_no_op() {
116 let area = Rect::new(0, 0, 1, 1);
117 let mut term = Terminal::new(Headless::new(1, 1));
118 BoxBorder::new().render(area, &mut term);
119 assert_eq!(term.grid().get(0, 0).glyph(), ' ');
120 }
121
122 #[test]
123 fn theme_maps_border_role_onto_style() {
124 let area = Rect::new(0, 0, 5, 3);
125 let mut term = Terminal::new(Headless::new(5, 3));
126 BoxBorder::new().theme(Theme::DARK).render(area, &mut term);
127
128 assert_eq!(
129 term.grid().get(0, 0).style().foreground(),
130 Theme::DARK.border
131 );
132 assert_eq!(
133 term.grid().get(0, 0).style().background(),
134 Theme::DARK.panel_bg
135 );
136 }
137}