[WIP] Current development efforts*
Pushing this broken code development in case I ever want to revisit this project. For now, this branch doesn't compile.
This commit is contained in:
parent
dc9426c8a5
commit
26b8bdb551
12
.gitignore
vendored
12
.gitignore
vendored
@ -14,3 +14,15 @@ Cargo.lock
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
|
||||
|
||||
# Added by cargo
|
||||
|
||||
/target
|
||||
|
||||
|
||||
# Added by cargo
|
||||
#
|
||||
# already existing elements were commented out
|
||||
|
||||
#/target
|
||||
|
@ -3,7 +3,7 @@ use std::error::Error;
|
||||
use crossterm::event::{self, KeyCode};
|
||||
|
||||
use crate::{
|
||||
state::{ApplicationState, Task},
|
||||
state::{AppMode, ApplicationState, Task},
|
||||
utility::Op,
|
||||
};
|
||||
|
||||
@ -12,29 +12,40 @@ use crate::{
|
||||
pub fn event_key_handler(app: &mut ApplicationState) -> Result<(), Box<dyn Error>> {
|
||||
if event::poll(std::time::Duration::from_millis(100))? {
|
||||
if let event::Event::Key(key_event) = event::read()? {
|
||||
match key_event.code {
|
||||
KeyCode::Esc | KeyCode::Char('q') => app.should_quit = true,
|
||||
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)
|
||||
_ => {}
|
||||
match app.mode {
|
||||
AppMode::Board => board_key_handler(app, &key_event.code)?,
|
||||
AppMode::NewTask => panic!("Not implemented yet"),
|
||||
}
|
||||
}
|
||||
};
|
||||
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(())
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
mod state;
|
||||
mod task;
|
||||
pub use crate::state::state::AppMode;
|
||||
pub use crate::state::state::ApplicationState;
|
||||
pub use crate::state::task::Task;
|
||||
|
@ -1,9 +1,16 @@
|
||||
use crate::utility::Op;
|
||||
use crate::{utility::Op, widgets::popups::NewTaskPopup};
|
||||
|
||||
use super::Task;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AppMode {
|
||||
Board,
|
||||
NewTask,
|
||||
}
|
||||
/// Struct which holds the current application state.
|
||||
/// In essence this reprents the data associated with the Kanban board.
|
||||
#[derive(Debug)]
|
||||
pub struct ApplicationState {
|
||||
pub struct ApplicationState<'a> {
|
||||
pub columns: usize,
|
||||
col_idx: Vec<usize>,
|
||||
pub tasks: Vec<Task>,
|
||||
@ -11,9 +18,11 @@ pub struct ApplicationState {
|
||||
pub should_quit: bool,
|
||||
pub selected_col: 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
|
||||
pub fn new(columns: usize) -> Self {
|
||||
ApplicationState {
|
||||
@ -24,6 +33,8 @@ impl ApplicationState {
|
||||
should_quit: false,
|
||||
selected_col: 0,
|
||||
selected_item: 0,
|
||||
mode: AppMode::Board,
|
||||
popup: NewTaskPopup::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,9 +73,15 @@ impl ApplicationState {
|
||||
|
||||
/// Remove a task from the board
|
||||
pub fn remove_task(&mut self, col: usize, item: usize) {
|
||||
self.tasks.remove(item);
|
||||
for start_idx in self.col_idx[col + 1..].iter_mut() {
|
||||
*start_idx -= 1;
|
||||
if !self.tasks.is_empty() && item < self.tasks.len() {
|
||||
self.tasks.remove(item);
|
||||
for start_idx in self.col_idx[col + 1..].iter_mut() {
|
||||
*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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,6 +103,11 @@ impl ApplicationState {
|
||||
|
||||
/// 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) {
|
||||
// Nothing to move since nothing is selected
|
||||
if item == self.tasks.len() {
|
||||
return;
|
||||
}
|
||||
|
||||
if in_col {
|
||||
// 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.
|
||||
|
@ -16,3 +16,13 @@ impl Task {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Task {
|
||||
fn default() -> Self {
|
||||
Task {
|
||||
title: "".to_owned(),
|
||||
notes: "".to_owned(),
|
||||
tags: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
22
src/tui.rs
22
src/tui.rs
@ -2,7 +2,7 @@ use std::{error::Error, io, iter::zip};
|
||||
|
||||
use crossterm::{terminal, ExecutableCommand};
|
||||
use ratatui::{
|
||||
layout::{Constraint, Direction, Layout},
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
prelude::CrosstermBackend,
|
||||
style::{Color, Style},
|
||||
widgets::{Block, Borders, List, ListItem, Paragraph, Wrap},
|
||||
@ -11,7 +11,7 @@ use ratatui::{
|
||||
|
||||
use crate::{
|
||||
event_handler::event_key_handler,
|
||||
state::ApplicationState,
|
||||
state::{AppMode, ApplicationState},
|
||||
widgets::blocks::{basic_block, highlighted_border_block, highlighted_item_block},
|
||||
};
|
||||
|
||||
@ -38,9 +38,9 @@ pub fn run(
|
||||
for (i, chunk) in chunks.iter().enumerate() {
|
||||
// Create columns and apply styling based on wehter the column is selected or not
|
||||
let block = if i == app_state.selected_col {
|
||||
highlighted_border_block(i)
|
||||
highlighted_border_block(&format!("Column {}", i + 1))
|
||||
} else {
|
||||
basic_block(Some(i))
|
||||
basic_block(&format!("Column {}", i + 1))
|
||||
};
|
||||
f.render_widget(block, *chunk);
|
||||
|
||||
@ -67,11 +67,23 @@ pub fn run(
|
||||
Style::default()
|
||||
};
|
||||
let paragraph = Paragraph::new(task.title.clone())
|
||||
.block(basic_block(None))
|
||||
.block(basic_block(""))
|
||||
.style(style);
|
||||
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
|
||||
|
@ -4,22 +4,17 @@ use ratatui::{
|
||||
};
|
||||
|
||||
/// Return the basic block which uses normal foreground coloring
|
||||
pub fn basic_block(i: Option<usize>) -> Block<'static> {
|
||||
pub fn basic_block(title: &str) -> Block<'static> {
|
||||
let block = Block::new().borders(Borders::ALL);
|
||||
|
||||
if let Some(i) = i {
|
||||
block.title(format!("Column {}", i + 1))
|
||||
} else {
|
||||
block
|
||||
}
|
||||
block.title(title.to_owned())
|
||||
}
|
||||
|
||||
/// Return the block used for highlighted borders
|
||||
pub fn highlighted_border_block(i: usize) -> Block<'static> {
|
||||
pub fn highlighted_border_block(title: &str) -> Block<'static> {
|
||||
Block::new()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::Blue))
|
||||
.title(format!("Column {}", i + 1))
|
||||
.title(title.to_owned())
|
||||
}
|
||||
|
||||
pub fn highlighted_item_block() -> Block<'static> {
|
||||
|
@ -1 +1,2 @@
|
||||
pub mod blocks;
|
||||
pub mod popups;
|
||||
|
Reference in New Issue
Block a user