Compare commits

..

No commits in common. "inprogress" and "master" have entirely different histories.

8 changed files with 42 additions and 106 deletions

12
.gitignore vendored
View File

@ -14,15 +14,3 @@ Cargo.lock
# MSVC Windows builds of rustc generate these, which store debugging information # MSVC Windows builds of rustc generate these, which store debugging information
*.pdb *.pdb
# Added by cargo
/target
# Added by cargo
#
# already existing elements were commented out
#/target

View File

@ -3,7 +3,7 @@ use std::error::Error;
use crossterm::event::{self, KeyCode}; use crossterm::event::{self, KeyCode};
use crate::{ use crate::{
state::{AppMode, ApplicationState, Task}, state::{ApplicationState, Task},
utility::Op, utility::Op,
}; };
@ -12,40 +12,29 @@ use crate::{
pub fn event_key_handler(app: &mut ApplicationState) -> Result<(), Box<dyn Error>> { pub fn event_key_handler(app: &mut ApplicationState) -> Result<(), Box<dyn Error>> {
if event::poll(std::time::Duration::from_millis(100))? { if event::poll(std::time::Duration::from_millis(100))? {
if let event::Event::Key(key_event) = event::read()? { if let event::Event::Key(key_event) = event::read()? {
match app.mode { match key_event.code {
AppMode::Board => board_key_handler(app, &key_event.code)?, KeyCode::Esc | KeyCode::Char('q') => app.should_quit = true,
AppMode::NewTask => panic!("Not implemented yet"), KeyCode::Char('a') => app.insert_task(app.selected_col, Task::new_test_task()),
KeyCode::Char('h') => app.update_selected_column(&Op::Decrement),
KeyCode::Char('l') => app.update_selected_column(&Op::Increment),
KeyCode::Char('k') => app.update_selected_item(&Op::Decrement),
KeyCode::Char('j') => app.update_selected_item(&Op::Increment),
KeyCode::Char('H') => {
app.move_task(app.selected_col, app.selected_item, &Op::Decrement, false)
}
KeyCode::Char('L') => {
app.move_task(app.selected_col, app.selected_item, &Op::Increment, false)
}
KeyCode::Char('K') => {
app.move_task(app.selected_col, app.selected_item, &Op::Decrement, true)
}
KeyCode::Char('J') => {
app.move_task(app.selected_col, app.selected_item, &Op::Increment, true)
}
// Resize the window (event handling is automatic with ratatui)
_ => {}
} }
} }
}; };
Ok(()) Ok(())
} }
fn board_key_handler(app: &mut ApplicationState, code: &KeyCode) -> Result<(), Box<dyn Error>> {
match code {
KeyCode::Esc | KeyCode::Char('q') => app.should_quit = true,
KeyCode::Char('a') => {
app.popup.clear();
app.mode = AppMode::NewTask
}
KeyCode::Char('h') => app.update_selected_column(&Op::Decrement),
KeyCode::Char('l') => app.update_selected_column(&Op::Increment),
KeyCode::Char('k') => app.update_selected_item(&Op::Decrement),
KeyCode::Char('j') => app.update_selected_item(&Op::Increment),
KeyCode::Char('H') => {
app.move_task(app.selected_col, app.selected_item, &Op::Decrement, false)
}
KeyCode::Char('L') => {
app.move_task(app.selected_col, app.selected_item, &Op::Increment, false)
}
KeyCode::Char('K') => {
app.move_task(app.selected_col, app.selected_item, &Op::Decrement, true)
}
KeyCode::Char('J') => {
app.move_task(app.selected_col, app.selected_item, &Op::Increment, true)
}
KeyCode::Char('x') => app.remove_task(app.selected_col, app.selected_item),
_ => {}
}
Ok(())
}

View File

@ -1,5 +1,4 @@
mod state; mod state;
mod task; mod task;
pub use crate::state::state::AppMode;
pub use crate::state::state::ApplicationState; pub use crate::state::state::ApplicationState;
pub use crate::state::task::Task; pub use crate::state::task::Task;

View File

@ -1,16 +1,9 @@
use crate::{utility::Op, widgets::popups::NewTaskPopup}; use crate::utility::Op;
use super::Task;
#[derive(Debug)]
pub enum AppMode {
Board,
NewTask,
}
/// Struct which holds the current application state. /// Struct which holds the current application state.
/// In essence this reprents the data associated with the Kanban board. /// In essence this reprents the data associated with the Kanban board.
#[derive(Debug)] #[derive(Debug)]
pub struct ApplicationState<'a> { pub struct ApplicationState {
pub columns: usize, pub columns: usize,
col_idx: Vec<usize>, col_idx: Vec<usize>,
pub tasks: Vec<Task>, pub tasks: Vec<Task>,
@ -18,11 +11,9 @@ pub struct ApplicationState<'a> {
pub should_quit: bool, pub should_quit: bool,
pub selected_col: usize, pub selected_col: usize,
pub selected_item: usize, pub selected_item: usize,
pub mode: AppMode,
pub popup: NewTaskPopup<'a>,
} }
impl ApplicationState<'_> { impl ApplicationState {
/// Initialize the application state from the number of columns /// Initialize the application state from the number of columns
pub fn new(columns: usize) -> Self { pub fn new(columns: usize) -> Self {
ApplicationState { ApplicationState {
@ -33,8 +24,6 @@ impl ApplicationState<'_> {
should_quit: false, should_quit: false,
selected_col: 0, selected_col: 0,
selected_item: 0, selected_item: 0,
mode: AppMode::Board,
popup: NewTaskPopup::new(),
} }
} }
@ -73,15 +62,9 @@ impl ApplicationState<'_> {
/// Remove a task from the board /// Remove a task from the board
pub fn remove_task(&mut self, col: usize, item: usize) { pub fn remove_task(&mut self, col: usize, item: usize) {
if !self.tasks.is_empty() && item < self.tasks.len() { self.tasks.remove(item);
self.tasks.remove(item); for start_idx in self.col_idx[col + 1..].iter_mut() {
for start_idx in self.col_idx[col + 1..].iter_mut() { *start_idx -= 1;
*start_idx -= 1;
}
// Now we want to update the selected item if there is another task in the column
if item != self.col_idx[col] {
self.selected_item -= 1;
}
} }
} }
@ -103,11 +86,6 @@ impl ApplicationState<'_> {
/// Move a task either between columns or within a column /// Move a task either between columns or within a column
pub fn move_task(&mut self, col: usize, item: usize, op: &Op, in_col: bool) { pub fn move_task(&mut self, col: usize, item: usize, op: &Op, in_col: bool) {
// Nothing to move since nothing is selected
if item == self.tasks.len() {
return;
}
if in_col { if in_col {
// Move a task up or down in the list by swapping. Make sure it can't go past the end // Move a task up or down in the list by swapping. Make sure it can't go past the end
// of the column or before the start of the column. // of the column or before the start of the column.

View File

@ -16,13 +16,3 @@ impl Task {
} }
} }
} }
impl Default for Task {
fn default() -> Self {
Task {
title: "".to_owned(),
notes: "".to_owned(),
tags: vec![],
}
}
}

View File

@ -2,7 +2,7 @@ use std::{error::Error, io, iter::zip};
use crossterm::{terminal, ExecutableCommand}; use crossterm::{terminal, ExecutableCommand};
use ratatui::{ use ratatui::{
layout::{Constraint, Direction, Layout, Rect}, layout::{Constraint, Direction, Layout},
prelude::CrosstermBackend, prelude::CrosstermBackend,
style::{Color, Style}, style::{Color, Style},
widgets::{Block, Borders, List, ListItem, Paragraph, Wrap}, widgets::{Block, Borders, List, ListItem, Paragraph, Wrap},
@ -11,7 +11,7 @@ use ratatui::{
use crate::{ use crate::{
event_handler::event_key_handler, event_handler::event_key_handler,
state::{AppMode, ApplicationState}, state::ApplicationState,
widgets::blocks::{basic_block, highlighted_border_block, highlighted_item_block}, widgets::blocks::{basic_block, highlighted_border_block, highlighted_item_block},
}; };
@ -38,9 +38,9 @@ pub fn run(
for (i, chunk) in chunks.iter().enumerate() { for (i, chunk) in chunks.iter().enumerate() {
// Create columns and apply styling based on wehter the column is selected or not // Create columns and apply styling based on wehter the column is selected or not
let block = if i == app_state.selected_col { let block = if i == app_state.selected_col {
highlighted_border_block(&format!("Column {}", i + 1)) highlighted_border_block(i)
} else { } else {
basic_block(&format!("Column {}", i + 1)) basic_block(Some(i))
}; };
f.render_widget(block, *chunk); f.render_widget(block, *chunk);
@ -67,23 +67,11 @@ pub fn run(
Style::default() Style::default()
}; };
let paragraph = Paragraph::new(task.title.clone()) let paragraph = Paragraph::new(task.title.clone())
.block(basic_block("")) .block(basic_block(None))
.style(style); .style(style);
f.render_widget(paragraph, *sub_chunk); f.render_widget(paragraph, *sub_chunk);
} }
} }
// Render popups if any
if let AppMode::NewTask = app_state.mode {
// take up a third of the screen vertically and half horizontally
let popup_area = Rect {
x: f.area().width / 4,
y: f.area().height / 3,
width: f.area().width / 2,
height: f.area().height / 3,
};
f.render_widget(app_state.popup, popup_area);
}
})?; })?;
// Handle events such as key presses // Handle events such as key presses

View File

@ -4,17 +4,22 @@ use ratatui::{
}; };
/// Return the basic block which uses normal foreground coloring /// Return the basic block which uses normal foreground coloring
pub fn basic_block(title: &str) -> Block<'static> { pub fn basic_block(i: Option<usize>) -> Block<'static> {
let block = Block::new().borders(Borders::ALL); let block = Block::new().borders(Borders::ALL);
block.title(title.to_owned())
if let Some(i) = i {
block.title(format!("Column {}", i + 1))
} else {
block
}
} }
/// Return the block used for highlighted borders /// Return the block used for highlighted borders
pub fn highlighted_border_block(title: &str) -> Block<'static> { pub fn highlighted_border_block(i: usize) -> Block<'static> {
Block::new() Block::new()
.borders(Borders::ALL) .borders(Borders::ALL)
.border_style(Style::default().fg(Color::Blue)) .border_style(Style::default().fg(Color::Blue))
.title(title.to_owned()) .title(format!("Column {}", i + 1))
} }
pub fn highlighted_item_block() -> Block<'static> { pub fn highlighted_item_block() -> Block<'static> {

View File

@ -1,2 +1 @@
pub mod blocks; pub mod blocks;
pub mod popups;