feat: Added basic two level navigation
This commit is contained in:
parent
e4a840d7e4
commit
6ef492ee10
5 changed files with 134 additions and 12 deletions
27
src/app.rs
27
src/app.rs
|
@ -1,10 +1,21 @@
|
||||||
|
|
||||||
|
use crate::uis::new_transaction::NewTransactionTabState;
|
||||||
|
|
||||||
pub type AppResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
pub type AppResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
|
pub enum FocusedBlock {
|
||||||
|
Navigation,
|
||||||
|
Body,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct App<'a> {
|
pub struct App<'a> {
|
||||||
pub running: bool,
|
pub running: bool,
|
||||||
pub tabs: Vec<&'a str>,
|
pub tabs: Vec<&'a str>,
|
||||||
pub tab_index: usize,
|
pub tab_index: usize,
|
||||||
|
pub focus: FocusedBlock,
|
||||||
|
|
||||||
|
pub new_transaction_tab_state: NewTransactionTabState<'a>,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> App<'a> {
|
impl<'a> App<'a> {
|
||||||
|
@ -12,8 +23,11 @@ impl<'a> App<'a> {
|
||||||
App {
|
App {
|
||||||
running: true,
|
running: true,
|
||||||
|
|
||||||
tabs: vec!["Tab1", "Tab2", "Tab3"],
|
tabs: vec!["History", "Tab2", "Tab3"],
|
||||||
tab_index: 0,
|
tab_index: 0,
|
||||||
|
focus: FocusedBlock::Navigation,
|
||||||
|
|
||||||
|
new_transaction_tab_state: NewTransactionTabState::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,4 +41,13 @@ impl<'a> App<'a> {
|
||||||
} else {
|
} else {
|
||||||
self.tab_index = self.tabs.len() - 1;
|
self.tab_index = self.tabs.len() - 1;
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
|
|
||||||
|
pub fn focus_navigation(&mut self) {
|
||||||
|
self.focus = FocusedBlock::Navigation;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_body(&mut self) {
|
||||||
|
self.focus = FocusedBlock::Body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
src/main.rs
35
src/main.rs
|
@ -7,8 +7,9 @@ use ratatui::{
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
|
|
||||||
use recount::app::{App, AppResult};
|
use recount::app::{App, AppResult, FocusedBlock};
|
||||||
use recount::tui::Tui;
|
use recount::tui::Tui;
|
||||||
|
use recount::uis::new_transaction::NewTransactionTabState;
|
||||||
|
|
||||||
fn main() -> AppResult<()> {
|
fn main() -> AppResult<()> {
|
||||||
// Create an application.
|
// Create an application.
|
||||||
|
@ -28,16 +29,34 @@ fn main() -> AppResult<()> {
|
||||||
|
|
||||||
// Handle events.
|
// Handle events.
|
||||||
if let Event::Key(key) = event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
if key.kind == KeyEventKind::Press {
|
match app.focus {
|
||||||
match key.code {
|
FocusedBlock::Body => {
|
||||||
KeyCode::Char('q') => break,
|
match app.tab_index {
|
||||||
KeyCode::Tab => app.next_tab(),
|
2 => {
|
||||||
// KeyCode::Right => app.next(),
|
NewTransactionTabState::handle_event(key, &mut app);
|
||||||
// KeyCode::Left => app.previous(),
|
},
|
||||||
_ => {}
|
_ => {
|
||||||
|
// Work around "You entered an empty body, and now, I am dead" loops
|
||||||
|
app.focus_navigation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
FocusedBlock::Navigation => {
|
||||||
|
if key.kind == KeyEventKind::Press {
|
||||||
|
match key.code {
|
||||||
|
KeyCode::Char('q') => break,
|
||||||
|
KeyCode::Tab => app.next_tab(),
|
||||||
|
KeyCode::Enter => app.focus_body(),
|
||||||
|
// KeyCode::Right => app.next(),
|
||||||
|
// KeyCode::Left => app.previous(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread::sleep(Duration::from_millis(20));
|
std::thread::sleep(Duration::from_millis(20));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use ratatui::{
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::uis::render_history_tab;
|
use crate::uis::{render_new_transaction_tab, render_history_tab};
|
||||||
|
|
||||||
|
|
||||||
pub fn render<B: Backend> (f: &mut Frame<B>, app: &App) {
|
pub fn render<B: Backend> (f: &mut Frame<B>, app: &App) {
|
||||||
|
@ -31,7 +31,8 @@ pub fn render<B: Backend> (f: &mut Frame<B>, app: &App) {
|
||||||
.constraints([Constraint::Percentage(100)])
|
.constraints([Constraint::Percentage(100)])
|
||||||
.split(chunks[1]);
|
.split(chunks[1]);
|
||||||
// if app.tabs selected == history,
|
// if app.tabs selected == history,
|
||||||
render_history_tab(f, bottom_chunk[0], app);
|
//render_history_tab(f, bottom_chunk[0], app);
|
||||||
|
render_new_transaction_tab(f, bottom_chunk[0], app);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_statusbar<B: Backend> (f: &mut Frame<B>, status_rect: Rect, app: &App) {
|
pub fn render_statusbar<B: Backend> (f: &mut Frame<B>, status_rect: Rect, app: &App) {
|
||||||
|
|
|
@ -2,3 +2,6 @@
|
||||||
|
|
||||||
pub mod history;
|
pub mod history;
|
||||||
pub use self::history::render_history_tab;
|
pub use self::history::render_history_tab;
|
||||||
|
|
||||||
|
pub mod new_transaction;
|
||||||
|
pub use self::new_transaction::render_new_transaction_tab;
|
||||||
|
|
76
src/uis/new_transaction.rs
Normal file
76
src/uis/new_transaction.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use crate::app::App;
|
||||||
|
use crossterm::event::{Event, KeyEvent, KeyEventKind, KeyCode};
|
||||||
|
use ratatui::{backend::Backend, Frame, layout::{Rect, Layout, Direction, Constraint}, widgets::{Paragraph, Borders, Block}, text::Text, style::{Style, Color}};
|
||||||
|
|
||||||
|
pub struct NewTransactionTabState<'a> {
|
||||||
|
pub cur_tab_index: usize,
|
||||||
|
pub tabs: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NewTransactionTabState<'a> {
|
||||||
|
pub fn new() -> NewTransactionTabState<'a> {
|
||||||
|
NewTransactionTabState {
|
||||||
|
cur_tab_index: 1,
|
||||||
|
tabs: vec!["Quick Entry", "Manual Entry"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_tab(&mut self) {
|
||||||
|
self.cur_tab_index = (self.cur_tab_index + 1) % self.tabs.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_event(event: KeyEvent, app: &mut App) {
|
||||||
|
if event.kind == KeyEventKind::Press {
|
||||||
|
match event.code {
|
||||||
|
KeyCode::Char('q') => app.focus_navigation(),
|
||||||
|
KeyCode::Tab => app.new_transaction_tab_state.next_tab(),
|
||||||
|
// KeyCode::Right => app.next(),
|
||||||
|
// KeyCode::Left => app.previous(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_new_transaction_tab<B: Backend> (f: &mut Frame<B>, body_rect: Rect, app: &App) {
|
||||||
|
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Length(3), Constraint::Min(0)])
|
||||||
|
.split(body_rect);
|
||||||
|
|
||||||
|
// Render the custom tab bar
|
||||||
|
let mut constraints: Vec<Constraint> = vec![];
|
||||||
|
let tab_percent: u16 = (100 / app.new_transaction_tab_state.tabs.len()) as u16;
|
||||||
|
for _ in 0..app.new_transaction_tab_state.tabs.len() {
|
||||||
|
constraints.push(Constraint::Percentage(tab_percent));
|
||||||
|
}
|
||||||
|
|
||||||
|
let tab_chunks = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints(constraints)
|
||||||
|
.split(chunks[0]);
|
||||||
|
|
||||||
|
for i in 0..app.new_transaction_tab_state.tabs.len() {
|
||||||
|
|
||||||
|
let tab = Paragraph::new(
|
||||||
|
Text::styled(app.new_transaction_tab_state.tabs[i],
|
||||||
|
Style::default().fg(Color::White)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.alignment(ratatui::layout::Alignment::Center)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.style({
|
||||||
|
if app.new_transaction_tab_state.cur_tab_index == i {
|
||||||
|
Style::default().bg(Color::Blue)
|
||||||
|
} else {
|
||||||
|
Style::default()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
f.render_widget(tab, tab_chunks[i])
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue