Add docs and split state.rs

master
Alex Selimov 4 weeks ago
parent 21ba0d9882
commit 1d8b050a3d

@ -7,13 +7,13 @@ use crate::{
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>> {
if event::poll(std::time::Duration::from_millis(100))? {
if let event::Event::Key(key_event) = event::read()? {
match key_event.code {
// Escape to exit
KeyCode::Esc => app.should_quit = true,
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),

@ -27,10 +27,14 @@ fn main() -> Result<(), Box<dyn Error>> {
3
};
// Initialize application state
// Initialize application state and terminal
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
terminal::disable_raw_mode()?;
ratatui::restore();
Ok(())
}

@ -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;
/// Struct which holds the current application state.
/// In essence this reprents the data associated with the Kanban board.
#[derive(Debug)]
pub struct ApplicationState {
pub columns: usize,
@ -11,24 +13,8 @@ pub struct ApplicationState {
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 {
/// Initialize the application state from the number of columns
pub fn new(columns: usize) -> Self {
ApplicationState {
columns,
@ -59,7 +45,7 @@ impl ApplicationState {
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) {
// Compute the index of the new item
let idx = self.col_idx[col + 1];
@ -74,6 +60,7 @@ impl ApplicationState {
self.selected_item = idx;
}
/// 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() {
@ -100,6 +87,8 @@ 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) {
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
.apply(item)
.max(self.col_idx[col])
@ -107,6 +96,7 @@ impl ApplicationState {
self.tasks.swap(item, new_idx);
self.selected_item = new_idx;
} else {
// Move a task to the end of either the next column or previous column
match op {
Op::Decrement => {
if self.selected_col > 0 {

@ -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![],
}
}
}

@ -6,7 +6,7 @@ use ratatui::{
prelude::CrosstermBackend,
style::{Color, Style},
widgets::{Block, Borders, List, ListItem, Paragraph, Wrap},
Terminal,
DefaultTerminal, Terminal,
};
use crate::{
@ -15,17 +15,11 @@ use crate::{
widgets::blocks::{basic_block, highlighted_border_block, highlighted_item_block},
};
pub fn run(app_state: &mut ApplicationState) -> Result<(), Box<dyn Error>> {
// Set up terminal
let mut stdout = io::stdout();
terminal::enable_raw_mode()?;
stdout.execute(terminal::Clear(terminal::ClearType::All))?;
stdout.execute(terminal::EnterAlternateScreen)?;
// Initialize the terminal with the Crossterm backend
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
/// Main running function for the application that sets up the interaction loop
pub fn run(
mut terminal: DefaultTerminal,
app_state: &mut ApplicationState,
) -> Result<(), Box<dyn Error>> {
// Start the main loop for rendering
loop {
terminal.draw(|f| {
@ -41,8 +35,8 @@ pub fn run(app_state: &mut ApplicationState) -> Result<(), Box<dyn Error>> {
.constraints::<&Vec<Constraint>>(constraints.as_ref())
.split(size);
// Render a item blocks in each column
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)
} else {
@ -54,6 +48,8 @@ pub fn run(app_state: &mut ApplicationState) -> Result<(), Box<dyn Error>> {
if nitems == 0 {
continue;
}
// Set up sub layout which holds each item
let constraints: Vec<Constraint> =
vec![Constraint::Max(5); nitems].into_iter().collect();
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
zip(app_state.get_col_slice(i), sub_chunks.iter())
{
// Set up each item and style the selected item
let style = if highlight {
Style::default().bg(Color::Blue).fg(Color::DarkGray)
} else {

@ -5,6 +5,7 @@ pub enum Op {
}
impl Op {
/// Apply the operator to a usize. We use a saturating subtraction to avoid panicking
pub fn apply(&self, val: usize) -> usize {
match &self {
Op::Decrement => val.saturating_sub(1),

Loading…
Cancel
Save