rustfmt
This commit is contained in:
parent
e3ce612ca3
commit
5d3e69d679
33
src/app.rs
33
src/app.rs
@ -2,7 +2,7 @@
|
||||
// use int_enum::IntEnum;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::min;
|
||||
use std::fs::{File};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use tui_textarea::TextArea;
|
||||
|
||||
@ -29,7 +29,7 @@ pub struct Project {
|
||||
pub name: String,
|
||||
pub filepath: String,
|
||||
pub selected_column_idx: usize,
|
||||
pub columns: Vec<Column>
|
||||
pub columns: Vec<Column>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@ -52,7 +52,7 @@ pub struct TaskState<'a> {
|
||||
pub title: TextArea<'a>,
|
||||
pub description: TextArea<'a>,
|
||||
pub focus: TaskEditFocus,
|
||||
pub is_edit: bool
|
||||
pub is_edit: bool,
|
||||
}
|
||||
|
||||
impl Default for TaskState<'_> {
|
||||
@ -61,7 +61,7 @@ impl Default for TaskState<'_> {
|
||||
title: TextArea::default(),
|
||||
description: TextArea::default(),
|
||||
focus: TaskEditFocus::Title,
|
||||
is_edit: false
|
||||
is_edit: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -135,13 +135,11 @@ impl<'a> Column {
|
||||
|
||||
#[must_use]
|
||||
pub fn get_task_state_from_curr_selected_task(&self) -> Option<TaskState<'a>> {
|
||||
self.get_selected_task().map(|t| {
|
||||
TaskState {
|
||||
title: TextArea::from(t.title.lines()),
|
||||
description: TextArea::from(t.description.lines()),
|
||||
focus: TaskEditFocus::Title,
|
||||
is_edit: true
|
||||
}
|
||||
self.get_selected_task().map(|t| TaskState {
|
||||
title: TextArea::from(t.title.lines()),
|
||||
description: TextArea::from(t.description.lines()),
|
||||
focus: TaskEditFocus::Title,
|
||||
is_edit: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -203,10 +201,7 @@ impl Project {
|
||||
}
|
||||
|
||||
pub fn select_next_column(&mut self) -> &Column {
|
||||
self.selected_column_idx = min(
|
||||
self.selected_column_idx + 1,
|
||||
self.columns.len() - 1,
|
||||
);
|
||||
self.selected_column_idx = min(self.selected_column_idx + 1, self.columns.len() - 1);
|
||||
&self.columns[self.selected_column_idx]
|
||||
}
|
||||
|
||||
@ -245,7 +240,9 @@ impl Project {
|
||||
pub fn move_task_up(&mut self) {
|
||||
let column = self.get_selected_column_mut();
|
||||
if column.selected_task_idx > 0 {
|
||||
column.tasks.swap(column.selected_task_idx, column.selected_task_idx - 1);
|
||||
column
|
||||
.tasks
|
||||
.swap(column.selected_task_idx, column.selected_task_idx - 1);
|
||||
column.selected_task_idx -= 1;
|
||||
self.save();
|
||||
}
|
||||
@ -254,7 +251,9 @@ impl Project {
|
||||
pub fn move_task_down(&mut self) {
|
||||
let column = self.get_selected_column_mut();
|
||||
if column.selected_task_idx < column.tasks.len() - 1 {
|
||||
column.tasks.swap(column.selected_task_idx, column.selected_task_idx + 1);
|
||||
column
|
||||
.tasks
|
||||
.swap(column.selected_task_idx, column.selected_task_idx + 1);
|
||||
column.selected_task_idx += 1;
|
||||
self.save();
|
||||
}
|
||||
|
129
src/input.rs
129
src/input.rs
@ -1,6 +1,6 @@
|
||||
use crate::app::{State, TaskEditFocus, TaskState};
|
||||
use crossterm::event;
|
||||
use crossterm::event::{Event, KeyCode};
|
||||
use crate::app::{TaskState, State, TaskEditFocus};
|
||||
|
||||
/// # Errors
|
||||
///
|
||||
@ -14,81 +14,76 @@ pub fn handle(state: &mut State<'_>) -> Result<(), std::io::Error> {
|
||||
// TODO: Extract this code to a separate function to avoid nesting
|
||||
match task.focus {
|
||||
// TODO: Handle wrapping around the enum rather than doing it manually
|
||||
TaskEditFocus::Title => {
|
||||
match key.code {
|
||||
KeyCode::Tab => task.focus = TaskEditFocus::Description,
|
||||
KeyCode::BackTab => task.focus = TaskEditFocus::CancelBtn,
|
||||
KeyCode::Enter => (),
|
||||
_ => { task.title.input(key); }
|
||||
TaskEditFocus::Title => match key.code {
|
||||
KeyCode::Tab => task.focus = TaskEditFocus::Description,
|
||||
KeyCode::BackTab => task.focus = TaskEditFocus::CancelBtn,
|
||||
KeyCode::Enter => (),
|
||||
_ => {
|
||||
task.title.input(key);
|
||||
}
|
||||
}
|
||||
TaskEditFocus::Description => {
|
||||
match key.code {
|
||||
KeyCode::Tab => task.focus = TaskEditFocus::ConfirmBtn,
|
||||
KeyCode::BackTab => task.focus = TaskEditFocus::Title,
|
||||
_ => { task.description.input(key); }
|
||||
},
|
||||
TaskEditFocus::Description => match key.code {
|
||||
KeyCode::Tab => task.focus = TaskEditFocus::ConfirmBtn,
|
||||
KeyCode::BackTab => task.focus = TaskEditFocus::Title,
|
||||
_ => {
|
||||
task.description.input(key);
|
||||
}
|
||||
}
|
||||
TaskEditFocus::ConfirmBtn => {
|
||||
match key.code {
|
||||
KeyCode::Tab => task.focus = TaskEditFocus::CancelBtn,
|
||||
KeyCode::BackTab => task.focus = TaskEditFocus::Description,
|
||||
KeyCode::Enter => {
|
||||
let title = task.title.clone().into_lines().join("\n");
|
||||
let description = task.description.clone().into_lines().join("\n");
|
||||
if task.is_edit {
|
||||
if let Some(selected_task) = column.get_selected_task_mut() {
|
||||
selected_task.title = title;
|
||||
selected_task.description = description;
|
||||
}
|
||||
} else {
|
||||
column.add_task(title, description);
|
||||
},
|
||||
TaskEditFocus::ConfirmBtn => match key.code {
|
||||
KeyCode::Tab => task.focus = TaskEditFocus::CancelBtn,
|
||||
KeyCode::BackTab => task.focus = TaskEditFocus::Description,
|
||||
KeyCode::Enter => {
|
||||
let title = task.title.clone().into_lines().join("\n");
|
||||
let description = task.description.clone().into_lines().join("\n");
|
||||
if task.is_edit {
|
||||
if let Some(selected_task) = column.get_selected_task_mut() {
|
||||
selected_task.title = title;
|
||||
selected_task.description = description;
|
||||
}
|
||||
state.task_edit_state = None;
|
||||
project.save();
|
||||
} else {
|
||||
column.add_task(title, description);
|
||||
}
|
||||
_ => (),
|
||||
state.task_edit_state = None;
|
||||
project.save();
|
||||
}
|
||||
}
|
||||
TaskEditFocus::CancelBtn => {
|
||||
match key.code {
|
||||
KeyCode::Tab => task.focus = TaskEditFocus::Title,
|
||||
KeyCode::BackTab => task.focus = TaskEditFocus::ConfirmBtn,
|
||||
KeyCode::Enter => {
|
||||
state.task_edit_state = None;
|
||||
}
|
||||
_ => (),
|
||||
_ => (),
|
||||
},
|
||||
TaskEditFocus::CancelBtn => match key.code {
|
||||
KeyCode::Tab => task.focus = TaskEditFocus::Title,
|
||||
KeyCode::BackTab => task.focus = TaskEditFocus::ConfirmBtn,
|
||||
KeyCode::Enter => {
|
||||
state.task_edit_state = None;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
};
|
||||
}
|
||||
None => {
|
||||
match key.code {
|
||||
KeyCode::Char('q') => state.quit = true,
|
||||
KeyCode::Char('h') |
|
||||
KeyCode::Left => { project.select_previous_column(); },
|
||||
KeyCode::Char('j') |
|
||||
KeyCode::Down => column.select_next_task(),
|
||||
KeyCode::Char('k') |
|
||||
KeyCode::Up => column.select_previous_task(),
|
||||
KeyCode::Char('l') |
|
||||
KeyCode::Right => { project.select_next_column(); },
|
||||
KeyCode::Char('g') => column.select_first_task(),
|
||||
KeyCode::Char('G') => column.select_last_task(),
|
||||
KeyCode::Char('H') => project.move_task_previous_column(),
|
||||
KeyCode::Char('L') => project.move_task_next_column(),
|
||||
KeyCode::Char('J') => project.move_task_down(),
|
||||
KeyCode::Char('K') => project.move_task_up(),
|
||||
KeyCode::Char('n') => state.task_edit_state = Some(TaskState::default()),
|
||||
KeyCode::Char('e') =>
|
||||
state.task_edit_state = column.get_task_state_from_curr_selected_task(),
|
||||
KeyCode::Char('D') => {
|
||||
column.remove_task();
|
||||
project.save();
|
||||
},
|
||||
_ => {}
|
||||
None => match key.code {
|
||||
KeyCode::Char('q') => state.quit = true,
|
||||
KeyCode::Char('h') | KeyCode::Left => {
|
||||
project.select_previous_column();
|
||||
}
|
||||
}
|
||||
KeyCode::Char('j') | KeyCode::Down => column.select_next_task(),
|
||||
KeyCode::Char('k') | KeyCode::Up => column.select_previous_task(),
|
||||
KeyCode::Char('l') | KeyCode::Right => {
|
||||
project.select_next_column();
|
||||
}
|
||||
KeyCode::Char('g') => column.select_first_task(),
|
||||
KeyCode::Char('G') => column.select_last_task(),
|
||||
KeyCode::Char('H') => project.move_task_previous_column(),
|
||||
KeyCode::Char('L') => project.move_task_next_column(),
|
||||
KeyCode::Char('J') => project.move_task_down(),
|
||||
KeyCode::Char('K') => project.move_task_up(),
|
||||
KeyCode::Char('n') => state.task_edit_state = Some(TaskState::default()),
|
||||
KeyCode::Char('e') => {
|
||||
state.task_edit_state = column.get_task_state_from_curr_selected_task()
|
||||
}
|
||||
KeyCode::Char('D') => {
|
||||
column.remove_task();
|
||||
project.save();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -1,8 +1,8 @@
|
||||
#![deny(rust_2018_idioms)]
|
||||
mod app;
|
||||
mod ui;
|
||||
mod input;
|
||||
mod ui;
|
||||
|
||||
pub use app::*;
|
||||
pub use ui::draw;
|
||||
pub use input::handle;
|
||||
pub use ui::draw;
|
||||
|
98
src/main.rs
98
src/main.rs
@ -1,23 +1,18 @@
|
||||
#![deny(rust_2018_idioms)]
|
||||
use kanban_tui::{Project, State};
|
||||
use clap::{Parser, ValueHint::FilePath};
|
||||
use crossterm::{
|
||||
event::{DisableMouseCapture, EnableMouseCapture},
|
||||
terminal::{
|
||||
disable_raw_mode,
|
||||
enable_raw_mode,
|
||||
EnterAlternateScreen,
|
||||
LeaveAlternateScreen
|
||||
},
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use kanban_tui::{Project, State};
|
||||
use std::{
|
||||
error::Error,
|
||||
fs::{File, OpenOptions},
|
||||
io::{self, Write},
|
||||
path::PathBuf,
|
||||
fs::{File, OpenOptions},
|
||||
error::Error
|
||||
};
|
||||
use tui::backend::CrosstermBackend;
|
||||
use tui::Terminal;
|
||||
use clap::{Parser, ValueHint::FilePath};
|
||||
|
||||
const DEFAULT_DATABASE_NAME: &str = "kanban.json";
|
||||
|
||||
@ -27,7 +22,7 @@ const DEFAULT_DATABASE_NAME: &str = "kanban.json";
|
||||
pub struct CliArgs {
|
||||
#[arg(value_name="DATABASE", value_hint=FilePath, index=1)]
|
||||
/// Path to the
|
||||
pub filepath: Option<PathBuf>
|
||||
pub filepath: Option<PathBuf>,
|
||||
}
|
||||
|
||||
// TODO: This should just return a struct beacuse we should add a
|
||||
@ -42,54 +37,55 @@ fn prompt_project_init(default_name: &str) -> (String, io::Result<File>) {
|
||||
let result = io::stdin().read_line(&mut input);
|
||||
let input = input.trim();
|
||||
|
||||
let filename =
|
||||
match result {
|
||||
Ok(b) if b == 0 => std::process::exit(0),
|
||||
Ok(b) if b > 0 && !input.is_empty() => input,
|
||||
_ => default_name
|
||||
};
|
||||
let filename = match result {
|
||||
Ok(b) if b == 0 => std::process::exit(0),
|
||||
Ok(b) if b > 0 && !input.is_empty() => input,
|
||||
_ => default_name,
|
||||
};
|
||||
|
||||
// TODO: This might be a good time to prompt the user if they want
|
||||
// to change the default column names
|
||||
|
||||
(filename.to_string(),
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.read(true)
|
||||
.create(true)
|
||||
.open(filename))
|
||||
(
|
||||
filename.to_string(),
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.read(true)
|
||||
.create(true)
|
||||
.open(filename),
|
||||
)
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<(), Box<dyn Error>> {
|
||||
let (filepath, file) =
|
||||
match CliArgs::parse() {
|
||||
CliArgs { filepath: Some(filepath) } => {
|
||||
let fpath = filepath.into_os_string().into_string().unwrap();
|
||||
let file = OpenOptions::new()
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(&fpath);
|
||||
#[async_std::main]
|
||||
async fn main() -> anyhow::Result<(), Box<dyn Error>> {
|
||||
let (filepath, file) = match CliArgs::parse() {
|
||||
CliArgs {
|
||||
filepath: Some(filepath),
|
||||
} => {
|
||||
let fpath = filepath.into_os_string().into_string().unwrap();
|
||||
let file = OpenOptions::new().write(true).read(true).open(&fpath);
|
||||
|
||||
if let Ok(f) = file {
|
||||
(fpath, f)
|
||||
} else {
|
||||
let (fp, fname) = prompt_project_init(&fpath);
|
||||
(fp, fname.unwrap())
|
||||
}
|
||||
},
|
||||
CliArgs { filepath: None } => {
|
||||
let file = OpenOptions::new()
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(DEFAULT_DATABASE_NAME);
|
||||
if let Ok(f) = file {
|
||||
(DEFAULT_DATABASE_NAME.to_string(), f)
|
||||
} else {
|
||||
let (fp, fname) = prompt_project_init(DEFAULT_DATABASE_NAME);
|
||||
(fp, fname.unwrap())
|
||||
}
|
||||
if let Ok(f) = file {
|
||||
(fpath, f)
|
||||
} else {
|
||||
let (fp, fname) = prompt_project_init(&fpath);
|
||||
(fp, fname.unwrap())
|
||||
}
|
||||
};
|
||||
}
|
||||
CliArgs { filepath: None } => {
|
||||
let file = OpenOptions::new()
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(DEFAULT_DATABASE_NAME);
|
||||
if let Ok(f) = file {
|
||||
(DEFAULT_DATABASE_NAME.to_string(), f)
|
||||
} else {
|
||||
let (fp, fname) = prompt_project_init(DEFAULT_DATABASE_NAME);
|
||||
(fp, fname.unwrap())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut state = State::new(Project::load(filepath, &file)?);
|
||||
|
||||
enable_raw_mode()?;
|
||||
|
14
src/ui.rs
14
src/ui.rs
@ -19,7 +19,8 @@ fn draw_tasks<B: Backend>(f: &mut Frame<'_, B>, area: Rect, state: &State<'_>) {
|
||||
.split(area);
|
||||
|
||||
for (i, column) in state.project.columns.iter().enumerate() {
|
||||
let items: Vec<ListItem<'_>> = column.tasks
|
||||
let items: Vec<ListItem<'_>> = column
|
||||
.tasks
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(j, task)| {
|
||||
@ -123,7 +124,7 @@ pub fn draw_task_popup<B: Backend>(f: &mut Frame<'_, B>, state: &mut State<'_>,
|
||||
[
|
||||
Constraint::Percentage(80),
|
||||
Constraint::Min(10),
|
||||
Constraint::Min(10)
|
||||
Constraint::Min(10),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
@ -167,7 +168,8 @@ pub fn draw_task_popup<B: Backend>(f: &mut Frame<'_, B>, state: &mut State<'_>,
|
||||
task.title.set_block(b1);
|
||||
if let TaskEditFocus::Title = task.focus {
|
||||
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 {
|
||||
task.title.set_style(Style::default());
|
||||
task.title.set_cursor_style(Style::default());
|
||||
@ -176,8 +178,10 @@ pub fn draw_task_popup<B: Backend>(f: &mut Frame<'_, B>, state: &mut State<'_>,
|
||||
|
||||
task.description.set_block(b2);
|
||||
if let TaskEditFocus::Description = task.focus {
|
||||
task.description.set_style(Style::default().fg(Color::Yellow));
|
||||
task.description.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
|
||||
task.description
|
||||
.set_style(Style::default().fg(Color::Yellow));
|
||||
task.description
|
||||
.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
|
||||
} else {
|
||||
task.description.set_style(Style::default());
|
||||
task.description.set_cursor_style(Style::default());
|
||||
|
Loading…
x
Reference in New Issue
Block a user