summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormo8it <mo8it@proton.me>2024-04-07 16:33:00 +0200
committermo8it <mo8it@proton.me>2024-04-07 16:33:00 +0200
commitd988054ad851cb6ce67c77e2607322142d188804 (patch)
tree206455826f76556506fc9214b8bc1eb2fca01b2c
parent2db86833a9f3fae4dc5410aac828b3071dda1984 (diff)
Add UiState
-rw-r--r--src/list.rs242
1 files changed, 137 insertions, 105 deletions
diff --git a/src/list.rs b/src/list.rs
index 5153e01..dad2182 100644
--- a/src/list.rs
+++ b/src/list.rs
@@ -10,112 +10,156 @@ use ratatui::{
style::{Style, Stylize},
text::Span,
widgets::{Block, Borders, HighlightSpacing, Row, Table, TableState},
- Terminal,
+ Frame, Terminal,
};
use std::io;
use crate::{exercise::Exercise, state::State};
-fn rows<'s, 'e, 'i>(
- state: &'s State,
- exercises: &'e [Exercise],
-) -> impl Iterator<Item = Row<'e>> + 'i
-where
- 's: 'i,
- 'e: 'i,
-{
- exercises
- .iter()
- .zip(state.progress())
- .enumerate()
- .map(|(ind, (exercise, done))| {
- let next = if ind == state.next_exercise_ind() {
- ">>>>".bold().red()
- } else {
- Span::default()
- };
-
- let exercise_state = if *done {
- "DONE".green()
- } else {
- "PENDING".yellow()
- };
-
- Row::new([
- next,
- exercise_state,
- Span::raw(&exercise.name),
- Span::raw(exercise.path.to_string_lossy()),
- ])
- })
+struct UiState<'a> {
+ pub table: Table<'a>,
+ selected: usize,
+ table_state: TableState,
+ last_ind: usize,
}
-fn table<'a>(state: &State, exercises: &'a [Exercise]) -> Table<'a> {
- let header = Row::new(["Next", "State", "Name", "Path"]);
-
- let max_name_len = exercises
- .iter()
- .map(|exercise| exercise.name.len())
- .max()
- .unwrap_or(4) as u16;
-
- let widths = [
- Constraint::Length(4),
- Constraint::Length(7),
- Constraint::Length(max_name_len),
- Constraint::Fill(1),
- ];
-
- Table::new(rows(state, exercises), widths)
- .header(header)
- .column_spacing(2)
- .highlight_spacing(HighlightSpacing::Always)
- .highlight_style(Style::new().bg(ratatui::style::Color::Rgb(50, 50, 50)))
- .highlight_symbol("🦀")
- .block(Block::default().borders(Borders::BOTTOM))
+impl<'a> UiState<'a> {
+ pub fn rows<'s, 'i>(
+ state: &'s State,
+ exercises: &'a [Exercise],
+ ) -> impl Iterator<Item = Row<'a>> + 'i
+ where
+ 's: 'i,
+ 'a: 'i,
+ {
+ exercises
+ .iter()
+ .zip(state.progress())
+ .enumerate()
+ .map(|(ind, (exercise, done))| {
+ let next = if ind == state.next_exercise_ind() {
+ ">>>>".bold().red()
+ } else {
+ Span::default()
+ };
+
+ let exercise_state = if *done {
+ "DONE".green()
+ } else {
+ "PENDING".yellow()
+ };
+
+ Row::new([
+ next,
+ exercise_state,
+ Span::raw(&exercise.name),
+ Span::raw(exercise.path.to_string_lossy()),
+ ])
+ })
+ }
+
+ pub fn new(state: &State, exercises: &'a [Exercise]) -> Self {
+ let header = Row::new(["Next", "State", "Name", "Path"]);
+
+ let max_name_len = exercises
+ .iter()
+ .map(|exercise| exercise.name.len())
+ .max()
+ .unwrap_or(4) as u16;
+
+ let widths = [
+ Constraint::Length(4),
+ Constraint::Length(7),
+ Constraint::Length(max_name_len),
+ Constraint::Fill(1),
+ ];
+
+ let rows = Self::rows(state, exercises);
+
+ let table = Table::new(rows, widths)
+ .header(header)
+ .column_spacing(2)
+ .highlight_spacing(HighlightSpacing::Always)
+ .highlight_style(Style::new().bg(ratatui::style::Color::Rgb(50, 50, 50)))
+ .highlight_symbol("🦀")
+ .block(Block::default().borders(Borders::BOTTOM));
+
+ let selected = 0;
+ let table_state = TableState::default().with_selected(Some(selected));
+ let last_ind = exercises.len() - 1;
+
+ Self {
+ table,
+ selected,
+ table_state,
+ last_ind,
+ }
+ }
+
+ fn select(&mut self, ind: usize) {
+ self.selected = ind;
+ self.table_state.select(Some(ind));
+ }
+
+ pub fn select_next(&mut self) {
+ self.select(self.selected.saturating_add(1).min(self.last_ind));
+ }
+
+ pub fn select_previous(&mut self) {
+ self.select(self.selected.saturating_sub(1));
+ }
+
+ #[inline]
+ pub fn select_first(&mut self) {
+ self.select(0);
+ }
+
+ #[inline]
+ pub fn select_last(&mut self) {
+ self.select(self.last_ind);
+ }
+
+ pub fn draw(&mut self, frame: &mut Frame) {
+ let area = frame.size();
+
+ frame.render_stateful_widget(
+ &self.table,
+ Rect {
+ x: 0,
+ y: 0,
+ width: area.width,
+ height: area.height - 1,
+ },
+ &mut self.table_state,
+ );
+
+ // Help footer
+ let footer =
+ "↓/j ↑/k home/g end/G │ Filter <d>one/<p>ending │ <r>eset │ <c>ontinue at │ <q>uit";
+ frame.render_widget(
+ Span::raw(footer),
+ Rect {
+ x: 0,
+ y: area.height - 1,
+ width: area.width,
+ height: 1,
+ },
+ );
+ }
}
pub fn list(state: &mut State, exercises: &[Exercise]) -> Result<()> {
let mut stdout = io::stdout().lock();
-
stdout.execute(EnterAlternateScreen)?;
enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?;
terminal.clear()?;
- let mut table = table(state, exercises);
-
- let last_ind = exercises.len() - 1;
- let mut selected = 0;
- let mut table_state = TableState::default().with_selected(Some(selected));
+ let mut ui_state = UiState::new(state, exercises);
'outer: loop {
- terminal.draw(|frame| {
- let area = frame.size();
-
- frame.render_stateful_widget(
- &table,
- Rect {
- x: 0,
- y: 0,
- width: area.width,
- height: area.height - 1,
- },
- &mut table_state,
- );
-
- // Help footer
- frame.render_widget(
- Span::raw("↓/j ↑/k home/g end/G │ Filter <d>one/<p>ending │ <r>eset │ <c>ontinue at │ <q>uit"),
- Rect {
- x: 0,
- y: area.height - 1,
- width: area.width,
- height: 1,
- },
- );
- })?;
+ terminal.draw(|frame| ui_state.draw(frame))?;
let key = loop {
match event::read()? {
@@ -135,25 +179,13 @@ pub fn list(state: &mut State, exercises: &[Exercise]) -> Result<()> {
match key.code {
KeyCode::Char('q') => break,
- KeyCode::Down | KeyCode::Char('j') => {
- selected = selected.saturating_add(1).min(last_ind);
- table_state.select(Some(selected));
- }
- KeyCode::Up | KeyCode::Char('k') => {
- selected = selected.saturating_sub(1).max(0);
- table_state.select(Some(selected));
- }
- KeyCode::Home | KeyCode::Char('g') => {
- selected = 0;
- table_state.select(Some(selected));
- }
- KeyCode::End | KeyCode::Char('G') => {
- selected = last_ind;
- table_state.select(Some(selected));
- }
+ KeyCode::Down | KeyCode::Char('j') => ui_state.select_next(),
+ KeyCode::Up | KeyCode::Char('k') => ui_state.select_previous(),
+ KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(),
+ KeyCode::End | KeyCode::Char('G') => ui_state.select_last(),
KeyCode::Char('c') => {
- state.set_next_exercise_ind(selected)?;
- table = table.rows(rows(state, exercises));
+ state.set_next_exercise_ind(ui_state.selected)?;
+ ui_state.table = ui_state.table.rows(UiState::rows(state, exercises));
}
_ => (),
}