use super::{
BatteryWidget, DateTimeWidget, NetworkWidget, NewTermWidget, SystemMenuWidget, Widget,
WidgetAlignment, WidgetClickResult, WidgetContext,
};
use crate::rendering::{Cell, Theme, VideoBuffer};
use crate::window::manager::FocusState;
#[derive(Clone, Debug)]
struct WidgetPosition {
alignment: WidgetAlignment,
index: usize,
x: u16,
}
pub struct TopBar {
new_term: NewTermWidget,
datetime: DateTimeWidget,
battery: BatteryWidget,
network: NetworkWidget,
system_menu: SystemMenuWidget,
positions: Vec<WidgetPosition>,
last_cols: u16,
layout_dirty: bool,
}
impl TopBar {
pub fn new(show_date_in_clock: bool) -> Self {
Self {
new_term: NewTermWidget::new(),
datetime: DateTimeWidget::new(show_date_in_clock),
battery: BatteryWidget::new(),
network: NetworkWidget::new(),
system_menu: SystemMenuWidget::new(),
positions: Vec::new(),
last_cols: 0,
layout_dirty: true, }
}
pub fn configure_network(&mut self, interface: &str, enabled: bool) {
self.network.configure(interface, enabled);
self.layout_dirty = true; }
pub fn update(&mut self, ctx: &WidgetContext) {
self.new_term.update(ctx);
self.datetime.update(ctx);
self.battery.update(ctx);
self.network.update(ctx);
self.system_menu.update(ctx);
if ctx.cols != self.last_cols {
self.last_cols = ctx.cols;
self.layout_dirty = true;
}
if self.layout_dirty {
self.layout(ctx);
self.layout_dirty = false;
}
}
fn layout(&mut self, ctx: &WidgetContext) {
self.positions.clear();
let left_x = 1u16;
if self.new_term.is_visible(ctx) {
self.positions.push(WidgetPosition {
alignment: WidgetAlignment::Left,
index: 0,
x: left_x,
});
}
let mut right_x = ctx.cols;
if self.system_menu.is_visible(ctx) {
let sm_width = self.system_menu.width();
right_x = right_x.saturating_sub(sm_width + 1); self.positions.push(WidgetPosition {
alignment: WidgetAlignment::Right,
index: 1, x: right_x,
});
}
if self.network.is_visible(ctx) {
let network_width = self.network.width();
right_x = right_x.saturating_sub(network_width);
self.positions.push(WidgetPosition {
alignment: WidgetAlignment::Right,
index: 2, x: right_x,
});
}
if self.battery.is_visible(ctx) {
let battery_width = self.battery.width();
right_x = right_x.saturating_sub(battery_width);
self.positions.push(WidgetPosition {
alignment: WidgetAlignment::Right,
index: 0, x: right_x,
});
}
let datetime_width = self.datetime.width();
let center_x = ctx.cols.saturating_sub(datetime_width) / 2;
if self.datetime.is_visible(ctx) {
self.positions.push(WidgetPosition {
alignment: WidgetAlignment::Center,
index: 0,
x: center_x,
});
}
self.positions.sort_by_key(|p| p.x);
}
pub fn render(&self, buffer: &mut VideoBuffer, theme: &Theme, ctx: &WidgetContext) {
let (cols, _) = buffer.dimensions();
let (bg_color, fg_color) = match ctx.focus {
FocusState::Desktop | FocusState::Topbar => {
(theme.topbar_bg_focused, theme.topbar_fg_focused)
}
FocusState::Window(_) => (theme.topbar_bg_unfocused, theme.topbar_fg_unfocused),
};
let bar_cell = Cell::new_unchecked(' ', fg_color, bg_color);
for x in 0..cols {
buffer.set(x, 0, bar_cell);
}
for pos in &self.positions {
match (pos.alignment, pos.index) {
(WidgetAlignment::Left, 0) => self.new_term.render(buffer, pos.x, theme, ctx),
(WidgetAlignment::Center, 0) => self.datetime.render(buffer, pos.x, theme, ctx),
(WidgetAlignment::Right, 0) => self.battery.render(buffer, pos.x, theme, ctx),
(WidgetAlignment::Right, 1) => self.system_menu.render(buffer, pos.x, theme, ctx),
(WidgetAlignment::Right, 2) => self.network.render(buffer, pos.x, theme, ctx),
_ => {}
}
}
}
pub fn update_hover(&mut self, mouse_x: u16, mouse_y: u16, _ctx: &WidgetContext) {
if mouse_y != 0 {
self.reset_all_states();
return;
}
for pos in &self.positions {
match (pos.alignment, pos.index) {
(WidgetAlignment::Left, 0) => {
self.new_term.update_hover(mouse_x, mouse_y, pos.x);
}
(WidgetAlignment::Center, 0) => {
self.datetime.update_hover(mouse_x, mouse_y, pos.x);
}
(WidgetAlignment::Right, 0) => {
self.battery.update_hover(mouse_x, mouse_y, pos.x);
}
(WidgetAlignment::Right, 1) => {
self.system_menu.update_hover(mouse_x, mouse_y, pos.x);
}
(WidgetAlignment::Right, 2) => {
self.network.update_hover(mouse_x, mouse_y, pos.x);
}
_ => {}
}
}
}
pub fn handle_click(&mut self, mouse_x: u16, mouse_y: u16) -> WidgetClickResult {
if mouse_y != 0 {
return WidgetClickResult::NotHandled;
}
for pos in &self.positions {
let result = match (pos.alignment, pos.index) {
(WidgetAlignment::Left, 0) => self.new_term.handle_click(mouse_x, mouse_y, pos.x),
(WidgetAlignment::Center, 0) => self.datetime.handle_click(mouse_x, mouse_y, pos.x),
(WidgetAlignment::Right, 0) => self.battery.handle_click(mouse_x, mouse_y, pos.x),
(WidgetAlignment::Right, 1) => {
self.system_menu.handle_click(mouse_x, mouse_y, pos.x)
}
(WidgetAlignment::Right, 2) => self.network.handle_click(mouse_x, mouse_y, pos.x),
_ => WidgetClickResult::NotHandled,
};
if !matches!(result, WidgetClickResult::NotHandled) {
return result;
}
}
WidgetClickResult::NotHandled
}
fn reset_all_states(&mut self) {
self.new_term.reset_state();
self.datetime.reset_state();
self.battery.reset_state();
self.network.reset_state();
self.system_menu.reset_state();
}
pub fn is_battery_hovered(&self) -> bool {
self.battery.is_hovered()
}
pub fn close_system_menu(&mut self) {
self.system_menu.close_menu();
}
pub fn get_system_menu_x(&self) -> u16 {
for pos in &self.positions {
if pos.alignment == WidgetAlignment::Right && pos.index == 1 {
return pos.x;
}
}
0
}
}
impl Default for TopBar {
fn default() -> Self {
Self::new(false)
}
}