flywheel/widget/
status_bar.rs1use crate::actor::InputEvent;
7use crate::buffer::{Buffer, Cell, Rgb};
8use crate::layout::Rect;
9use super::traits::Widget;
10
11#[derive(Debug, Clone)]
13pub struct StatusBarConfig {
14 pub bg: Rgb,
16 pub left_fg: Rgb,
18 pub center_fg: Rgb,
20 pub right_fg: Rgb,
22}
23
24impl Default for StatusBarConfig {
25 fn default() -> Self {
26 Self {
27 bg: Rgb::new(40, 40, 40),
28 left_fg: Rgb::WHITE,
29 center_fg: Rgb::new(150, 150, 150),
30 right_fg: Rgb::new(100, 200, 100),
31 }
32 }
33}
34
35#[derive(Debug)]
37pub struct StatusBar {
38 left: String,
40 center: String,
42 right: String,
44 bounds: Rect,
46 config: StatusBarConfig,
48 dirty: bool,
50}
51
52impl StatusBar {
53 pub fn new(bounds: Rect) -> Self {
55 Self {
56 left: String::new(),
57 center: String::new(),
58 right: String::new(),
59 bounds,
60 config: StatusBarConfig::default(),
61 dirty: true,
62 }
63 }
64
65 pub const fn with_config(bounds: Rect, config: StatusBarConfig) -> Self {
67 Self {
68 left: String::new(),
69 center: String::new(),
70 right: String::new(),
71 bounds,
72 config,
73 dirty: true,
74 }
75 }
76
77 pub fn set_left(&mut self, text: impl Into<String>) {
79 self.left = text.into();
80 self.dirty = true;
81 }
82
83 pub fn set_center(&mut self, text: impl Into<String>) {
85 self.center = text.into();
86 self.dirty = true;
87 }
88
89 pub fn set_right(&mut self, text: impl Into<String>) {
91 self.right = text.into();
92 self.dirty = true;
93 }
94
95 pub fn set_all(&mut self, left: impl Into<String>, center: impl Into<String>, right: impl Into<String>) {
97 self.left = left.into();
98 self.center = center.into();
99 self.right = right.into();
100 self.dirty = true;
101 }
102
103 pub fn left(&self) -> &str {
105 &self.left
106 }
107
108 pub fn center(&self) -> &str {
110 &self.center
111 }
112
113 pub fn right(&self) -> &str {
115 &self.right
116 }
117}
118
119impl Widget for StatusBar {
120 fn bounds(&self) -> Rect {
121 self.bounds
122 }
123
124 fn set_bounds(&mut self, bounds: Rect) {
125 self.bounds = bounds;
126 self.dirty = true;
127 }
128
129 fn render(&self, buffer: &mut Buffer) {
130 let x = self.bounds.x;
131 let y = self.bounds.y;
132 let width = self.bounds.width as usize;
133
134 for i in 0..self.bounds.width {
136 buffer.set(x + i, y, Cell::new(' ').with_bg(self.config.bg));
137 }
138
139 let left_chars: Vec<char> = self.left.chars().collect();
141 for (i, &c) in left_chars.iter().take(width / 3).enumerate() {
142 #[allow(clippy::cast_possible_truncation)]
143 let px = x + i as u16;
144 buffer.set(px, y, Cell::new(c)
145 .with_fg(self.config.left_fg)
146 .with_bg(self.config.bg));
147 }
148
149 let center_chars: Vec<char> = self.center.chars().collect();
151 let center_len = center_chars.len().min(width / 3);
152 #[allow(clippy::cast_possible_truncation)]
153 let center_start = x + ((width - center_len) / 2) as u16;
154 for (i, &c) in center_chars.iter().take(center_len).enumerate() {
155 #[allow(clippy::cast_possible_truncation)]
156 let px = center_start + i as u16;
157 buffer.set(px, y, Cell::new(c)
158 .with_fg(self.config.center_fg)
159 .with_bg(self.config.bg));
160 }
161
162 let right_chars: Vec<char> = self.right.chars().collect();
164 let right_len = right_chars.len().min(width / 3);
165 #[allow(clippy::cast_possible_truncation)]
166 let right_start = x + (width - right_len) as u16;
167 for (i, &c) in right_chars.iter().take(right_len).enumerate() {
168 #[allow(clippy::cast_possible_truncation)]
169 let px = right_start + i as u16;
170 buffer.set(px, y, Cell::new(c)
171 .with_fg(self.config.right_fg)
172 .with_bg(self.config.bg));
173 }
174 }
175
176 fn handle_input(&mut self, _event: &InputEvent) -> bool {
177 false
179 }
180
181 fn needs_redraw(&self) -> bool {
182 self.dirty
183 }
184
185 fn clear_redraw(&mut self) {
186 self.dirty = false;
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn test_status_bar_basic() {
196 let mut bar = StatusBar::new(Rect::new(0, 0, 80, 1));
197
198 bar.set_left("Left");
199 bar.set_center("Center");
200 bar.set_right("Right");
201
202 assert_eq!(bar.left(), "Left");
203 assert_eq!(bar.center(), "Center");
204 assert_eq!(bar.right(), "Right");
205 }
206
207 #[test]
208 fn test_status_bar_set_all() {
209 let mut bar = StatusBar::new(Rect::new(0, 0, 80, 1));
210
211 bar.set_all("A", "B", "C");
212
213 assert_eq!(bar.left(), "A");
214 assert_eq!(bar.center(), "B");
215 assert_eq!(bar.right(), "C");
216 }
217}