use std::cell::RefCell; use std::time::{Duration, Instant}; use cagire_ratatui::Sparkles; use tachyonfx::{fx, Effect, EffectManager, Interpolation}; use crate::page::Page; use crate::state::effects::FxId; use crate::state::{ColorScheme, Modal}; #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub enum FlashKind { #[default] Success, Error, Info, } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum DictFocus { #[default] Categories, Words, } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum HelpFocus { #[default] Topics, Content, } pub struct UiState { pub sparkles: Sparkles, pub status_message: Option, pub flash_until: Option, pub flash_kind: FlashKind, pub modal: Modal, pub help_focus: HelpFocus, pub help_topic: usize, pub help_scrolls: Vec, pub help_search_active: bool, pub help_search_query: String, pub dict_focus: DictFocus, pub dict_category: usize, pub dict_scrolls: Vec, pub dict_search_query: String, pub dict_search_active: bool, pub show_title: bool, pub runtime_highlight: bool, pub show_completion: bool, pub minimap_until: Option, pub color_scheme: ColorScheme, pub hue_rotation: f32, pub effects: RefCell>, pub modal_fx: RefCell>, pub title_fx: RefCell>, pub prev_modal_open: bool, pub prev_page: Page, pub prev_show_title: bool, } impl Default for UiState { fn default() -> Self { Self { sparkles: Sparkles::default(), status_message: None, flash_until: None, flash_kind: FlashKind::Success, modal: Modal::None, help_focus: HelpFocus::default(), help_topic: 0, help_scrolls: vec![0; crate::model::docs::topic_count()], help_search_active: false, help_search_query: String::new(), dict_focus: DictFocus::default(), dict_category: 0, dict_scrolls: vec![0; crate::model::categories::category_count()], dict_search_query: String::new(), dict_search_active: false, show_title: true, runtime_highlight: false, show_completion: true, minimap_until: None, color_scheme: ColorScheme::default(), hue_rotation: 0.0, effects: RefCell::new(EffectManager::default()), modal_fx: RefCell::new(None), title_fx: RefCell::new(Some(fx::coalesce((400, Interpolation::QuadOut)))), prev_modal_open: false, prev_page: Page::default(), prev_show_title: true, } } } impl UiState { pub fn help_scroll(&self) -> usize { self.help_scrolls[self.help_topic] } pub fn help_scroll_mut(&mut self) -> &mut usize { &mut self.help_scrolls[self.help_topic] } pub fn dict_scroll(&self) -> usize { self.dict_scrolls[self.dict_category] } pub fn dict_scroll_mut(&mut self) -> &mut usize { &mut self.dict_scrolls[self.dict_category] } pub fn flash(&mut self, msg: &str, duration_ms: u64, kind: FlashKind) { self.status_message = Some(msg.to_string()); self.flash_until = Some(Instant::now() + Duration::from_millis(duration_ms)); self.flash_kind = kind; } pub fn flash_kind(&self) -> Option { if self.is_flashing() { Some(self.flash_kind) } else { None } } pub fn set_status(&mut self, msg: String) { self.status_message = Some(msg); } pub fn clear_status(&mut self) { self.status_message = None; } pub fn is_flashing(&self) -> bool { self.flash_until .map(|t| Instant::now() < t) .unwrap_or(false) } }