use crate::input::CommandRegistry;
use crate::models::{Project, Task};
use crate::ui::layout::SplitNode;
use crate::ui::dialogs::DialogType;
use anyhow::Result;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mode {
Normal,
Command,
TaskSelect,
Dialog,
Help,
SpaceMenu,
Preview,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MenuState {
Main,
Project,
Window,
Task,
}
pub struct App {
pub projects: Vec<Project>,
pub split_tree: SplitNode,
pub focused_pane: usize,
pub mode: Mode,
pub key_buffer: Vec<char>,
pub selected_task_index: HashMap<usize, usize>,
pub selected_column: HashMap<usize, usize>,
pub command_input: String,
pub next_pane_id: usize,
pub should_quit: bool,
pub dialog: Option<DialogType>,
pub menu_state: Option<MenuState>,
pub pending_editor_file: Option<String>,
pub is_new_task_file: bool,
pub pending_preview_file: Option<String>,
pub preview_content: String,
pub preview_scroll: u16,
pub command_registry: CommandRegistry,
pub config: crate::config::Config,
pub show_welcome_dialog: bool,
pub ime_state: crate::ime::ImeState,
}
impl App {
pub fn new() -> Result<Self> {
let (config, is_first_run) = crate::config::check_first_run()?;
let projects = crate::fs::load_all_projects()?;
let mut split_tree = SplitNode::new_leaf(0);
if !projects.is_empty() {
if let Some(SplitNode::Leaf { project_id, .. }) = split_tree.find_pane_mut(0) {
*project_id = Some(projects[0].name.clone());
}
}
let mut app = Self {
projects,
split_tree,
focused_pane: 0,
mode: Mode::Normal,
key_buffer: Vec::new(),
selected_task_index: HashMap::new(),
selected_column: HashMap::new(),
command_input: String::new(),
next_pane_id: 1,
should_quit: false,
dialog: None,
menu_state: None,
pending_editor_file: None,
is_new_task_file: false,
pending_preview_file: None,
preview_content: String::new(),
preview_scroll: 0,
command_registry: CommandRegistry::new(),
config,
show_welcome_dialog: is_first_run,
ime_state: crate::ime::ImeState::new(),
};
if let Ok(state) = crate::state::load_state() {
crate::state::apply_state(&mut app, state);
}
app.ime_state.switch_to_english();
Ok(app)
}
pub fn handle_key(&mut self, key: crossterm::event::KeyEvent) -> bool {
use crate::input::handle_key_input;
handle_key_input(self, key)
}
pub fn get_focused_project(&self) -> Option<&Project> {
if let Some(SplitNode::Leaf { project_id, .. }) = self.split_tree.find_pane(self.focused_pane)
{
if let Some(pid) = project_id {
return self.projects.iter().find(|p| &p.name == pid);
}
}
None
}
pub fn get_focused_project_mut(&mut self) -> Option<&mut Project> {
if let Some(SplitNode::Leaf { project_id, .. }) = self.split_tree.find_pane(self.focused_pane)
{
if let Some(pid) = project_id.clone() {
return self.projects.iter_mut().find(|p| p.name == pid);
}
}
None
}
pub fn set_focused_project(&mut self, project_name: String) {
let projects_dir = crate::fs::get_projects_dir();
let project_path = projects_dir.join(&project_name);
if let Ok(updated_project) = crate::fs::load_project(&project_path) {
if let Some(project) = self.projects.iter_mut().find(|p| p.name == project_name) {
*project = updated_project;
} else {
self.projects.push(updated_project);
}
}
if let Some(SplitNode::Leaf { project_id, .. }) = self.split_tree.find_pane_mut(self.focused_pane)
{
*project_id = Some(project_name);
self.selected_task_index.insert(self.focused_pane, 0);
self.selected_column.insert(self.focused_pane, 0);
}
}
pub fn reload_current_project(&mut self) -> Result<()> {
if let Some(SplitNode::Leaf { project_id, .. }) = self.split_tree.find_pane(self.focused_pane)
{
if let Some(pid) = project_id {
if let Some(project) = self.projects.iter().find(|p| &p.name == pid) {
let project_path = project.path.clone();
let project_type = project.project_type;
if let Ok(updated_project) = crate::fs::load_project_with_type(&project_path, project_type) {
if let Some(project) = self.projects.iter_mut().find(|p| &p.name == pid) {
*project = updated_project;
}
}
}
}
}
Ok(())
}
}