now compiles again; but at what cost
not sure if saving cursor inside the state is a good approach, im not sure why i decided for such refactoring when i touched the project the last time
This commit is contained in:
parent
007398c576
commit
0631db171f
56
src/app.rs
56
src/app.rs
@ -13,15 +13,15 @@ mod td;
|
|||||||
mod filter;
|
mod filter;
|
||||||
mod main;
|
mod main;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
|
||||||
pub struct Cursor {
|
pub struct Cursor {
|
||||||
row: usize,
|
row: usize,
|
||||||
col: usize,
|
col: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cursor {
|
// impl Cursor {
|
||||||
fn
|
// fn
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
@ -39,10 +39,11 @@ pub enum Screen {
|
|||||||
|
|
||||||
impl Default for Screen {
|
impl Default for Screen {
|
||||||
fn default () -> Self {
|
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)]
|
#[derive(Debug, Default, PartialEq, Eq, FromRepr, EnumCount, EnumIter, Clone, Copy, Display)]
|
||||||
pub enum MainScreen {
|
pub enum MainScreen {
|
||||||
#[default]
|
#[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)]
|
#[derive(Debug, Default)]
|
||||||
@ -99,46 +108,45 @@ impl App {
|
|||||||
fn handle_key_event (&mut self, k: KeyEvent) -> Result<bool> {
|
fn handle_key_event (&mut self, k: KeyEvent) -> Result<bool> {
|
||||||
|
|
||||||
|
|
||||||
match &self.screen {
|
match &mut self.screen {
|
||||||
Screen::Main (screen) => {
|
Screen::Main { inner, cursor } => {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Normal => {
|
Mode::Normal => {
|
||||||
match k.code {
|
match k.code {
|
||||||
KeyCode::Char('q') => self.screen = Screen::Exiting,
|
KeyCode::Char('q') => self.screen = Screen::Exiting,
|
||||||
KeyCode::Tab => {
|
KeyCode::Tab => {
|
||||||
let next = *screen as usize + 1;
|
let next = *inner as usize + 1;
|
||||||
self.cursor = (0, 0);
|
// self.cursor = (0, 0);
|
||||||
self.selection.clear();
|
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 => {
|
KeyCode::BackTab => {
|
||||||
let prev = (*screen as usize).checked_sub(1).unwrap_or(MainScreen::COUNT - 1);
|
let prev = (*inner as usize).checked_sub(1).unwrap_or(MainScreen::COUNT - 1);
|
||||||
self.cursor = (0, 0);
|
|
||||||
self.selection.clear();
|
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 => {
|
KeyCode::Up => {
|
||||||
if self.cursor.0 > 0 {
|
if cursor.row > 0 {
|
||||||
self.cursor.0 -= 1;
|
cursor.row -= 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
KeyCode::Left => {
|
KeyCode::Left => {
|
||||||
if self.cursor.1 > 0 {
|
if cursor.col > 0 {
|
||||||
self.cursor.1 -= 1;
|
cursor.col -= 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
KeyCode::Down => {
|
KeyCode::Down => {
|
||||||
self.cursor.0 += 1;
|
cursor.row += 1;
|
||||||
},
|
},
|
||||||
KeyCode::Right => {
|
KeyCode::Right => {
|
||||||
self.cursor.1 += 1;
|
cursor.col += 1;
|
||||||
},
|
},
|
||||||
|
|
||||||
KeyCode::Char(' ') => {
|
KeyCode::Char(' ') => {
|
||||||
if self.selection.contains(&self.cursor.0) {
|
if self.selection.contains(&cursor.row) {
|
||||||
self.selection.remove(&self.cursor.0);
|
self.selection.remove(&cursor.row);
|
||||||
} else {
|
} else {
|
||||||
self.selection.insert(self.cursor.0);
|
self.selection.insert(cursor.row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +161,7 @@ impl App {
|
|||||||
Screen::Exiting => {
|
Screen::Exiting => {
|
||||||
match k.code {
|
match k.code {
|
||||||
KeyCode::Enter | KeyCode::Char('y') | KeyCode::Char('q') => return Ok(true),
|
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 {
|
impl Screen {
|
||||||
fn render (&self, app: &App, area: Rect, buf: &mut Buffer) -> () {
|
fn render (&self, app: &App, area: Rect, buf: &mut Buffer) -> () {
|
||||||
match self {
|
match self {
|
||||||
Self::Main(ms) => main::screen(app, *ms, area, buf),
|
Self::Main { inner, cursor } => main::screen(app, *inner, *cursor, area, buf),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
137
src/app/main.rs
137
src/app/main.rs
@ -1,4 +1,4 @@
|
|||||||
use crate::prelude::*;
|
use crate::{app::{Cursor, Screen}, prelude::*};
|
||||||
use super::MainScreen;
|
use super::MainScreen;
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
@ -12,7 +12,7 @@ use utils::truncate_with_ellipsis as tc;
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use strum::IntoEnumIterator;
|
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()
|
let layout = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints(vec![
|
.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)))
|
.intersperse(Span::styled(" | ", Style::default().fg(Color::White)))
|
||||||
.collect_vec()
|
.collect_vec()
|
||||||
).centered()).block(Block::default().borders(Borders::BOTTOM).padding(Padding::top(1))).render(layout[0], buf);
|
).centered()).block(Block::default().borders(Borders::BOTTOM).padding(Padding::top(1))).render(layout[0], buf);
|
||||||
let mut list_items = Vec::<ListItem>::new();
|
let mut list_items = Vec::<ListItem>::new();
|
||||||
|
|
||||||
|
let w = area.width;
|
||||||
|
|
||||||
let w = area.width;
|
macro_rules! wx {
|
||||||
|
($i: literal) => {
|
||||||
macro_rules! wx {
|
(w / 100).max(1) as usize * $i
|
||||||
($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<Span> = 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()
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let tr = |row: usize| return move |col: usize| td(row, col);
|
let cw: [(usize, &'static str, super::td::TdFn); 6] = [
|
||||||
|
(wx!(4), "#", super::td::id),
|
||||||
list_items.push(ListItem::new(Line::from(header)));
|
(wx!(15), "Title", super::td::name),
|
||||||
list_items.push(ListItem::new(Line::from(Span::from("-".repeat(w as usize)))));
|
(wx!(40), "Description", super::td::description),
|
||||||
|
(wx!(15), "Expires", super::td::expiry),
|
||||||
for s @ (idx, _task) in iterator().enumerate() {
|
(wx!(10), "Status", super::td::status),
|
||||||
let idx = idx % item_count;
|
(wx!(10), "Kind", super::td::kind)
|
||||||
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 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 th = |col: usize| if col == cursor.col % cw.len() { Style::default().bold().bg(Color::from_u32(0x253325)) } else { Style::default() };
|
||||||
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);
|
let delimiter = || Span::styled("|", Style::default());
|
||||||
}
|
|
||||||
|
let header: Vec<Span> = 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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user