rpi_tui/
dynamic_border.rs1use std::any::Any;
9
10use super::component::Component;
11use crate::ansi::dim;
12use crate::theme::{theme, Color};
13
14pub struct DynamicBorder {
20 color: Option<Color>,
21}
22
23impl DynamicBorder {
24 pub fn new() -> Self {
26 Self { color: None }
27 }
28
29 pub fn with_color(color: Color) -> Self {
31 Self { color: Some(color) }
32 }
33}
34
35impl Default for DynamicBorder {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl Component for DynamicBorder {
42 fn render(&self, width: usize) -> Vec<String> {
43 let rule = "─".repeat(width.max(1));
44 let rendered = match self.color {
45 Some(color) => color.fg(&rule),
46 None => dim(&theme().colors.border_muted.fg(&rule)),
47 };
48 vec![rendered]
49 }
50
51 fn invalidate(&self) {
52 }
54
55 fn as_any(&self) -> &dyn Any {
56 self
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use crate::ansi::visible_width;
64
65 #[test]
66 fn test_dynamic_border_renders_one_line() {
67 let border = DynamicBorder::new();
68 let lines = border.render(40);
69 assert_eq!(lines.len(), 1);
70 assert_eq!(visible_width(&lines[0]), 40);
72 assert!(lines[0].contains("\x1b[2m"));
73 }
74
75 #[test]
76 fn test_dynamic_border_zero_width_safe() {
77 let border = DynamicBorder::new();
79 let lines = border.render(0);
80 assert_eq!(lines.len(), 1);
81 assert_eq!(visible_width(&lines[0]), 1);
82 }
83
84 #[test]
85 fn test_dynamic_border_with_color_contains_escape() {
86 let border = DynamicBorder::with_color(Color::Ansi256(39));
87 let lines = border.render(10);
88 assert!(lines[0].contains('\x1b'));
89 }
90}