use std::any::Any;
use std::sync::RwLock;
use super::{Element, ViewLimits, ViewStretch};
use super::context::{BasicContext, Context};
use crate::support::point::Point;
use crate::support::color::Color;
use crate::support::theme::get_theme;
#[derive(Debug, Clone)]
pub struct StatusSegment {
pub text: String,
pub flex: f32, }
impl StatusSegment {
pub fn new(text: impl Into<String>) -> Self {
Self {
text: text.into(),
flex: 0.0,
}
}
pub fn flex(text: impl Into<String>, flex: f32) -> Self {
Self {
text: text.into(),
flex,
}
}
}
pub struct StatusBar {
segments: RwLock<Vec<StatusSegment>>,
background_color: Color,
text_color: Color,
separator_color: Color,
height: f32,
padding: f32,
}
impl StatusBar {
pub fn new() -> Self {
let theme = get_theme();
Self {
segments: RwLock::new(Vec::new()),
background_color: theme.panel_color,
text_color: theme.label_font_color.with_alpha(0.8),
separator_color: theme.frame_color,
height: 24.0,
padding: 8.0,
}
}
pub fn segments(self, segments: Vec<StatusSegment>) -> Self {
*self.segments.write().unwrap() = segments;
self
}
pub fn text(self, text: impl Into<String>) -> Self {
*self.segments.write().unwrap() = vec![StatusSegment::flex(text, 1.0)];
self
}
pub fn background_color(mut self, color: Color) -> Self {
self.background_color = color;
self
}
pub fn text_color(mut self, color: Color) -> Self {
self.text_color = color;
self
}
pub fn height(mut self, height: f32) -> Self {
self.height = height;
self
}
pub fn set_segment_text(&self, index: usize, text: impl Into<String>) {
let mut segments = self.segments.write().unwrap();
if let Some(segment) = segments.get_mut(index) {
segment.text = text.into();
}
}
pub fn set_text(&self, text: impl Into<String>) {
self.set_segment_text(0, text);
}
fn calculate_segment_widths(&self, total_width: f32) -> Vec<f32> {
let segments = self.segments.read().unwrap();
let theme = get_theme();
if segments.is_empty() {
return Vec::new();
}
let mut widths = Vec::with_capacity(segments.len());
let mut fixed_width = 0.0f32;
let mut total_flex = 0.0f32;
for segment in segments.iter() {
if segment.flex == 0.0 {
let w = segment.text.len() as f32 * theme.label_font_size * 0.6 + self.padding * 2.0;
widths.push(w);
fixed_width += w;
} else {
widths.push(0.0);
total_flex += segment.flex;
}
}
let remaining = (total_width - fixed_width).max(0.0);
for (i, segment) in segments.iter().enumerate() {
if segment.flex > 0.0 {
widths[i] = remaining * (segment.flex / total_flex);
}
}
widths
}
}
impl Default for StatusBar {
fn default() -> Self {
Self::new()
}
}
impl Element for StatusBar {
fn limits(&self, _ctx: &BasicContext) -> ViewLimits {
ViewLimits {
min: Point::new(100.0, self.height),
max: Point::new(super::FULL_EXTENT, self.height),
}
}
fn stretch(&self) -> ViewStretch {
ViewStretch::new(1.0, 0.0)
}
fn draw(&self, ctx: &Context) {
let mut canvas = ctx.canvas.borrow_mut();
let theme = get_theme();
canvas.fill_style(self.background_color);
canvas.fill_rect(ctx.bounds);
canvas.stroke_style(self.separator_color);
canvas.line_width(1.0);
canvas.begin_path();
canvas.move_to(Point::new(ctx.bounds.left, ctx.bounds.top));
canvas.line_to(Point::new(ctx.bounds.right, ctx.bounds.top));
canvas.stroke();
let segments = self.segments.read().unwrap();
let widths = self.calculate_segment_widths(ctx.bounds.width());
let mut x = ctx.bounds.left;
for (i, segment) in segments.iter().enumerate() {
let width = widths.get(i).copied().unwrap_or(0.0);
canvas.fill_style(self.text_color);
canvas.font_size(theme.label_font_size * 0.9);
let text_x = x + self.padding;
let text_y = ctx.bounds.center().y + theme.label_font_size * 0.3;
let max_chars = ((width - self.padding * 2.0) / (theme.label_font_size * 0.5)) as usize;
let display_text = if segment.text.len() > max_chars && max_chars > 3 {
format!("{}...", &segment.text[..max_chars - 3])
} else {
segment.text.clone()
};
canvas.fill_text(&display_text, Point::new(text_x, text_y));
x += width;
if i < segments.len() - 1 {
canvas.stroke_style(self.separator_color);
canvas.line_width(1.0);
canvas.begin_path();
canvas.move_to(Point::new(x, ctx.bounds.top + 4.0));
canvas.line_to(Point::new(x, ctx.bounds.bottom - 4.0));
canvas.stroke();
}
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
pub fn status_bar() -> StatusBar {
StatusBar::new()
}
pub fn status_bar_with_text(text: impl Into<String>) -> StatusBar {
StatusBar::new().text(text)
}