scrin 0.1.80

A terminal UI toolkit with panes, widgets, overlays, animations, and Aisling-powered effects/loaders.
Documentation
use crate::core::buffer::Buffer;
use crate::core::color::Color;
use crate::core::rect::Rect;

#[derive(Debug, Clone)]
pub struct StatusBarSection {
    pub text: String,
    pub color: Color,
    pub bold: bool,
}

#[derive(Debug, Clone, PartialEq)]
pub enum StatusBarPosition {
    Top,
    Bottom,
}

#[derive(Debug, Clone)]
pub struct StatusBar {
    pub left_sections: Vec<StatusBarSection>,
    pub center_sections: Vec<StatusBarSection>,
    pub right_sections: Vec<StatusBarSection>,
    pub position: StatusBarPosition,
    pub bg: Color,
    pub border_color: Color,
    pub height: u16,
}

impl StatusBar {
    pub fn new() -> Self {
        Self {
            left_sections: Vec::new(),
            center_sections: Vec::new(),
            right_sections: Vec::new(),
            position: StatusBarPosition::Bottom,
            bg: Color::rgb(13, 17, 23),
            border_color: Color::rgb(48, 54, 61),
            height: 1,
        }
    }

    pub fn with_position(mut self, pos: StatusBarPosition) -> Self {
        self.position = pos;
        self
    }

    pub fn with_bg(mut self, bg: Color) -> Self {
        self.bg = bg;
        self
    }

    pub fn set_left(&mut self, text: &str, color: Color) {
        self.left_sections.clear();
        self.left_sections.push(StatusBarSection {
            text: text.to_string(),
            color,
            bold: false,
        });
    }

    pub fn set_center(&mut self, text: &str, color: Color) {
        self.center_sections.clear();
        self.center_sections.push(StatusBarSection {
            text: text.to_string(),
            color,
            bold: false,
        });
    }

    pub fn set_right(&mut self, text: &str, color: Color) {
        self.right_sections.clear();
        self.right_sections.push(StatusBarSection {
            text: text.to_string(),
            color,
            bold: false,
        });
    }

    pub fn push_left(&mut self, text: &str, color: Color) {
        self.left_sections.push(StatusBarSection {
            text: text.to_string(),
            color,
            bold: false,
        });
    }

    pub fn push_right(&mut self, text: &str, color: Color) {
        self.right_sections.push(StatusBarSection {
            text: text.to_string(),
            color,
            bold: false,
        });
    }

    pub fn clear(&mut self) {
        self.left_sections.clear();
        self.center_sections.clear();
        self.right_sections.clear();
    }

    pub fn render(&self, buffer: &mut Buffer, area: Rect) {
        let bar_rect = match self.position {
            StatusBarPosition::Bottom => Rect::new(
                area.x,
                area.bottom().saturating_sub(self.height),
                area.width,
                self.height,
            ),
            StatusBarPosition::Top => Rect::new(area.x, area.y, area.width, self.height),
        };

        buffer.fill(bar_rect, ' ', Color::WHITE, Some(self.bg));

        if bar_rect.height > 1 {
            for x in bar_rect.x as usize..bar_rect.right() as usize {
                buffer.set(
                    x,
                    bar_rect.y as usize,
                    crate::core::buffer::Cell {
                        ch: '',
                        fg: self.border_color,
                        bg: Some(self.bg),
                        bold: false,
                        italic: false,
                        underlined: false,
                    },
                );
            }
        }

        let left_text: String = self
            .left_sections
            .iter()
            .map(|s| s.text.as_str())
            .collect::<Vec<_>>()
            .join("");
        let center_text: String = self
            .center_sections
            .iter()
            .map(|s| s.text.as_str())
            .collect::<Vec<_>>()
            .join("");
        let right_text: String = self
            .right_sections
            .iter()
            .map(|s| s.text.as_str())
            .collect::<Vec<_>>()
            .join("");

        let left_color = self
            .left_sections
            .first()
            .map(|s| s.color)
            .unwrap_or(Color::rgb(139, 148, 158));
        let center_color = self
            .center_sections
            .first()
            .map(|s| s.color)
            .unwrap_or(Color::rgb(139, 148, 158));
        let right_color = self
            .right_sections
            .first()
            .map(|s| s.color)
            .unwrap_or(Color::rgb(139, 148, 158));

        let bar_y = if bar_rect.height > 1 {
            bar_rect.y as usize + 1
        } else {
            bar_rect.y as usize
        };

        let left_display: String = left_text.chars().take((area.width / 3) as usize).collect();
        buffer.set_str(
            (bar_rect.x + 2) as usize,
            bar_y,
            &left_display,
            left_color,
            Some(self.bg),
        );

        if !center_text.is_empty() {
            let center_display: String = center_text
                .chars()
                .take((area.width / 3) as usize)
                .collect();
            let cx = bar_rect.x as usize + (area.width as usize - center_display.len()) / 2;
            buffer.set_str(cx, bar_y, &center_display, center_color, Some(self.bg));
        }

        if !right_text.is_empty() {
            let right_display: String =
                right_text.chars().take((area.width / 3) as usize).collect();
            let rx = bar_rect.right() as usize - right_display.len() - 2;
            buffer.set_str(rx, bar_y, &right_display, right_color, Some(self.bg));
        }
    }
}

impl Default for StatusBar {
    fn default() -> Self {
        Self::new()
    }
}