use std::collections::HashSet;
use std::time::Instant;
use ratatui::widgets::{ListState, ScrollbarState};
use super::header::{Header, Tab};
pub struct App<'a> {
pub should_quit: bool,
pub header: Header<'a>,
pub components: Vec<String>,
pub components_scroll: usize,
pub hooks_scroll: usize,
pub components_scroll_state: ScrollbarState,
pub hooks_scroll_state: ScrollbarState,
pub components_list_state: ListState,
pub hooks_list_state: ListState,
pub terminal_width: u16,
pub icons_selected: usize,
pub components_search_query: String,
pub components_search_active: bool,
pub components_checked: HashSet<String>,
pub hooks_search_query: String,
pub hooks_search_active: bool,
pub hooks_checked: HashSet<String>,
pub show_popup: bool,
pub show_help_popup: bool,
pub last_click_time: Option<Instant>,
pub last_click_pos: Option<(u16, u16)>,
pub last_escape_time: Option<Instant>,
}
impl<'a> App<'a> {
pub fn new(title: &'a str, components: Vec<String>) -> Self {
App {
should_quit: false,
header: Header::new(title),
components,
components_scroll: 0,
hooks_scroll: 0,
components_scroll_state: ScrollbarState::default(),
hooks_scroll_state: ScrollbarState::default(),
components_list_state: ListState::default(),
hooks_list_state: ListState::default(),
terminal_width: 0,
icons_selected: 0,
components_search_query: String::new(),
components_search_active: false,
components_checked: HashSet::new(),
hooks_search_query: String::new(),
hooks_search_active: false,
hooks_checked: HashSet::new(),
show_popup: false,
show_help_popup: false,
last_click_time: None,
last_click_pos: None,
last_escape_time: None,
}
}
pub fn on_up(&mut self) {
match self.header.tabs.current {
Tab::Components => {
self.components_scroll = self.components_scroll.saturating_sub(1);
self.components_scroll_state = self.components_scroll_state.position(self.components_scroll);
}
Tab::Hooks => {
self.hooks_scroll = self.hooks_scroll.saturating_sub(1);
self.hooks_scroll_state = self.hooks_scroll_state.position(self.hooks_scroll);
}
Tab::Icons => {
self.icons_selected = self.icons_selected.saturating_sub(1);
}
Tab::Blocks | Tab::Settings => {}
}
}
pub fn on_down(&mut self) {
match self.header.tabs.current {
Tab::Components => {
self.components_scroll = self.components_scroll.saturating_add(1);
self.components_scroll_state = self.components_scroll_state.position(self.components_scroll);
}
Tab::Hooks => {
self.hooks_scroll = self.hooks_scroll.saturating_add(1);
self.hooks_scroll_state = self.hooks_scroll_state.position(self.hooks_scroll);
}
Tab::Icons => {
if self.icons_selected < 1 {
self.icons_selected += 1;
}
}
Tab::Blocks | Tab::Settings => {}
}
}
pub fn on_right(&mut self) {
self.header.tabs.next();
}
pub fn on_left(&mut self) {
self.header.tabs.previous();
}
pub fn on_key(&mut self, c: char) {
if c == 'q' {
self.should_quit = true;
}
}
pub fn on_tick(&mut self) {}
pub fn on_mouse_click(&mut self, column: u16, row: u16, terminal_width: u16) {
self.terminal_width = terminal_width;
if row < 3 {
self.header.handle_click(column, terminal_width);
}
}
pub fn toggle_components_search(&mut self) {
self.components_search_active = !self.components_search_active;
if !self.components_search_active {
self.components_search_query.clear();
self.components_scroll = 0;
}
}
pub fn components_search_input(&mut self, c: char) {
if self.components_search_active {
self.components_search_query.push(c);
self.components_scroll = 0;
}
}
pub fn components_search_backspace(&mut self) {
if self.components_search_active {
self.components_search_query.pop();
self.components_scroll = 0;
}
}
pub fn toggle_component_checkbox(&mut self, component: &str) {
if self.components_checked.contains(component) {
self.components_checked.remove(component);
} else {
self.components_checked.insert(component.to_string());
}
}
pub fn toggle_popup(&mut self) {
self.show_popup = !self.show_popup;
}
pub fn toggle_help_popup(&mut self) {
self.show_help_popup = !self.show_help_popup;
}
pub fn deselect_all_components(&mut self) {
self.components_checked.clear();
}
pub fn get_components_double_click_info(&self, column: u16, row: u16, terminal_width: u16) -> Option<usize> {
if matches!(self.header.tabs.current, Tab::Components) && !self.show_popup {
let left_panel_width = (terminal_width as f32 * 0.35) as u16;
if column <= left_panel_width && row > 6 {
let visual_row = (row - 7) as usize;
let viewport_offset = self.components_list_state.offset();
let item_index = visual_row + viewport_offset;
return Some(item_index);
}
}
None
}
pub fn toggle_hooks_search(&mut self) {
self.hooks_search_active = !self.hooks_search_active;
if !self.hooks_search_active {
self.hooks_search_query.clear();
self.hooks_scroll = 0;
}
}
pub fn hooks_search_input(&mut self, c: char) {
if self.hooks_search_active {
self.hooks_search_query.push(c);
self.hooks_scroll = 0;
}
}
pub fn hooks_search_backspace(&mut self) {
if self.hooks_search_active {
self.hooks_search_query.pop();
self.hooks_scroll = 0;
}
}
pub fn toggle_hook_checkbox(&mut self, hook: &str) {
if self.hooks_checked.contains(hook) {
self.hooks_checked.remove(hook);
} else {
self.hooks_checked.insert(hook.to_string());
}
}
pub fn deselect_all_hooks(&mut self) {
self.hooks_checked.clear();
}
pub fn get_hooks_double_click_info(&self, column: u16, row: u16, terminal_width: u16) -> Option<usize> {
if matches!(self.header.tabs.current, Tab::Hooks) && !self.show_popup {
let left_panel_width = (terminal_width as f32 * 0.35) as u16;
if column <= left_panel_width && row > 6 {
let visual_row = (row - 7) as usize;
let viewport_offset = self.hooks_list_state.offset();
let item_index = visual_row + viewport_offset;
return Some(item_index);
}
}
None
}
}