diff --git a/src/app.rs b/src/app.rs index 33fad39..8cb1301 100644 --- a/src/app.rs +++ b/src/app.rs @@ -13,15 +13,15 @@ mod td; mod filter; mod main; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] pub struct Cursor { row: usize, col: usize, } -impl Cursor { - fn -} +// impl Cursor { +// fn +// } #[derive(Debug, Default)] pub struct App { @@ -39,10 +39,11 @@ pub enum Screen { impl Default for Screen { fn default () -> Self { - Self::Main(MainScreen::default()) + Self::Main { inner: MainScreen::default(), cursor: Default::default() } } } + #[derive(Debug, Default, PartialEq, Eq, FromRepr, EnumCount, EnumIter, Clone, Copy, Display)] pub enum MainScreen { #[default] @@ -55,6 +56,14 @@ pub enum MainScreen { } +impl MainScreen { + pub fn with_default_cursor (self) -> Screen { + Screen::Main { + inner: self, + cursor: Default::default(), + } + } +} #[derive(Debug, Default)] @@ -99,46 +108,45 @@ impl App { fn handle_key_event (&mut self, k: KeyEvent) -> Result { - match &self.screen { - Screen::Main (screen) => { + match &mut self.screen { + Screen::Main { inner, cursor } => { match self.mode { Mode::Normal => { match k.code { KeyCode::Char('q') => self.screen = Screen::Exiting, KeyCode::Tab => { - let next = *screen as usize + 1; - self.cursor = (0, 0); + let next = *inner as usize + 1; + // self.cursor = (0, 0); self.selection.clear(); - self.screen = Screen::Main(MainScreen::from_repr(next % MainScreen::COUNT).unwrap_or_default()); + self.screen = MainScreen::from_repr(next % MainScreen::COUNT).unwrap_or_default().with_default_cursor(); }, KeyCode::BackTab => { - let prev = (*screen as usize).checked_sub(1).unwrap_or(MainScreen::COUNT - 1); - self.cursor = (0, 0); + let prev = (*inner as usize).checked_sub(1).unwrap_or(MainScreen::COUNT - 1); self.selection.clear(); - self.screen = Screen::Main(MainScreen::from_repr(prev).unwrap_or_default()); + self.screen = MainScreen::from_repr(prev % MainScreen::COUNT).unwrap_or_default().with_default_cursor(); }, KeyCode::Up => { - if self.cursor.0 > 0 { - self.cursor.0 -= 1; + if cursor.row > 0 { + cursor.row -= 1; } }, KeyCode::Left => { - if self.cursor.1 > 0 { - self.cursor.1 -= 1; + if cursor.col > 0 { + cursor.col -= 1; } }, KeyCode::Down => { - self.cursor.0 += 1; + cursor.row += 1; }, KeyCode::Right => { - self.cursor.1 += 1; + cursor.col += 1; }, KeyCode::Char(' ') => { - if self.selection.contains(&self.cursor.0) { - self.selection.remove(&self.cursor.0); + if self.selection.contains(&cursor.row) { + self.selection.remove(&cursor.row); } else { - self.selection.insert(self.cursor.0); + self.selection.insert(cursor.row); } } @@ -153,7 +161,7 @@ impl App { Screen::Exiting => { match k.code { KeyCode::Enter | KeyCode::Char('y') | KeyCode::Char('q') => return Ok(true), - KeyCode::Esc | KeyCode::Char('n') => self.screen = Screen::Main(MainScreen::default()), + KeyCode::Esc | KeyCode::Char('n') => self.screen = Screen::default(), _ => () } } @@ -174,7 +182,7 @@ impl Widget for &App { impl Screen { fn render (&self, app: &App, area: Rect, buf: &mut Buffer) -> () { match self { - Self::Main(ms) => main::screen(app, *ms, area, buf), + Self::Main { inner, cursor } => main::screen(app, *inner, *cursor, area, buf), _ => () } } diff --git a/src/app/main.rs b/src/app/main.rs index ade0b0e..c3e3bfe 100644 --- a/src/app/main.rs +++ b/src/app/main.rs @@ -1,4 +1,4 @@ -use crate::prelude::*; +use crate::{app::{Cursor, Screen}, prelude::*}; use super::MainScreen; use ratatui::{ @@ -12,7 +12,7 @@ use utils::truncate_with_ellipsis as tc; use itertools::Itertools; use strum::IntoEnumIterator; -pub fn screen (app: &App, ms: MainScreen, area: Rect, buf: &mut Buffer) { +pub fn screen (app: &App, ms: MainScreen, cursor: Cursor, area: Rect, buf: &mut Buffer) { let layout = Layout::default() .direction(Direction::Vertical) .constraints(vec![ @@ -34,79 +34,80 @@ pub fn screen (app: &App, ms: MainScreen, area: Rect, buf: &mut Buffer) { .intersperse(Span::styled(" | ", Style::default().fg(Color::White))) .collect_vec() ).centered()).block(Block::default().borders(Borders::BOTTOM).padding(Padding::top(1))).render(layout[0], buf); - let mut list_items = Vec::::new(); + let mut list_items = Vec::::new(); + let w = area.width; - let w = area.width; - - macro_rules! wx { - ($i: literal) => { - (w / 100).max(1) as usize * $i - }; - } - - let cw: [(usize, &'static str, super::td::TdFn); 6] = [ - (wx!(4), "#", super::td::id), - (wx!(15), "Title", super::td::name), - (wx!(40), "Description", super::td::description), - (wx!(15), "Expires", super::td::expiry), - (wx!(10), "Status", super::td::status), - (wx!(10), "Kind", super::td::kind) - ]; - - let th = |col: usize| if col == app.cursor.1 % cw.len() { Style::default().bold().bg(Color::from_u32(0x253325)) } else { Style::default() }; - - let delimiter = || Span::styled("|", Style::default()); - - let header: Vec = cw.iter().enumerate().map(|(idx, (width, h, _))| Span::styled(format!("{: ^width$}", h, width = width), th(idx))).intersperse_with(delimiter).collect_vec(); - - let iterator = || app.tasks.iter().filter(ms.task_filter()); - let item_count = iterator().count(); - - let td = |row: usize, col: usize| match (row, col) { - (r, c) if r == app.cursor.0 % item_count && c == app.cursor.1 % cw.len() => Style::default().bg(Color::from_u32(0x007700)), - (r, _) if r == app.cursor.0 % item_count => Style::default().bg(Color::from_u32(0x005500)), - // (_, c) if c == app.cursor.1 % cw.len() => Style::default().bg(Color::from_u32(0x001500)), - _ => Style::default() + macro_rules! wx { + ($i: literal) => { + (w / 100).max(1) as usize * $i }; + } - let tr = |row: usize| return move |col: usize| td(row, col); - - list_items.push(ListItem::new(Line::from(header))); - list_items.push(ListItem::new(Line::from(Span::from("-".repeat(w as usize))))); - - for s @ (idx, _task) in iterator().enumerate() { - let idx = idx % item_count; - let td = tr(idx); - let bg = if app.selection.contains(&idx) { - Color::from_u32(0x000044) - } else if idx == app.cursor.0 % item_count { - Color::from_u32(0x002200) - } else { - Color::default() - }; - let line = cw.iter().enumerate().map(|(idx, (width, _, content))| Span::styled(format!("{: ^width$}", tc(&content(s), *width), width = width), td(idx))).intersperse_with(delimiter).collect_vec(); - list_items.push(ListItem::new(Line::from(line).bg(bg))); - } - - let list = List::new(list_items); - Widget::render(list, layout[1], buf); + let cw: [(usize, &'static str, super::td::TdFn); 6] = [ + (wx!(4), "#", super::td::id), + (wx!(15), "Title", super::td::name), + (wx!(40), "Description", super::td::description), + (wx!(15), "Expires", super::td::expiry), + (wx!(10), "Status", super::td::status), + (wx!(10), "Kind", super::td::kind) + ]; - let footer = Layout::default() - .direction(Direction::Horizontal) - .constraints(vec![ - Constraint::Percentage(40), - Constraint::Percentage(40), - Constraint::Fill(1) - ]) - .split(layout[2]); - if !app.selection.is_empty() { - let total = item_count; - let selected = app.selection.len(); + let th = |col: usize| if col == cursor.col % cw.len() { Style::default().bold().bg(Color::from_u32(0x253325)) } else { Style::default() }; - Paragraph::new(Line::from(Span::styled(format!("[{selected}/{total}]"), Style::default()))).block(Block::default().borders(Borders::ALL)).render(footer[2], buf); - } + let delimiter = || Span::styled("|", Style::default()); + + let header: Vec = cw.iter().enumerate().map(|(idx, (width, h, _))| Span::styled(format!("{: ^width$}", h, width = width), th(idx))).intersperse_with(delimiter).collect_vec(); + + let iterator = || app.tasks.iter().filter(ms.task_filter()); + let item_count = iterator().count(); + + let td = |row: usize, col: usize| match (row, col) { + (r, c) if r == cursor.row % item_count && c == cursor.col % cw.len() => Style::default().bg(Color::from_u32(0x007700)), + (r, _) if r == cursor.row % item_count => Style::default().bg(Color::from_u32(0x005500)), + // (_, c) if c == app.cursor.1 % cw.len() => Style::default().bg(Color::from_u32(0x001500)), + _ => Style::default() + }; + + let tr = |row: usize| return move |col: usize| td(row, col); + + list_items.push(ListItem::new(Line::from(header))); + list_items.push(ListItem::new(Line::from(Span::from("-".repeat(w as usize))))); + + for s @ (idx, _task) in iterator().enumerate() { + let idx = idx % item_count; + let td = tr(idx); + let bg = if app.selection.contains(&idx) { + Color::from_u32(0x000044) + } else if idx == cursor.row % item_count { + Color::from_u32(0x002200) + } else { + Color::default() + }; + let line = cw.iter().enumerate().map(|(idx, (width, _, content))| Span::styled(format!("{: ^width$}", tc(&content(s), *width), width = width), td(idx))).intersperse_with(delimiter).collect_vec(); + list_items.push(ListItem::new(Line::from(line).bg(bg))); + } + + let list = List::new(list_items); + Widget::render(list, layout[1], buf); + + + let footer = Layout::default() + .direction(Direction::Horizontal) + .constraints(vec![ + Constraint::Percentage(40), + Constraint::Percentage(40), + Constraint::Fill(1) + ]) + .split(layout[2]); + + if !app.selection.is_empty() { + let total = item_count; + let selected = app.selection.len(); + + Paragraph::new(Line::from(Span::styled(format!("[{selected}/{total}]"), Style::default()))).block(Block::default().borders(Borders::ALL)).render(footer[2], buf); + } }