Compare commits

..

No commits in common. "bef08361849443929c70b73dc7788e1785078c32" and "fc237b9f2924fe2538954b0f55aa0e78947b5185" have entirely different histories.

4 changed files with 119 additions and 129 deletions

View File

@ -57,27 +57,25 @@ impl Default for Project {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum TaskEditFocus { pub enum NewTaskFocus {
Title, Title,
Description, Description,
CreateBtn, CreateBtn,
CancelBtn, CancelBtn
} }
pub struct TaskState<'a> { pub struct NewTask<'a> {
pub title: TextArea<'a>, pub title: TextArea<'a>,
pub description: TextArea<'a>, pub description: TextArea<'a>,
pub focus: TaskEditFocus, pub focus: NewTaskFocus
pub is_edit: bool
} }
impl Default for TaskState<'_> { impl Default for NewTask<'_> {
fn default() -> Self { fn default() -> Self {
TaskState { NewTask {
title: TextArea::default(), title: TextArea::default(),
description: TextArea::default(), description: TextArea::default(),
focus: TaskEditFocus::Title, focus: NewTaskFocus::Title
is_edit: false
} }
} }
} }
@ -86,14 +84,14 @@ pub struct AppState<'a> {
pub project: Project, pub project: Project,
pub quit: bool, pub quit: bool,
pub columns: Vec<Column>, pub columns: Vec<Column>,
pub task_edit_state: Option<TaskState<'a>>, pub new_task_state: Option<NewTask<'a>>,
} }
impl AppState<'_> { impl AppState<'_> {
pub fn new(project: Project) -> Self { pub fn new(project: Project) -> Self {
AppState { AppState {
quit: false, quit: false,
task_edit_state: None, new_task_state: None,
project, project,
columns: vec![], columns: vec![],
} }
@ -142,16 +140,6 @@ impl Column {
self.selected_task_idx = self.tasks.len() - 1; self.selected_task_idx = self.tasks.len() - 1;
} }
pub fn get_task_state_from_curr_selected(&self) -> Option<TaskState> {
self.get_selected_task().map(|t| {
TaskState {
title: TextArea::from(t.title.chars()),
description: TextArea::from(t.description.chars()),
focus: TaskEditFocus::Title,
is_edit: true
}
})
}
} }
impl Project { impl Project {

View File

@ -1,7 +1,7 @@
#![allow(unused_imports)] #![allow(unused_imports)]
use crossterm::event; use crossterm::event;
use crossterm::event::{Event, KeyCode}; use crossterm::event::{Event, KeyCode};
use crate::app::{TaskState, AppState, TaskEditFocus}; use crate::app::{NewTask, AppState, NewTaskFocus};
use std::io::{stdout, Write}; use std::io::{stdout, Write};
use tui_textarea::TextArea; use tui_textarea::TextArea;
@ -9,45 +9,45 @@ pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> {
let project = &mut state.project; let project = &mut state.project;
let column = project.get_selected_column_mut(); let column = project.get_selected_column_mut();
if let Event::Key(key) = event::read()? { if let Event::Key(key) = event::read()? {
match &mut state.task_edit_state { match &mut state.new_task_state {
Some(task) => { Some(task) => {
// TODO: Extract this code to a separate function to avoid nesting // TODO: Extract this code to a separate function to avoid nesting
match task.focus { match task.focus {
// TODO: Handle wrapping around the enum rather than doing it manually // TODO: Handle wrapping around the enum rather than doing it manually
TaskEditFocus::Title => { NewTaskFocus::Title => {
match key.code { match key.code {
KeyCode::Tab => task.focus = TaskEditFocus::Description, KeyCode::Tab => task.focus = NewTaskFocus::Description,
KeyCode::BackTab => task.focus = TaskEditFocus::CancelBtn, KeyCode::BackTab => task.focus = NewTaskFocus::CancelBtn,
KeyCode::Enter => (), KeyCode::Enter => (),
_ => { task.title.input(key); } _ => { task.title.input(key); }
} }
} }
TaskEditFocus::Description => { NewTaskFocus::Description => {
match key.code { match key.code {
KeyCode::Tab => task.focus = TaskEditFocus::CreateBtn, KeyCode::Tab => task.focus = NewTaskFocus::CreateBtn,
KeyCode::BackTab => task.focus = TaskEditFocus::Title, KeyCode::BackTab => task.focus = NewTaskFocus::Title,
_ => { task.description.input(key); } _ => { task.description.input(key); }
} }
} }
TaskEditFocus::CreateBtn => { NewTaskFocus::CreateBtn => {
match key.code { match key.code {
KeyCode::Tab => task.focus = TaskEditFocus::CancelBtn, KeyCode::Tab => task.focus = NewTaskFocus::CancelBtn,
KeyCode::BackTab => task.focus = TaskEditFocus::Description, KeyCode::BackTab => task.focus = NewTaskFocus::Description,
KeyCode::Enter => { KeyCode::Enter => {
let title = task.title.clone().into_lines().join("\n"); let title = task.title.clone().into_lines().join("\n");
let description = task.description.clone().into_lines().clone().join("\n"); let description = task.description.clone().into_lines().clone().join("\n");
column.add_task(title, description); column.add_task(title, description);
state.task_edit_state = None state.new_task_state = None
} }
_ => (), _ => (),
} }
} }
TaskEditFocus::CancelBtn => { NewTaskFocus::CancelBtn => {
match key.code { match key.code {
KeyCode::Tab => task.focus = TaskEditFocus::Title, KeyCode::Tab => task.focus = NewTaskFocus::Title,
KeyCode::BackTab => task.focus = TaskEditFocus::CreateBtn, KeyCode::BackTab => task.focus = NewTaskFocus::CreateBtn,
KeyCode::Enter => { KeyCode::Enter => {
state.task_edit_state = None state.new_task_state = None
} }
_ => (), _ => (),
} }
@ -73,10 +73,11 @@ pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> {
KeyCode::Char('J') => project.move_task_down(), KeyCode::Char('J') => project.move_task_down(),
KeyCode::Char('-') | KeyCode::Char('-') |
KeyCode::Char('K') => project.move_task_up(), KeyCode::Char('K') => project.move_task_up(),
KeyCode::Char('n') => state.task_edit_state = Some(TaskState::default()), KeyCode::Char('p') => {
KeyCode::Char('e') => { match state.new_task_state {
let task = column.get_task_state_from_curr_selected(); None => state.new_task_state = Some(NewTask::default()),
state.task_edit_state = task; Some(_) => state.new_task_state = None,
}
} }
KeyCode::Char('D') => column.remove_task(), KeyCode::Char('D') => column.remove_task(),
_ => {} _ => {}
@ -86,4 +87,3 @@ pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> {
} }
Ok(()) Ok(())
} }

View File

@ -1,4 +1,3 @@
#![deny(rust_2018_idioms)]
#![allow(unused_imports)] #![allow(unused_imports)]
#![allow(dead_code)] #![allow(dead_code)]
use kanban_tui::*; use kanban_tui::*;

View File

@ -89,23 +89,25 @@ fn centered_rect_for_popup(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
.split(popup_layout[1])[1] .split(popup_layout[1])[1]
} }
pub fn draw_task_popup<B: Backend>(f: &mut Frame<B>, state: &mut AppState, popup_title: &str) { pub fn draw_new_task_popup<B: Backend>(f: &mut Frame<B>, state: &mut AppState) {
let area = centered_rect_for_popup(45, 60, f.size()); let area = centered_rect_for_popup(45, 60, f.size());
f.render_widget(Clear, area);
match &mut state.new_task_state {
None => {}
Some(task) => {
let block = Block::default() let block = Block::default()
.title(popup_title) .title("Add Task")
.title_alignment(Alignment::Center) .title_alignment(Alignment::Center)
.borders(Borders::ALL); .borders(Borders::ALL);
f.render_widget(Clear, area);
f.render_widget(Paragraph::new("").block(block.clone()), area);
if let Some(task) = &mut state.task_edit_state {
let block_inner = block.inner(area); let block_inner = block.inner(area);
// let main = Paragraph::new(task.description.clone()).block(block);
let layout = Layout::default() let layout = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints( .constraints(
[ [
Constraint::Length(3), Constraint::Length(3),
Constraint::Max(100), Constraint::Max(100),
Constraint::Length(1), Constraint::Length(3),
Constraint::Length(2), Constraint::Length(2),
] ]
.as_ref(), .as_ref(),
@ -129,22 +131,22 @@ pub fn draw_task_popup<B: Backend>(f: &mut Frame<B>, state: &mut AppState, popup
let cancel_style; let cancel_style;
let cancel_txt; let cancel_txt;
match task.focus { match task.focus {
TaskEditFocus::CreateBtn => { NewTaskFocus::CreateBtn => {
create_style = Style::default().fg(Color::Yellow); create_style = Style::default().fg(Color::Yellow);
cancel_style = Style::default(); cancel_style = Style::default();
create_txt = "[Confirm]"; create_txt = "[Create]";
cancel_txt = " Cancel "; cancel_txt = " Cancel ";
} }
TaskEditFocus::CancelBtn => { NewTaskFocus::CancelBtn => {
create_style = Style::default(); create_style = Style::default();
cancel_style = Style::default().fg(Color::Yellow); cancel_style = Style::default().fg(Color::Yellow);
create_txt = " Confirm "; create_txt = " Create ";
cancel_txt = "[Cancel]"; cancel_txt = "[Cancel]";
} }
_ => { _ => {
create_style = Style::default(); create_style = Style::default();
cancel_style = Style::default(); cancel_style = Style::default();
create_txt = " Confirm "; create_txt = " Create ";
cancel_txt = " Cancel "; cancel_txt = " Cancel ";
} }
} }
@ -160,7 +162,7 @@ pub fn draw_task_popup<B: Backend>(f: &mut Frame<B>, state: &mut AppState, popup
task.description.set_cursor_line_style(Style::default()); task.description.set_cursor_line_style(Style::default());
task.title.set_block(b1); task.title.set_block(b1);
if let TaskEditFocus::Title = task.focus { if let NewTaskFocus::Title = task.focus {
task.title.set_style(Style::default().fg(Color::Yellow)); task.title.set_style(Style::default().fg(Color::Yellow));
task.title.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED)); task.title.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
} else { } else {
@ -170,7 +172,7 @@ pub fn draw_task_popup<B: Backend>(f: &mut Frame<B>, state: &mut AppState, popup
f.render_widget(task.title.widget(), layout[0]); f.render_widget(task.title.widget(), layout[0]);
task.description.set_block(b2); task.description.set_block(b2);
if let TaskEditFocus::Description = task.focus { if let NewTaskFocus::Description = task.focus {
task.description.set_style(Style::default().fg(Color::Yellow)); task.description.set_style(Style::default().fg(Color::Yellow));
task.description.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED)); task.description.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
} else { } else {
@ -183,6 +185,7 @@ pub fn draw_task_popup<B: Backend>(f: &mut Frame<B>, state: &mut AppState, popup
f.render_widget(footer, layout[3]); f.render_widget(footer, layout[3]);
} }
} }
}
pub fn draw<B: Backend>(f: &mut Frame<B>, state: &mut AppState) { pub fn draw<B: Backend>(f: &mut Frame<B>, state: &mut AppState) {
let main_layout = Layout::default() let main_layout = Layout::default()
@ -211,7 +214,7 @@ pub fn draw<B: Backend>(f: &mut Frame<B>, state: &mut AppState) {
let footer = Paragraph::new(foot_txt).block(block); let footer = Paragraph::new(foot_txt).block(block);
f.render_widget(footer, main_layout[3]); f.render_widget(footer, main_layout[3]);
if state.task_edit_state.is_some() { if state.new_task_state.is_some() {
draw_task_popup(f, state, "Create Task"); draw_new_task_popup(f, state);
} }
} }