Add docs and split state.rs
This commit is contained in:
parent
21ba0d9882
commit
1d8b050a3d
@ -7,13 +7,13 @@ use crate::{
|
|||||||
utility::Op,
|
utility::Op,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Handle all key events
|
/// Handle all key events.
|
||||||
|
/// This function maps any user key input to the corresponding application state update
|
||||||
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 key_event.code {
|
match key_event.code {
|
||||||
// Escape to exit
|
KeyCode::Esc | KeyCode::Char('q') => app.should_quit = true,
|
||||||
KeyCode::Esc => app.should_quit = true,
|
|
||||||
KeyCode::Char('a') => app.insert_task(app.selected_col, Task::new_test_task()),
|
KeyCode::Char('a') => app.insert_task(app.selected_col, Task::new_test_task()),
|
||||||
KeyCode::Char('h') => app.update_selected_column(&Op::Decrement),
|
KeyCode::Char('h') => app.update_selected_column(&Op::Decrement),
|
||||||
KeyCode::Char('l') => app.update_selected_column(&Op::Increment),
|
KeyCode::Char('l') => app.update_selected_column(&Op::Increment),
|
||||||
|
10
src/main.rs
10
src/main.rs
@ -27,10 +27,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
3
|
3
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize application state
|
// Initialize application state and terminal
|
||||||
let mut app_state = ApplicationState::new(num_columns);
|
let mut app_state = ApplicationState::new(num_columns);
|
||||||
run(&mut app_state)?;
|
let mut terminal = ratatui::init();
|
||||||
|
terminal.clear()?;
|
||||||
|
|
||||||
|
// Run main loop
|
||||||
|
run(terminal, &mut app_state)?;
|
||||||
// Clean up terminal state
|
// Clean up terminal state
|
||||||
terminal::disable_raw_mode()?;
|
ratatui::restore();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
4
src/state/mod.rs
Normal file
4
src/state/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
mod state;
|
||||||
|
mod task;
|
||||||
|
pub use crate::state::state::ApplicationState;
|
||||||
|
pub use crate::state::task::Task;
|
@ -1,5 +1,7 @@
|
|||||||
use crate::utility::Op;
|
use crate::utility::Op;
|
||||||
|
|
||||||
|
/// Struct which holds the current application state.
|
||||||
|
/// In essence this reprents the data associated with the Kanban board.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ApplicationState {
|
pub struct ApplicationState {
|
||||||
pub columns: usize,
|
pub columns: usize,
|
||||||
@ -11,24 +13,8 @@ pub struct ApplicationState {
|
|||||||
pub selected_item: usize,
|
pub selected_item: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Task {
|
|
||||||
pub title: String,
|
|
||||||
pub notes: String,
|
|
||||||
pub tags: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task {
|
|
||||||
pub fn new_test_task() -> Self {
|
|
||||||
Task {
|
|
||||||
title: "This is a test".to_string(),
|
|
||||||
notes: "".to_string(),
|
|
||||||
tags: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApplicationState {
|
impl ApplicationState {
|
||||||
|
/// Initialize the application state from the number of columns
|
||||||
pub fn new(columns: usize) -> Self {
|
pub fn new(columns: usize) -> Self {
|
||||||
ApplicationState {
|
ApplicationState {
|
||||||
columns,
|
columns,
|
||||||
@ -59,7 +45,7 @@ impl ApplicationState {
|
|||||||
self.col_idx[col] == self.col_idx[col + 1]
|
self.col_idx[col] == self.col_idx[col + 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Placeholder testing function to insert a new task in a column
|
/// Insert a new task at the end of a column
|
||||||
pub fn insert_task(&mut self, col: usize, task: Task) {
|
pub fn insert_task(&mut self, col: usize, task: Task) {
|
||||||
// Compute the index of the new item
|
// Compute the index of the new item
|
||||||
let idx = self.col_idx[col + 1];
|
let idx = self.col_idx[col + 1];
|
||||||
@ -74,6 +60,7 @@ impl ApplicationState {
|
|||||||
self.selected_item = idx;
|
self.selected_item = idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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) {
|
||||||
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() {
|
||||||
@ -100,6 +87,8 @@ 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) {
|
||||||
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
|
||||||
|
// of the column or before the start of the column.
|
||||||
let new_idx = op
|
let new_idx = op
|
||||||
.apply(item)
|
.apply(item)
|
||||||
.max(self.col_idx[col])
|
.max(self.col_idx[col])
|
||||||
@ -107,6 +96,7 @@ impl ApplicationState {
|
|||||||
self.tasks.swap(item, new_idx);
|
self.tasks.swap(item, new_idx);
|
||||||
self.selected_item = new_idx;
|
self.selected_item = new_idx;
|
||||||
} else {
|
} else {
|
||||||
|
// Move a task to the end of either the next column or previous column
|
||||||
match op {
|
match op {
|
||||||
Op::Decrement => {
|
Op::Decrement => {
|
||||||
if self.selected_col > 0 {
|
if self.selected_col > 0 {
|
18
src/state/task.rs
Normal file
18
src/state/task.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/// Struct representing a single task
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Task {
|
||||||
|
pub title: String,
|
||||||
|
pub notes: String,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Task {
|
||||||
|
/// Testing function to generate a new dummy task
|
||||||
|
pub fn new_test_task() -> Self {
|
||||||
|
Task {
|
||||||
|
title: "This is a test".to_string(),
|
||||||
|
notes: "".to_string(),
|
||||||
|
tags: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/tui.rs
23
src/tui.rs
@ -6,7 +6,7 @@ use ratatui::{
|
|||||||
prelude::CrosstermBackend,
|
prelude::CrosstermBackend,
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
widgets::{Block, Borders, List, ListItem, Paragraph, Wrap},
|
widgets::{Block, Borders, List, ListItem, Paragraph, Wrap},
|
||||||
Terminal,
|
DefaultTerminal, Terminal,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -15,17 +15,11 @@ use crate::{
|
|||||||
widgets::blocks::{basic_block, highlighted_border_block, highlighted_item_block},
|
widgets::blocks::{basic_block, highlighted_border_block, highlighted_item_block},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(app_state: &mut ApplicationState) -> Result<(), Box<dyn Error>> {
|
/// Main running function for the application that sets up the interaction loop
|
||||||
// Set up terminal
|
pub fn run(
|
||||||
let mut stdout = io::stdout();
|
mut terminal: DefaultTerminal,
|
||||||
terminal::enable_raw_mode()?;
|
app_state: &mut ApplicationState,
|
||||||
stdout.execute(terminal::Clear(terminal::ClearType::All))?;
|
) -> Result<(), Box<dyn Error>> {
|
||||||
stdout.execute(terminal::EnterAlternateScreen)?;
|
|
||||||
|
|
||||||
// Initialize the terminal with the Crossterm backend
|
|
||||||
let backend = CrosstermBackend::new(stdout);
|
|
||||||
let mut terminal = Terminal::new(backend)?;
|
|
||||||
|
|
||||||
// Start the main loop for rendering
|
// Start the main loop for rendering
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|f| {
|
terminal.draw(|f| {
|
||||||
@ -41,8 +35,8 @@ pub fn run(app_state: &mut ApplicationState) -> Result<(), Box<dyn Error>> {
|
|||||||
.constraints::<&Vec<Constraint>>(constraints.as_ref())
|
.constraints::<&Vec<Constraint>>(constraints.as_ref())
|
||||||
.split(size);
|
.split(size);
|
||||||
|
|
||||||
// Render a item blocks in each column
|
|
||||||
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
|
||||||
let block = if i == app_state.selected_col {
|
let block = if i == app_state.selected_col {
|
||||||
highlighted_border_block(i)
|
highlighted_border_block(i)
|
||||||
} else {
|
} else {
|
||||||
@ -54,6 +48,8 @@ pub fn run(app_state: &mut ApplicationState) -> Result<(), Box<dyn Error>> {
|
|||||||
if nitems == 0 {
|
if nitems == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up sub layout which holds each item
|
||||||
let constraints: Vec<Constraint> =
|
let constraints: Vec<Constraint> =
|
||||||
vec![Constraint::Max(5); nitems].into_iter().collect();
|
vec![Constraint::Max(5); nitems].into_iter().collect();
|
||||||
let sub_chunks = Layout::default()
|
let sub_chunks = Layout::default()
|
||||||
@ -64,6 +60,7 @@ pub fn run(app_state: &mut ApplicationState) -> Result<(), Box<dyn Error>> {
|
|||||||
for ((task, highlight), sub_chunk) in
|
for ((task, highlight), sub_chunk) in
|
||||||
zip(app_state.get_col_slice(i), sub_chunks.iter())
|
zip(app_state.get_col_slice(i), sub_chunks.iter())
|
||||||
{
|
{
|
||||||
|
// Set up each item and style the selected item
|
||||||
let style = if highlight {
|
let style = if highlight {
|
||||||
Style::default().bg(Color::Blue).fg(Color::DarkGray)
|
Style::default().bg(Color::Blue).fg(Color::DarkGray)
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,6 +5,7 @@ pub enum Op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Op {
|
impl Op {
|
||||||
|
/// Apply the operator to a usize. We use a saturating subtraction to avoid panicking
|
||||||
pub fn apply(&self, val: usize) -> usize {
|
pub fn apply(&self, val: usize) -> usize {
|
||||||
match &self {
|
match &self {
|
||||||
Op::Decrement => val.saturating_sub(1),
|
Op::Decrement => val.saturating_sub(1),
|
||||||
|
Reference in New Issue
Block a user