Compare commits

...
This repository has been archived on 2025-03-05. You can view files and clone it, but cannot push or open issues or pull requests.

1 Commits

Author SHA1 Message Date
26b8bdb551 [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.
2025-03-05 09:28:52 -05:00
8 changed files with 106 additions and 42 deletions

12
.gitignore vendored
View File

@ -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

View File

@ -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(())
}

View File

@ -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;

View File

@ -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.

View File

@ -16,3 +16,13 @@ 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 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

View File

@ -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> {

View File

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