Refactor task edit by wrapping focus with int-enum, remove some .clone()'s

This commit is contained in:
Joseph Ferano 2023-06-15 20:44:25 +07:00
parent 41778accdc
commit a479b72a29
2 changed files with 68 additions and 55 deletions

View File

@ -3,6 +3,7 @@ use rusqlite::Connection;
use serde::{Deserialize, Serialize};
use std::cmp::min;
use tui_textarea::TextArea;
use int_enum::IntEnum;
use crate::db;
@ -24,12 +25,15 @@ pub struct Task {
pub description: String,
}
#[derive(Debug)]
pub const EDIT_WINDOW_FOCUS_STATES: i8 = 4;
#[repr(i8)]
#[derive(Debug, IntEnum, Copy, Clone)]
pub enum TaskEditFocus {
Title,
Description,
ConfirmBtn,
CancelBtn,
Title = 0,
Description = 1,
ConfirmBtn = 2,
CancelBtn = 3,
}
pub struct TaskState<'a> {
@ -276,8 +280,9 @@ impl<'a> State<'a> {
if let Some(selected_task) = self.get_selected_task_mut() {
selected_task.title = title;
selected_task.description = description;
let cloned = selected_task.clone();
self.db_conn.update_task_text(&cloned)?;
}
if let Some(task) = self.get_selected_task() {
self.db_conn.update_task_text(task)?;
}
Ok(())
}

View File

@ -1,57 +1,65 @@
use crate::app::{State, TaskEditFocus, TaskState};
use crate::app::{State, TaskEditFocus, TaskState, EDIT_WINDOW_FOCUS_STATES};
use crossterm::event;
use crossterm::event::{Event, KeyCode};
use int_enum::IntEnum;
use anyhow::Error;
pub fn handle_task_edit(state: &mut State<'_>, key: event::KeyEvent) -> Result<(), anyhow::Error> {
if let Some(mut task) = state.task_edit_state.take() {
let mut clear_task = false;
match task.focus {
// TODO: Handle wrapping around the enum rather than doing it manually
TaskEditFocus::Title => match key.code {
KeyCode::Tab => task.focus = TaskEditFocus::Description,
KeyCode::BackTab => task.focus = TaskEditFocus::CancelBtn,
KeyCode::Enter => (),
_ => {
task.title.input(key);
}
},
TaskEditFocus::Description => match key.code {
KeyCode::Tab => task.focus = TaskEditFocus::ConfirmBtn,
KeyCode::BackTab => task.focus = TaskEditFocus::Title,
_ => {
task.description.input(key);
}
},
TaskEditFocus::ConfirmBtn => match key.code {
KeyCode::Tab => task.focus = TaskEditFocus::CancelBtn,
KeyCode::BackTab => task.focus = TaskEditFocus::Description,
KeyCode::Enter => {
let title = task.title.clone().into_lines().join("\n");
let description = task.description.clone().into_lines().join("\n");
if task.is_edit {
state.edit_task(title, description)?;
} else {
state.add_new_task(title, description)?;
}
clear_task = true;
}
_ => (),
},
TaskEditFocus::CancelBtn => match key.code {
KeyCode::Tab => task.focus = TaskEditFocus::Title,
KeyCode::BackTab => task.focus = TaskEditFocus::ConfirmBtn,
KeyCode::Enter => clear_task = true,
_ => (),
},
}
if !clear_task {
state.task_edit_state = Some(task);
}
pub fn cycle_focus(task: &mut TaskState<'_>, forward: bool) -> Result<(), Error>{
let cycle;
if forward {
cycle = (task.focus.int_value() + 1) % EDIT_WINDOW_FOCUS_STATES;
} else {
cycle = (task.focus.int_value() - 1) % EDIT_WINDOW_FOCUS_STATES;
}
task.focus = TaskEditFocus::from_int(cycle)?;
Ok(())
}
pub fn handle_main(state: &mut State<'_>, key: event::KeyEvent) -> Result<(), anyhow::Error> {
pub fn handle_task_edit(state: &mut State<'_>, key: event::KeyEvent) -> Result<(), Error> {
// .take() the option so we can avoid borrow checker issues when
// we try to edit the task since that mutably borrows State, then
// assign later to task_edit_state
let updated_task = if let Some(mut task) = state.task_edit_state.take() {
match (key.code, task.focus) {
(KeyCode::Tab, _) => {
cycle_focus(&mut task, true)?;
Some(task)
},
(KeyCode::BackTab, _) => {
cycle_focus(&mut task, false)?;
Some(task)
},
(KeyCode::Enter, TaskEditFocus::ConfirmBtn) => {
let title = task.title.clone().into_lines().join("\n");
let description = task.description.clone().into_lines().join("\n");
if task.is_edit {
state.edit_task(title, description)?;
} else {
state.add_new_task(title, description)?;
}
None
}
(KeyCode::Enter, TaskEditFocus::CancelBtn) => None,
// Ignore enter on the title bar to effectively make it single line
(KeyCode::Enter, TaskEditFocus::Title) => Some(task),
(_, TaskEditFocus::Title) => {
task.title.input(key);
Some(task)
},
(_, TaskEditFocus::Description) => {
task.description.input(key);
Some(task)
}
_ => Some(task)
}
} else {
None
};
state.task_edit_state = updated_task;
Ok(())
}
pub fn handle_main(state: &mut State<'_>, key: event::KeyEvent) -> Result<(), Error> {
match key.code {
KeyCode::Char('q') => state.quit = true,
KeyCode::Char('h') | KeyCode::Left => state.select_previous_column()?,
@ -75,7 +83,7 @@ pub fn handle_main(state: &mut State<'_>, key: event::KeyEvent) -> Result<(), an
/// # Errors
///
/// Crossterm `event::read()` might return an error
pub fn handle(state: &mut State<'_>) -> Result<(), anyhow::Error> {
pub fn handle(state: &mut State<'_>) -> Result<(), Error> {
if let Event::Key(key) = event::read()? {
if state.task_edit_state.is_some() {
handle_task_edit(state, key)?;