Change columns up. Refactor input and state change code.
This commit is contained in:
parent
77f9c95cf1
commit
c38fa823c9
19
src/input.rs
19
src/input.rs
@ -1,28 +1,19 @@
|
|||||||
use std::cmp::min;
|
|
||||||
use crossterm::event;
|
use crossterm::event;
|
||||||
use crossterm::event::{Event, KeyCode};
|
use crossterm::event::{Event, KeyCode};
|
||||||
use int_enum::IntEnum;
|
use crate::types::{AppState};
|
||||||
use crate::types::{AppState, TaskStatus};
|
|
||||||
|
|
||||||
pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> {
|
pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> {
|
||||||
if let Event::Key(key) = event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Char('q') => state.quit = true,
|
KeyCode::Char('q') => state.quit = true,
|
||||||
KeyCode::Char('h') |
|
KeyCode::Char('h') |
|
||||||
KeyCode::Left => state.selected_column = state.selected_column.saturating_sub(1),
|
KeyCode::Left => state.select_previous_column(),
|
||||||
KeyCode::Char('j') |
|
KeyCode::Char('j') |
|
||||||
KeyCode::Down => {
|
KeyCode::Down => state.select_next_task(),
|
||||||
let column: TaskStatus = TaskStatus::from_int(state.selected_column).unwrap();
|
|
||||||
let tasks = state.current_project.tasks_per_column.get(&column).unwrap();
|
|
||||||
if tasks.len() > 0 {
|
|
||||||
let mins = min(state.selected_task[state.selected_column] + 1, tasks.len() - 1);
|
|
||||||
state.selected_task[state.selected_column] = mins;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
KeyCode::Char('k') |
|
KeyCode::Char('k') |
|
||||||
KeyCode::Up => state.selected_task[state.selected_column] = state.selected_task[state.selected_column].saturating_sub(1),
|
KeyCode::Up => state.select_previous_task(),
|
||||||
KeyCode::Char('l') |
|
KeyCode::Char('l') |
|
||||||
KeyCode::Right => state.selected_column = min(state.selected_column + 1, 4),
|
KeyCode::Right => state.select_next_column(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
83
src/types.rs
83
src/types.rs
@ -1,3 +1,4 @@
|
|||||||
|
use std::cmp::min;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use int_enum::IntEnum;
|
use int_enum::IntEnum;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -5,13 +6,13 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[repr(usize)]
|
#[repr(usize)]
|
||||||
#[derive(Deserialize, Serialize, Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, IntEnum)]
|
#[derive(Deserialize, Serialize, Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, IntEnum)]
|
||||||
pub enum TaskStatus {
|
pub enum TaskStatus {
|
||||||
Done = 0,
|
Todo = 0,
|
||||||
Todo = 1,
|
InProgress = 1,
|
||||||
InProgress = 2,
|
Done = 2,
|
||||||
Testing = 3,
|
Ideas = 3,
|
||||||
Backlog = 4,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #[derive(Deserialize, Serialize, Debug, Clone, Copy)]
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
@ -27,6 +28,7 @@ impl Default for Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type used mainly for serialization at this time
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct Project {
|
pub struct Project {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -41,15 +43,30 @@ impl Project {
|
|||||||
[(TaskStatus::Done, vec![]),
|
[(TaskStatus::Done, vec![]),
|
||||||
(TaskStatus::Todo, vec![]),
|
(TaskStatus::Todo, vec![]),
|
||||||
(TaskStatus::InProgress, vec![]),
|
(TaskStatus::InProgress, vec![]),
|
||||||
(TaskStatus::Testing, vec![]),
|
(TaskStatus::Ideas, vec![])],
|
||||||
(TaskStatus::Backlog, vec![])],
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_task(&mut self, status: TaskStatus, task: Task) {
|
pub fn load() -> Self {
|
||||||
|
let json = std::fs::read_to_string("kanban-tui.json")
|
||||||
|
.expect("Could not read json file");
|
||||||
|
serde_json::from_str(&json)
|
||||||
|
.expect("There is something wrong with the json schema, it doesn't match Project struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_task(&mut self, status: TaskStatus, task: Task) {
|
||||||
self.tasks_per_column.entry(status).or_default().push(task);
|
self.tasks_per_column.entry(status).or_default().push(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Comment out cause this is dangerous
|
||||||
|
pub fn save() {
|
||||||
|
// let mut project = Project::new("Kanban Tui");
|
||||||
|
// project.add_task(Task::default());
|
||||||
|
// project.add_task(Task::default());
|
||||||
|
// let json = serde_json::to_string_pretty(&project).unwrap();
|
||||||
|
// std::fs::write("./project.json", json).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Project {
|
impl Default for Project {
|
||||||
@ -61,27 +78,10 @@ impl Default for Project {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Project {
|
|
||||||
pub fn load() -> Self {
|
|
||||||
let json = std::fs::read_to_string("kanban-tui.json")
|
|
||||||
.expect("Could not read json file");
|
|
||||||
serde_json::from_str(&json)
|
|
||||||
.expect("There is something wrong with the json schema, it doesn't match Project struct")
|
|
||||||
}
|
|
||||||
/// Comment out cause this is dangerous
|
|
||||||
pub fn save() {
|
|
||||||
// let mut project = Project::new("Kanban Tui");
|
|
||||||
// project.add_task(Task::default());
|
|
||||||
// project.add_task(Task::default());
|
|
||||||
// let json = serde_json::to_string_pretty(&project).unwrap();
|
|
||||||
// std::fs::write("./project.json", json).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub selected_column: usize,
|
pub selected_column: usize,
|
||||||
pub selected_task: [usize; 5],
|
pub selected_task: [usize; 4],
|
||||||
pub current_project: Project,
|
pub project: Project,
|
||||||
pub quit: bool,
|
pub quit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,10 +89,35 @@ impl AppState {
|
|||||||
pub fn new(project: Project) -> Self {
|
pub fn new(project: Project) -> Self {
|
||||||
AppState {
|
AppState {
|
||||||
selected_column: 0,
|
selected_column: 0,
|
||||||
selected_task: [0, 0, 0, 0, 0],
|
selected_task: [0, 0, 0, 0],
|
||||||
quit: false,
|
quit: false,
|
||||||
current_project: project,
|
project: project,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_tasks_in_active_column(&self) -> &Vec<Task> {
|
||||||
|
let column: TaskStatus = TaskStatus::from_int(self.selected_column).unwrap().clone();
|
||||||
|
self.project.tasks_per_column.get(&column).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_previous_task(&mut self) {
|
||||||
|
self.selected_task[self.selected_column] = self.selected_task[self.selected_column].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[self.selected_column] + 1, tasks.len() - 1);
|
||||||
|
self.selected_task[self.selected_column] = mins;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_previous_column(&mut self) {
|
||||||
|
self.selected_column = self.selected_column.saturating_sub(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_next_column(&mut self) {
|
||||||
|
self.selected_column = min(self.selected_column + 1, self.project.tasks_per_column.len() - 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,12 +11,12 @@ fn draw_tasks<B: Backend>(f: &mut Frame<B>, area: &Rect, state: &AppState) {
|
|||||||
let columns = Layout::default()
|
let columns = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints(
|
.constraints(
|
||||||
vec![Constraint::Percentage(20);
|
vec![Constraint::Percentage(100 / state.project.tasks_per_column.len() as u16);
|
||||||
state.current_project.tasks_per_column.len()].as_ref()
|
state.project.tasks_per_column.len()].as_ref()
|
||||||
)
|
)
|
||||||
.split(*area);
|
.split(*area);
|
||||||
|
|
||||||
for (i, (status, tasks)) in state.current_project.tasks_per_column.iter().enumerate() {
|
for (i, (status, tasks)) in state.project.tasks_per_column.iter().enumerate() {
|
||||||
let items: Vec<ListItem> = tasks.iter().enumerate().map(|(j, task)| {
|
let items: Vec<ListItem> = tasks.iter().enumerate().map(|(j, task)| {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
if i == state.selected_column && j == state.selected_task[state.selected_column] {
|
if i == state.selected_column && j == state.selected_task[state.selected_column] {
|
||||||
@ -48,7 +48,7 @@ fn draw_task_info<B: Backend>(f: &mut Frame<B>, area: &Rect, state: &AppState) {
|
|||||||
.title("TASK INFO")
|
.title("TASK INFO")
|
||||||
.borders(Borders::ALL);
|
.borders(Borders::ALL);
|
||||||
let column: TaskStatus = TaskStatus::from_int(state.selected_column).unwrap();
|
let column: TaskStatus = TaskStatus::from_int(state.selected_column).unwrap();
|
||||||
let tasks = state.current_project.tasks_per_column.get(&column).unwrap();
|
let tasks = state.project.tasks_per_column.get(&column).unwrap();
|
||||||
if tasks.len() > 0 {
|
if tasks.len() > 0 {
|
||||||
let task: &Task = &tasks[state.selected_task[state.selected_column]];
|
let task: &Task = &tasks[state.selected_task[state.selected_column]];
|
||||||
let p = Paragraph::new(task.description.as_str()).block(block).wrap(Wrap { trim: true });
|
let p = Paragraph::new(task.description.as_str()).block(block).wrap(Wrap { trim: true });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user