use std::rc::Rc;
use crate::{
App, Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled, Window, div,
h_flex, px,
};
#[derive(Clone)]
pub struct StatusBarState {
pub line: usize,
pub column: usize,
pub selection_chars: Option<usize>,
pub language: SharedString,
pub encoding: SharedString,
pub line_ending: SharedString,
pub lsp_status: LspStatus,
pub lsp_server_name: Option<SharedString>,
pub error_count: usize,
pub warning_count: usize,
pub info_count: usize,
pub indent_info: Option<SharedString>,
pub git_branch: Option<SharedString>,
pub custom_items: Vec<StatusBarItem>,
}
impl Default for StatusBarState {
fn default() -> Self {
Self {
line: 1,
column: 1,
selection_chars: None,
language: "Plain Text".into(),
encoding: "UTF-8".into(),
line_ending: "LF".into(),
lsp_status: LspStatus::Disconnected,
lsp_server_name: None,
error_count: 0,
warning_count: 0,
info_count: 0,
indent_info: None,
git_branch: None,
custom_items: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LspStatus {
Connected,
Initializing,
Disconnected,
Error,
}
#[derive(Clone)]
pub struct StatusBarItem {
pub label: SharedString,
pub tooltip: Option<SharedString>,
pub on_click: Option<Rc<dyn Fn(&mut Window, &mut App)>>,
}
pub struct StatusBar {
state: Entity<StatusBarState>,
}
impl StatusBar {
pub fn new(state: Entity<StatusBarState>) -> Self {
Self { state }
}
}
impl Render for StatusBar {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let state = self.state.read(cx);
let lsp_indicator = match state.lsp_status {
LspStatus::Connected => {
let name = state
.lsp_server_name
.clone()
.unwrap_or_else(|| "LSP".into());
h_flex()
.gap_1()
.child(div().w_2().h_2().rounded_full().bg(crate::green_500()))
.child(div().text_xs().child(name))
}
LspStatus::Initializing => h_flex()
.gap_1()
.child(div().w_2().h_2().rounded_full().bg(crate::yellow_500()))
.child("LSP..."),
LspStatus::Disconnected => h_flex()
.gap_1()
.child(div().w_2().h_2().rounded_full().bg(crate::gray_500()))
.child("No LSP"),
LspStatus::Error => h_flex()
.gap_1()
.child(div().w_2().h_2().rounded_full().bg(crate::red_500()))
.child("LSP Error"),
};
let diagnostics = if state.error_count > 0 || state.warning_count > 0 {
let mut items = Vec::new();
if state.error_count > 0 {
items.push(
div()
.text_xs()
.child(format!("{} errors", state.error_count)),
);
}
if state.warning_count > 0 {
items.push(
div()
.text_xs()
.child(format!("{} warnings", state.warning_count)),
);
}
h_flex().gap_2().children(items)
} else {
div()
};
let position_info = h_flex()
.gap_1()
.child(
div()
.text_xs()
.child(format!("Ln {}, Col {}", state.line, state.column)),
)
.children(
state
.selection_chars
.map(|chars| div().text_xs().child(format!("({} selected)", chars))),
);
let language_info = h_flex()
.gap_2()
.child(div().text_xs().child(state.language.clone()))
.child(div().text_xs().child(state.encoding.clone()))
.child(div().text_xs().child(state.line_ending.clone()))
.children(
state
.indent_info
.as_ref()
.map(|info| div().text_xs().child(info.clone())),
);
let git_info = state.git_branch.as_ref().map(|branch| {
h_flex()
.gap_1()
.child(div().text_xs().child(format!(" {}", branch)))
});
h_flex()
.w_full()
.h(px(28.))
.items_center()
.justify_between()
.px_2()
.bg(crate::gray_900())
.child(
h_flex()
.items_center()
.gap_3()
.child(lsp_indicator)
.child(diagnostics),
)
.child(
h_flex()
.items_center()
.gap_3()
.children(git_info)
.child(position_info)
.child(language_info),
)
}
}