TUI #1
@ -3,8 +3,8 @@ use crate::tui::component::{Action, Component, Focus, FocusState};
|
|||||||
use crate::tui::editor::Editor;
|
use crate::tui::editor::Editor;
|
||||||
use crate::tui::explorer::Explorer;
|
use crate::tui::explorer::Explorer;
|
||||||
use crate::tui::logger::Logger;
|
use crate::tui::logger::Logger;
|
||||||
use crate::tui::title_bar::TitleBar;
|
use crate::tui::title_bar::MenuBar;
|
||||||
use AppComponent::AppTitleBar;
|
use AppComponent::AppMenuBar;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
@ -27,14 +27,14 @@ pub enum AppComponent {
|
|||||||
AppEditor,
|
AppEditor,
|
||||||
AppExplorer,
|
AppExplorer,
|
||||||
AppLogger,
|
AppLogger,
|
||||||
AppTitleBar,
|
AppMenuBar,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct App<'a> {
|
pub struct App<'a> {
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
explorer: Explorer<'a>,
|
explorer: Explorer<'a>,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
title_bar: TitleBar,
|
menu_bar: MenuBar,
|
||||||
last_active: AppComponent,
|
last_active: AppComponent,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ impl<'a> App<'a> {
|
|||||||
editor: Editor::new(),
|
editor: Editor::new(),
|
||||||
explorer: Explorer::new(&root_path)?,
|
explorer: Explorer::new(&root_path)?,
|
||||||
logger: Logger::new(),
|
logger: Logger::new(),
|
||||||
title_bar: TitleBar::new(),
|
menu_bar: MenuBar::new(),
|
||||||
last_active: AppEditor,
|
last_active: AppEditor,
|
||||||
};
|
};
|
||||||
Ok(app)
|
Ok(app)
|
||||||
@ -95,12 +95,12 @@ impl<'a> App<'a> {
|
|||||||
AppEditor => self.editor.component_state.help_text.clone(),
|
AppEditor => self.editor.component_state.help_text.clone(),
|
||||||
AppExplorer => self.explorer.component_state.help_text.clone(),
|
AppExplorer => self.explorer.component_state.help_text.clone(),
|
||||||
AppLogger => self.logger.component_state.help_text.clone(),
|
AppLogger => self.logger.component_state.help_text.clone(),
|
||||||
AppTitleBar => self.title_bar.component_state.help_text.clone(),
|
AppMenuBar => self.menu_bar.component_state.help_text.clone(),
|
||||||
};
|
};
|
||||||
Paragraph::new(
|
Paragraph::new(
|
||||||
concat!(
|
concat!(
|
||||||
"ALT+Q: Focus project explorer | ALT+W: Focus editor | ALT+E: Focus logger |",
|
"ALT+Q: Focus project explorer | ALT+W: Focus editor | ALT+E: Focus logger |",
|
||||||
" CTRL+C: Quit\n"
|
" ALT+R: Focus menu bar | CTRL+C: Quit\n"
|
||||||
)
|
)
|
||||||
.to_string()
|
.to_string()
|
||||||
+ help.as_str(),
|
+ help.as_str(),
|
||||||
@ -135,15 +135,11 @@ impl<'a> App<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn change_focus(&mut self, focus: AppComponent) {
|
fn change_focus(&mut self, focus: AppComponent) {
|
||||||
if self.last_active == AppEditor {
|
|
||||||
self.editor.state.cursor.row = 0;
|
|
||||||
self.editor.state.cursor.col = 0;
|
|
||||||
}
|
|
||||||
match focus {
|
match focus {
|
||||||
AppEditor => self.editor.component_state.set_focus(Focus::Active),
|
AppEditor => self.editor.component_state.set_focus(Focus::Active),
|
||||||
AppExplorer => self.explorer.component_state.set_focus(Focus::Active),
|
AppExplorer => self.explorer.component_state.set_focus(Focus::Active),
|
||||||
AppLogger => self.logger.component_state.set_focus(Focus::Active),
|
AppLogger => self.logger.component_state.set_focus(Focus::Active),
|
||||||
AppTitleBar => self.title_bar.component_state.set_focus(Focus::Active),
|
AppMenuBar => self.menu_bar.component_state.set_focus(Focus::Active),
|
||||||
}
|
}
|
||||||
self.last_active = focus;
|
self.last_active = focus;
|
||||||
}
|
}
|
||||||
@ -210,7 +206,7 @@ impl<'a> Widget for &mut App<'a> {
|
|||||||
self.logger.render(vertical[2], buf);
|
self.logger.render(vertical[2], buf);
|
||||||
|
|
||||||
// The title bar is rendered last to overlay any popups created for drop-down menus.
|
// The title bar is rendered last to overlay any popups created for drop-down menus.
|
||||||
self.title_bar.render(vertical[0], buf);
|
self.menu_bar.render(vertical[0], buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +237,7 @@ impl<'a> Component for App<'a> {
|
|||||||
AppEditor => self.editor.handle_event(event)?,
|
AppEditor => self.editor.handle_event(event)?,
|
||||||
AppExplorer => self.explorer.handle_event(event)?,
|
AppExplorer => self.explorer.handle_event(event)?,
|
||||||
AppLogger => self.logger.handle_event(event)?,
|
AppLogger => self.logger.handle_event(event)?,
|
||||||
AppTitleBar => self.title_bar.handle_event(event)?,
|
AppMenuBar => self.menu_bar.handle_event(event)?,
|
||||||
};
|
};
|
||||||
match action {
|
match action {
|
||||||
Action::Quit | Action::Handled => return Ok(action),
|
Action::Quit | Action::Handled => return Ok(action),
|
||||||
@ -286,7 +282,7 @@ impl<'a> Component for App<'a> {
|
|||||||
kind: KeyEventKind::Press,
|
kind: KeyEventKind::Press,
|
||||||
state: _state,
|
state: _state,
|
||||||
} => {
|
} => {
|
||||||
self.change_focus(AppTitleBar);
|
self.change_focus(AppMenuBar);
|
||||||
Ok(Action::Handled)
|
Ok(Action::Handled)
|
||||||
}
|
}
|
||||||
KeyEvent {
|
KeyEvent {
|
||||||
|
|||||||
@ -10,13 +10,13 @@ use ratatui::widgets::{
|
|||||||
use strum::{EnumIter, FromRepr, IntoEnumIterator};
|
use strum::{EnumIter, FromRepr, IntoEnumIterator};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr, EnumIter)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr, EnumIter)]
|
||||||
enum TitleBarItem {
|
enum MenuBarItem {
|
||||||
File,
|
File,
|
||||||
View,
|
View,
|
||||||
Help,
|
Help,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TitleBarItem {
|
impl MenuBarItem {
|
||||||
pub fn next(self) -> Self {
|
pub fn next(self) -> Self {
|
||||||
let cur = self as usize;
|
let cur = self as usize;
|
||||||
let next = cur.saturating_add(1);
|
let next = cur.saturating_add(1);
|
||||||
@ -31,33 +31,33 @@ impl TitleBarItem {
|
|||||||
|
|
||||||
pub fn id(&self) -> &str {
|
pub fn id(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
TitleBarItem::File => "File",
|
MenuBarItem::File => "File",
|
||||||
TitleBarItem::View => "View",
|
MenuBarItem::View => "View",
|
||||||
TitleBarItem::Help => "Help",
|
MenuBarItem::Help => "Help",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn options(&self) -> &[&str] {
|
pub fn options(&self) -> &[&str] {
|
||||||
match self {
|
match self {
|
||||||
TitleBarItem::File => &["Save", "Reload"],
|
MenuBarItem::File => &["Save", "Reload"],
|
||||||
TitleBarItem::View => &["Show/hide explorer", "Show/hide logger"],
|
MenuBarItem::View => &["Show/hide explorer", "Show/hide logger"],
|
||||||
TitleBarItem::Help => &["About"],
|
MenuBarItem::Help => &["About"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TitleBar {
|
pub struct MenuBar {
|
||||||
selected: TitleBarItem,
|
selected: MenuBarItem,
|
||||||
opened: Option<TitleBarItem>,
|
opened: Option<MenuBarItem>,
|
||||||
pub(crate) component_state: ComponentState,
|
pub(crate) component_state: ComponentState,
|
||||||
list_state: ListState,
|
list_state: ListState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TitleBar {
|
impl MenuBar {
|
||||||
const DEFAULT_HELP: &str = "(←/h)/(→/l): Select option | Enter: Choose selection";
|
const DEFAULT_HELP: &str = "(←/h)/(→/l): Select option | Enter: Choose selection";
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
selected: TitleBarItem::File,
|
selected: MenuBarItem::File,
|
||||||
opened: None,
|
opened: None,
|
||||||
component_state: ComponentState::default().with_help_text(Self::DEFAULT_HELP),
|
component_state: ComponentState::default().with_help_text(Self::DEFAULT_HELP),
|
||||||
list_state: ListState::default().with_selected(Some(0)),
|
list_state: ListState::default().with_selected(Some(0)),
|
||||||
@ -65,7 +65,7 @@ impl TitleBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render_title_bar(&self, area: Rect, buf: &mut Buffer) {
|
fn render_title_bar(&self, area: Rect, buf: &mut Buffer) {
|
||||||
let titles: Vec<Line> = TitleBarItem::iter()
|
let titles: Vec<Line> = MenuBarItem::iter()
|
||||||
.map(|item| Line::from(item.id().to_owned()))
|
.map(|item| Line::from(item.id().to_owned()))
|
||||||
.collect();
|
.collect();
|
||||||
let tabs_style = Style::default();
|
let tabs_style = Style::default();
|
||||||
@ -87,7 +87,7 @@ impl TitleBar {
|
|||||||
title_bar_anchor: Rect,
|
title_bar_anchor: Rect,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
buf: &mut Buffer,
|
buf: &mut Buffer,
|
||||||
opened: TitleBarItem,
|
opened: MenuBarItem,
|
||||||
) {
|
) {
|
||||||
let popup_area = Self::rect_under_option(title_bar_anchor, area, 40, 10);
|
let popup_area = Self::rect_under_option(title_bar_anchor, area, 40, 10);
|
||||||
Clear::default().render(popup_area, buf);
|
Clear::default().render(popup_area, buf);
|
||||||
@ -135,7 +135,7 @@ impl TitleBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for TitleBar {
|
impl Component for MenuBar {
|
||||||
fn handle_key_events(&mut self, key: KeyEvent) -> anyhow::Result<Action> {
|
fn handle_key_events(&mut self, key: KeyEvent) -> anyhow::Result<Action> {
|
||||||
if self.opened.is_some() {
|
if self.opened.is_some() {
|
||||||
// Keybinds for popup menu.
|
// Keybinds for popup menu.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user