use crate::actor::InputEvent;
use crate::buffer::{Buffer, Cell, Rgb};
use crate::layout::Rect;
use super::traits::Widget;
#[derive(Debug, Clone)]
pub struct StatusBarConfig {
pub bg: Rgb,
pub left_fg: Rgb,
pub center_fg: Rgb,
pub right_fg: Rgb,
}
impl Default for StatusBarConfig {
fn default() -> Self {
Self {
bg: Rgb::new(40, 40, 40),
left_fg: Rgb::WHITE,
center_fg: Rgb::new(150, 150, 150),
right_fg: Rgb::new(100, 200, 100),
}
}
}
#[derive(Debug)]
pub struct StatusBar {
left: String,
center: String,
right: String,
bounds: Rect,
config: StatusBarConfig,
dirty: bool,
}
impl StatusBar {
pub fn new(bounds: Rect) -> Self {
Self {
left: String::new(),
center: String::new(),
right: String::new(),
bounds,
config: StatusBarConfig::default(),
dirty: true,
}
}
pub const fn with_config(bounds: Rect, config: StatusBarConfig) -> Self {
Self {
left: String::new(),
center: String::new(),
right: String::new(),
bounds,
config,
dirty: true,
}
}
pub fn set_left(&mut self, text: impl Into<String>) {
self.left = text.into();
self.dirty = true;
}
pub fn set_center(&mut self, text: impl Into<String>) {
self.center = text.into();
self.dirty = true;
}
pub fn set_right(&mut self, text: impl Into<String>) {
self.right = text.into();
self.dirty = true;
}
pub fn set_all(&mut self, left: impl Into<String>, center: impl Into<String>, right: impl Into<String>) {
self.left = left.into();
self.center = center.into();
self.right = right.into();
self.dirty = true;
}
pub fn left(&self) -> &str {
&self.left
}
pub fn center(&self) -> &str {
&self.center
}
pub fn right(&self) -> &str {
&self.right
}
}
impl Widget for StatusBar {
fn bounds(&self) -> Rect {
self.bounds
}
fn set_bounds(&mut self, bounds: Rect) {
self.bounds = bounds;
self.dirty = true;
}
fn render(&self, buffer: &mut Buffer) {
let x = self.bounds.x;
let y = self.bounds.y;
let width = self.bounds.width as usize;
for i in 0..self.bounds.width {
buffer.set(x + i, y, Cell::new(' ').with_bg(self.config.bg));
}
let left_chars: Vec<char> = self.left.chars().collect();
for (i, &c) in left_chars.iter().take(width / 3).enumerate() {
#[allow(clippy::cast_possible_truncation)]
let px = x + i as u16;
buffer.set(px, y, Cell::new(c)
.with_fg(self.config.left_fg)
.with_bg(self.config.bg));
}
let center_chars: Vec<char> = self.center.chars().collect();
let center_len = center_chars.len().min(width / 3);
#[allow(clippy::cast_possible_truncation)]
let center_start = x + ((width - center_len) / 2) as u16;
for (i, &c) in center_chars.iter().take(center_len).enumerate() {
#[allow(clippy::cast_possible_truncation)]
let px = center_start + i as u16;
buffer.set(px, y, Cell::new(c)
.with_fg(self.config.center_fg)
.with_bg(self.config.bg));
}
let right_chars: Vec<char> = self.right.chars().collect();
let right_len = right_chars.len().min(width / 3);
#[allow(clippy::cast_possible_truncation)]
let right_start = x + (width - right_len) as u16;
for (i, &c) in right_chars.iter().take(right_len).enumerate() {
#[allow(clippy::cast_possible_truncation)]
let px = right_start + i as u16;
buffer.set(px, y, Cell::new(c)
.with_fg(self.config.right_fg)
.with_bg(self.config.bg));
}
}
fn handle_input(&mut self, _event: &InputEvent) -> bool {
false
}
fn needs_redraw(&self) -> bool {
self.dirty
}
fn clear_redraw(&mut self) {
self.dirty = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_status_bar_basic() {
let mut bar = StatusBar::new(Rect::new(0, 0, 80, 1));
bar.set_left("Left");
bar.set_center("Center");
bar.set_right("Right");
assert_eq!(bar.left(), "Left");
assert_eq!(bar.center(), "Center");
assert_eq!(bar.right(), "Right");
}
#[test]
fn test_status_bar_set_all() {
let mut bar = StatusBar::new(Rect::new(0, 0, 80, 1));
bar.set_all("A", "B", "C");
assert_eq!(bar.left(), "A");
assert_eq!(bar.center(), "B");
assert_eq!(bar.right(), "C");
}
}