Add docs and split state.rs
This commit is contained in:
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),
|
||||
|
10
src/main.rs
10
src/main.rs
@ -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(())
|
||||
}
|
||||
|
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;
|
||||
|
||||
/// 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 {
|
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,
|
||||
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),
|
||||
|
Reference in New Issue
Block a user