use super::Widget;
use crate::{ColorPair, Result, Window};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StatusBarPosition {
Bottom,
Top,
Percentage(u16),
}
pub struct StatusBar {
left: String,
center: String,
right: String,
colors: Option<ColorPair>,
height: u16,
position: StatusBarPosition,
fill_background: bool,
}
impl StatusBar {
pub fn new() -> Self {
Self {
left: String::new(),
center: String::new(),
right: String::new(),
colors: None,
height: 1,
position: StatusBarPosition::Bottom,
fill_background: true,
}
}
pub fn with_left(mut self, text: impl Into<String>) -> Self {
self.left = text.into();
self
}
pub fn with_center(mut self, text: impl Into<String>) -> Self {
self.center = text.into();
self
}
pub fn with_right(mut self, text: impl Into<String>) -> Self {
self.right = text.into();
self
}
pub fn with_sections(
mut self,
left: impl Into<String>,
center: impl Into<String>,
right: impl Into<String>,
) -> Self {
self.left = left.into();
self.center = center.into();
self.right = right.into();
self
}
pub fn with_color(mut self, colors: ColorPair) -> Self {
self.colors = Some(colors);
self
}
pub fn with_height(mut self, height: u16) -> Self {
self.height = height;
self
}
pub fn with_position(mut self, position: StatusBarPosition) -> Self {
self.position = position;
self
}
pub fn with_fill_background(mut self, fill: bool) -> Self {
self.fill_background = fill;
self
}
pub fn set_left(&mut self, text: impl Into<String>) {
self.left = text.into();
}
pub fn set_center(&mut self, text: impl Into<String>) {
self.center = text.into();
}
pub fn set_right(&mut self, text: impl Into<String>) {
self.right = text.into();
}
pub fn left(&self) -> &str {
&self.left
}
pub fn center(&self) -> &str {
&self.center
}
pub fn right(&self) -> &str {
&self.right
}
pub fn calculate_y(&self, window_height: u16) -> u16 {
if self.height >= window_height {
return 0;
}
match self.position {
StatusBarPosition::Bottom => window_height - self.height,
StatusBarPosition::Top => 0,
StatusBarPosition::Percentage(percent) => {
let max_percent = 100 - (self.height * 100 / window_height);
let clamped_percent = percent.min(max_percent);
(clamped_percent as u32 * window_height as u32 / 100) as u16
}
}
}
}
impl Widget for StatusBar {
fn draw(&self, window: &mut dyn Window) -> Result<()> {
let (width, height) = window.get_size();
let y = self.calculate_y(height);
if self.fill_background {
if let Some(colors) = self.colors {
for i in 0..self.height {
let full_line = " ".repeat(width as usize);
window.write_str_colored(y + i, 0, &full_line, colors)?;
}
}
}
if !self.left.is_empty() {
let left_text = if self.left.chars().count() as u16 <= width {
self.left.clone()
} else {
let max_len = width.saturating_sub(1) as usize;
let chars: Vec<char> = self.left.chars().take(max_len).collect();
chars.into_iter().collect::<String>() + "…"
};
match self.colors {
Some(colors) => window.write_str_colored(y, 0, &left_text, colors),
None => window.write_str(y, 0, &left_text),
}?;
}
if !self.right.is_empty() {
let right_len = self.right.chars().count() as u16;
let right_x = width.saturating_sub(right_len);
let right_text = if right_len <= width {
self.right.clone()
} else {
let max_len = width.saturating_sub(1) as usize;
let chars: Vec<char> = self.right.chars().take(max_len).collect();
chars.into_iter().collect::<String>() + "…"
};
match self.colors {
Some(colors) => window.write_str_colored(y, right_x, &right_text, colors),
None => window.write_str(y, right_x, &right_text),
}?;
}
if !self.center.is_empty() {
let center_len = self.center.chars().count() as u16;
let left_available = if !self.left.is_empty() {
self.left.chars().count() as u16 + 1
} else {
0
};
let right_available = if !self.right.is_empty() {
self.right.chars().count() as u16 + 1
} else {
0
};
let center_x = if center_len + left_available + right_available < width {
left_available + (width - left_available - right_available - center_len) / 2
} else {
left_available
};
let center_text = if center_x + center_len <= width {
self.center.clone()
} else {
let available = width.saturating_sub(center_x);
if available > 1 {
let max_len = available.saturating_sub(1) as usize;
let chars: Vec<char> = self.center.chars().take(max_len).collect();
chars.into_iter().collect::<String>() + "…"
} else {
String::new()
}
};
match self.colors {
Some(colors) => window.write_str_colored(y, center_x, ¢er_text, colors),
None => window.write_str(y, center_x, ¢er_text),
}?;
}
for i in 1..self.height {
if y + i < height && self.fill_background {
if let Some(colors) = self.colors {
let full_line = " ".repeat(width as usize);
window.write_str_colored(y + i, 0, &full_line, colors)?;
}
}
}
Ok(())
}
fn get_size(&self) -> (u16, u16) {
(u16::MAX, self.height)
}
fn get_position(&self) -> (u16, u16) {
(0, 0) }
}
impl Default for StatusBar {
fn default() -> Self {
Self::new()
}
}