112 lines
3.2 KiB
Rust
112 lines
3.2 KiB
Rust
|
|
use crate::tui::component::{Action, Component, ComponentState};
|
||
|
|
use ratatui::buffer::Buffer;
|
||
|
|
use ratatui::crossterm::event::{KeyCode, KeyEvent};
|
||
|
|
use ratatui::layout::Rect;
|
||
|
|
use ratatui::style::{Color, Style};
|
||
|
|
use ratatui::text::Line;
|
||
|
|
use ratatui::widgets::{Block, Borders, Tabs, Widget};
|
||
|
|
use strum::{EnumIter, FromRepr, IntoEnumIterator};
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr, EnumIter)]
|
||
|
|
enum TitleBarItem {
|
||
|
|
File,
|
||
|
|
Edit,
|
||
|
|
View,
|
||
|
|
Help,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl TitleBarItem {
|
||
|
|
pub fn next(mut self) -> Self {
|
||
|
|
let cur = self as usize;
|
||
|
|
let next = cur.saturating_add(1);
|
||
|
|
Self::from_repr(next).unwrap_or(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn prev(self) -> Self {
|
||
|
|
let cur = self as usize;
|
||
|
|
let prev = cur.saturating_sub(1);
|
||
|
|
Self::from_repr(prev).unwrap_or(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn id(&self) -> &str {
|
||
|
|
match self {
|
||
|
|
TitleBarItem::File => "File",
|
||
|
|
TitleBarItem::Edit => "Edit",
|
||
|
|
TitleBarItem::View => "View",
|
||
|
|
TitleBarItem::Help => "Help",
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub struct TitleBar {
|
||
|
|
selected: TitleBarItem,
|
||
|
|
opened: Option<TitleBarItem>,
|
||
|
|
pub(crate) component_state: ComponentState,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl TitleBar {
|
||
|
|
pub fn new() -> Self {
|
||
|
|
Self {
|
||
|
|
selected: TitleBarItem::File,
|
||
|
|
opened: None,
|
||
|
|
component_state: ComponentState::default()
|
||
|
|
.with_help_text(concat!("TODO: Title bar help text.").as_ref()),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn render_title_bar(&self, area: Rect, buf: &mut Buffer) {
|
||
|
|
let titles: Vec<Line> = TitleBarItem::iter()
|
||
|
|
.map(|item| Line::from(item.id().to_owned()))
|
||
|
|
.collect();
|
||
|
|
let tabs_style = Style::default();
|
||
|
|
let highlight_style = if self.opened.is_some() {
|
||
|
|
Style::default().bg(Color::Blue).fg(Color::White)
|
||
|
|
} else {
|
||
|
|
Style::default().bg(Color::Cyan).fg(Color::Black)
|
||
|
|
};
|
||
|
|
Tabs::new(titles)
|
||
|
|
.style(tabs_style)
|
||
|
|
.block(Block::default().borders(Borders::ALL))
|
||
|
|
.highlight_style(highlight_style)
|
||
|
|
.select(self.selected as usize)
|
||
|
|
.render(area, buf);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Widget for &TitleBar {
|
||
|
|
fn render(self, area: Rect, buf: &mut Buffer)
|
||
|
|
where
|
||
|
|
Self: Sized,
|
||
|
|
{
|
||
|
|
let title_bar_area = Rect {
|
||
|
|
x: area.x,
|
||
|
|
y: area.y,
|
||
|
|
width: area.width,
|
||
|
|
height: 3,
|
||
|
|
};
|
||
|
|
self.render_title_bar(title_bar_area, buf);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Component for TitleBar {
|
||
|
|
fn handle_key_events(&mut self, key: KeyEvent) -> anyhow::Result<Action> {
|
||
|
|
match key.code {
|
||
|
|
// KeyCode::Up | KeyCode::Char('k') => self.selected.key_up(),
|
||
|
|
// KeyCode::Down | KeyCode::Char('j') => self.selected.key_down(),
|
||
|
|
KeyCode::Left | KeyCode::Char('h') => {
|
||
|
|
self.selected = self.selected.prev();
|
||
|
|
Ok(Action::Handled)
|
||
|
|
}
|
||
|
|
KeyCode::Right | KeyCode::Char('l') => {
|
||
|
|
self.selected = self.selected.next();
|
||
|
|
Ok(Action::Handled)
|
||
|
|
}
|
||
|
|
KeyCode::Enter => {
|
||
|
|
self.opened = Some(self.selected);
|
||
|
|
Ok(Action::Handled)
|
||
|
|
}
|
||
|
|
_ => Ok(Action::Noop),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|