use crate::model::categories::{self, CatEntry, CATEGORIES}; use crate::state::{DictFocus, UiState}; use CatEntry::{Category, Section}; pub fn toggle_focus(ui: &mut UiState) { ui.dict_focus = match ui.dict_focus { DictFocus::Categories => DictFocus::Words, DictFocus::Words => DictFocus::Categories, }; } pub fn select_category(ui: &mut UiState, index: usize) { if index < categories::category_count() { ui.dict_category = index; ui.dict_on_section = None; } } #[derive(Clone, Copy)] enum VisibleItem { Category(usize), Section(usize), } fn visible_items(collapsed: &[bool]) -> Vec { let mut result = Vec::new(); let mut section_idx = 0usize; let mut cat_idx = 0usize; let mut skipping = false; for entry in CATEGORIES.iter() { match entry { Section(_) => { let is_collapsed = collapsed.get(section_idx).copied().unwrap_or(false); if is_collapsed { result.push(VisibleItem::Section(section_idx)); } skipping = is_collapsed; section_idx += 1; } Category(_) => { if !skipping { result.push(VisibleItem::Category(cat_idx)); } cat_idx += 1; } } } result } fn find_current(items: &[VisibleItem], ui: &UiState) -> usize { if let Some(s) = ui.dict_on_section { items .iter() .position(|v| matches!(v, VisibleItem::Section(idx) if *idx == s)) .unwrap_or(0) } else { items .iter() .position(|v| matches!(v, VisibleItem::Category(idx) if *idx == ui.dict_category)) .unwrap_or(0) } } fn apply_selection(ui: &mut UiState, item: VisibleItem) { match item { VisibleItem::Category(idx) => { ui.dict_category = idx; ui.dict_on_section = None; } VisibleItem::Section(idx) => { ui.dict_on_section = Some(idx); } } } pub fn next_category(ui: &mut UiState) { let items = visible_items(&ui.dict_collapsed); if items.is_empty() { return; } let cur = find_current(&items, ui); let next = (cur + 1) % items.len(); apply_selection(ui, items[next]); } pub fn prev_category(ui: &mut UiState) { let items = visible_items(&ui.dict_collapsed); if items.is_empty() { return; } let cur = find_current(&items, ui); let next = (cur + items.len() - 1) % items.len(); apply_selection(ui, items[next]); } pub fn scroll_down(ui: &mut UiState, n: usize) { let s = ui.dict_scroll_mut(); *s = s.saturating_add(n); } pub fn scroll_up(ui: &mut UiState, n: usize) { let s = ui.dict_scroll_mut(); *s = s.saturating_sub(n); } pub fn activate_search(ui: &mut UiState) { ui.dict_search_active = true; ui.dict_focus = DictFocus::Words; } pub fn clear_search(ui: &mut UiState) { ui.dict_search_query.clear(); ui.dict_search_active = false; *ui.dict_scroll_mut() = 0; } pub fn search_input(ui: &mut UiState, c: char) { ui.dict_search_query.push(c); *ui.dict_scroll_mut() = 0; } pub fn search_backspace(ui: &mut UiState) { ui.dict_search_query.pop(); *ui.dict_scroll_mut() = 0; } pub fn search_confirm(ui: &mut UiState) { ui.dict_search_active = false; }