diff --git a/src/app.rs b/src/app.rs index 19cd83d..bf39213 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,20 +1,26 @@ -use indexmap::IndexMap; -use int_enum::IntEnum; +// use indexmap::IndexMap; +// use int_enum::IntEnum; use serde::{Deserialize, Serialize}; use std::cmp::min; #[cfg(test)] mod tests; -#[repr(usize)] -#[derive( - Deserialize, Serialize, Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, IntEnum, -)] -pub enum TaskStatus { - Todo = 0, - InProgress = 1, - Done = 2, - Ideas = 3, +#[derive(Debug, Serialize, Deserialize)] +pub struct Column { + pub name: String, + pub selected_task_idx: usize, + pub tasks: Vec, +} + +impl Column { + pub fn get_selected_task(&self) -> Option<&Task> { + self.tasks.get(self.selected_task_idx) + } + + pub fn get_selected_task_mut(&mut self) -> Option<&mut Task> { + self.tasks.get_mut(self.selected_task_idx) + } } // #[derive(Deserialize, Serialize, Debug, Clone, Copy)] @@ -37,7 +43,8 @@ impl Default for Task { #[derive(Deserialize, Serialize, Debug)] pub struct Project { pub name: String, - pub tasks_per_column: IndexMap>, + pub selected_column_idx: usize, + pub columns: Vec } #[derive(Debug, thiserror::Error)] @@ -52,12 +59,8 @@ impl Project { pub fn new(name: &str) -> Self { Project { name: name.to_owned(), - tasks_per_column: IndexMap::from([ - (TaskStatus::Done, vec![]), - (TaskStatus::Todo, vec![]), - (TaskStatus::InProgress, vec![]), - (TaskStatus::Ideas, vec![]), - ]), + columns: vec![], + selected_column_idx: 0, } } @@ -70,137 +73,112 @@ impl Project { Self::load_from_json(&json) } - pub fn add_task(&mut self, status: TaskStatus, task: Task) { - self.tasks_per_column.entry(status).or_default().push(task); - } + // pub fn add_task(&mut self, status: Column, task: Task) { + // self.tasks_per_column.entry(status).or_default().push(task); + // } pub fn save(&self) { - let json = serde_json::to_string_pretty(&self).unwrap(); - std::fs::write("kanban-tui.json", json).unwrap(); + let _json = serde_json::to_string_pretty(&self).unwrap(); + // std::fs::write("kanban-tui.json", json).unwrap(); } + + pub fn get_selected_column(&self) -> &Column { + &self.columns[self.selected_column_idx] + } + + pub fn get_selected_column_mut(&mut self) -> &mut Column { + &mut self.columns[self.selected_column_idx] + } + } impl Default for Project { fn default() -> Self { Project { name: String::new(), - tasks_per_column: IndexMap::new(), + columns: vec![], + selected_column_idx: 0, } } } pub struct AppState { - pub selected_column: usize, - pub selected_task: [usize; 4], pub project: Project, pub quit: bool, + pub columns: Vec, pub popup_text: Option, } impl AppState { pub fn new(project: Project) -> Self { AppState { - selected_column: 0, - selected_task: [0, 0, 0, 0], quit: false, popup_text: None, project, + columns: vec![], } } - fn selected_task_idx(&self) -> usize { - self.selected_task[self.selected_column] - } - - fn selected_task_idx_mut(&mut self) -> &mut usize { - &mut self.selected_task[self.selected_column] - } - - pub fn get_tasks_in_active_column(&self) -> &[Task] { - let column: TaskStatus = TaskStatus::from_int(self.selected_column).unwrap().clone(); - self.project.tasks_per_column.get(&column).unwrap() - } - - pub fn get_tasks_in_active_column_mut(&mut self) -> &mut Vec { - let column: TaskStatus = TaskStatus::from_int(self.selected_column).unwrap().clone(); - self.project.tasks_per_column.get_mut(&column).unwrap() - } - - pub fn get_selected_task(&self) -> Option<&Task> { - let tasks = self.get_tasks_in_active_column(); - tasks.get(self.selected_task_idx()) - } - pub fn select_previous_task(&mut self) { - *self.selected_task_idx_mut() = self.selected_task_idx().saturating_sub(1) + let task_idx = &mut self.project.get_selected_column_mut().selected_task_idx; + *task_idx = task_idx.saturating_sub(1) } pub fn select_next_task(&mut self) { - let tasks = self.get_tasks_in_active_column(); - if tasks.len() > 0 { - let mins = min(self.selected_task_idx() + 1, tasks.len() - 1); - *self.selected_task_idx_mut() = mins; - } + let column = &mut self.project.get_selected_column_mut(); + let task_idx = &mut column.selected_task_idx; + *task_idx = min(*task_idx, column.tasks.len() - 1) } - pub fn select_previous_column(&mut self) { - self.selected_column = self.selected_column.saturating_sub(1); + pub fn select_previous_column(&mut self) -> &Column { + self.project.selected_column_idx = self.project.selected_column_idx.saturating_sub(1); + &self.project.columns[self.project.selected_column_idx] } - pub fn select_next_column(&mut self) { - self.selected_column = min( - self.selected_column + 1, - self.project.tasks_per_column.len() - 1, - ) + pub fn select_next_column(&mut self) -> &Column { + self.project.selected_column_idx = min( + self.project.selected_column_idx + 1, + self.project.columns.len() - 1, + ); + &self.project.columns[self.project.selected_column_idx] } pub fn move_task_previous_column(&mut self) { - let tasks = self.get_tasks_in_active_column(); - let task_idx = self.selected_task_idx(); - if self.selected_column > 0 && tasks.len() > 0 && task_idx.clone() < tasks.len() { - let task = self.get_tasks_in_active_column_mut().remove(task_idx); - *self.selected_task_idx_mut() = self.selected_task_idx().saturating_sub(1); - self.select_previous_column(); - let target_tasks = self.get_tasks_in_active_column_mut(); - target_tasks.push(task); - *self.selected_task_idx_mut() = target_tasks.len() - 1; - self.project.save(); + let column = self.project.get_selected_column_mut(); + if let Some(task) = column.get_selected_task_mut() { + if self.project.selected_column_idx > 0 { + column.tasks.remove(column.selected_task_idx); + self.select_previous_column().tasks.push(*task); + self.project.save(); + } } } pub fn move_task_next_column(&mut self) { - let tasks = self.get_tasks_in_active_column(); - let task_idx = self.selected_task_idx(); - if self.selected_column < self.project.tasks_per_column.len() - && tasks.len() > 0 - && task_idx < tasks.len() - { - let task = self.get_tasks_in_active_column_mut().remove(task_idx); - *self.selected_task_idx_mut() = self.selected_task_idx().saturating_sub(1); - self.select_next_column(); - let target_tasks = self.get_tasks_in_active_column_mut(); - target_tasks.push(task); - *self.selected_task_idx_mut() = target_tasks.len() - 1; - self.project.save(); + let column = self.project.get_selected_column(); + if let Some(task) = column.get_selected_task() { + if self.project.selected_column_idx < self.project.columns.len() { + column.tasks.remove(column.selected_task_idx); + self.select_next_column().tasks.push(*task); + self.project.save(); + } } } pub fn move_task_up(&mut self) { - let task_idx = self.selected_task_idx(); - if task_idx > 0 { - let tasks = self.get_tasks_in_active_column_mut(); - tasks.swap(task_idx, task_idx - 1); - *self.selected_task_idx_mut() = task_idx - 1; + let column = self.project.get_selected_column(); + if column.selected_task_idx > 0 { + column.tasks.swap(column.selected_task_idx, column.selected_task_idx - 1); + column.selected_task_idx = column.selected_task_idx - 1; self.project.save(); } } pub fn move_task_down(&mut self) { - let task_idx = self.selected_task_idx(); - let tasks = self.get_tasks_in_active_column_mut(); - if task_idx < tasks.len() - 1 { - tasks.swap(task_idx, task_idx + 1); - *self.selected_task_idx_mut() = task_idx + 1; + let column = self.project.get_selected_column(); + if column.selected_task_idx < column.tasks.len() - 1 { + column.tasks.swap(column.selected_task_idx, column.selected_task_idx + 1); + column.selected_task_idx = column.selected_task_idx + 1; self.project.save(); } } diff --git a/src/input.rs b/src/input.rs index 66ecbbb..16ae6e0 100644 --- a/src/input.rs +++ b/src/input.rs @@ -7,13 +7,13 @@ pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> { match key.code { KeyCode::Char('q') => state.quit = true, KeyCode::Char('h') | - KeyCode::Left => state.select_previous_column(), + KeyCode::Left => { state.select_previous_column(); }, KeyCode::Char('j') | KeyCode::Down => state.select_next_task(), KeyCode::Char('k') | KeyCode::Up => state.select_previous_task(), KeyCode::Char('l') | - KeyCode::Right => state.select_next_column(), + KeyCode::Right => { state.select_next_column(); }, KeyCode::Char('<') | KeyCode::Char('H') => state.move_task_previous_column(), KeyCode::Char('>') | diff --git a/src/ui.rs b/src/ui.rs index c61847e..86da307 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -11,20 +11,22 @@ fn draw_tasks(f: &mut Frame, area: &Rect, state: &AppState) { .direction(Direction::Horizontal) .constraints( vec![ - Constraint::Percentage(100 / state.project.tasks_per_column.len() as u16); - state.project.tasks_per_column.len() + Constraint::Percentage(100 / state.project.columns.len() as u16); + state.project.columns.len() ] .as_ref(), ) .split(*area); - for (i, (status, tasks)) in state.project.tasks_per_column.iter().enumerate() { - let items: Vec = tasks + for (i, column) in state.project.columns.iter().enumerate() { + let items: Vec = column.tasks .iter() .enumerate() .map(|(j, task)| { let mut style = Style::default(); - if i == state.selected_column && j == state.selected_task[state.selected_column] { + let col_idx = state.project.selected_column_idx; + let task_idx = state.project.get_selected_column().selected_task_idx; + if i == col_idx && j == task_idx { style = style.fg(Color::White).add_modifier(Modifier::BOLD); } else { style = style.fg(Color::White); @@ -35,10 +37,10 @@ fn draw_tasks(f: &mut Frame, area: &Rect, state: &AppState) { }) .collect(); let mut style = Style::default(); - if i == state.selected_column { + if i == state.project.selected_column_idx { style = style.fg(Color::Green); }; - let mut s = Span::raw(format!("{:?}", status)); + let mut s = Span::raw(format!("{:?}", column.name)); s.style = Style::default() .add_modifier(Modifier::BOLD | Modifier::ITALIC | Modifier::UNDERLINED) .fg(Color::White); @@ -50,7 +52,7 @@ fn draw_tasks(f: &mut Frame, area: &Rect, state: &AppState) { fn draw_task_info(f: &mut Frame, area: &Rect, state: &AppState) { let block = Block::default().title("TASK INFO").borders(Borders::ALL); - if let Some(task) = state.get_selected_task() { + if let Some(task) = state.project.get_selected_column().get_selected_task() { let p = Paragraph::new(task.description.as_str()) .block(block) .wrap(Wrap { trim: true });