use super::popup::PopupManager;
use crate::input::handler::{DeferredAction, InputContext, InputHandler, InputResult};
use crossterm::event::{KeyCode, KeyEvent};
impl InputHandler for PopupManager {
fn handle_key_event(&mut self, event: &KeyEvent, ctx: &mut InputContext) -> InputResult {
if !self.is_visible() {
return InputResult::Ignored;
}
match event.code {
KeyCode::Enter => {
ctx.defer(DeferredAction::ConfirmPopup);
InputResult::Consumed
}
KeyCode::Esc => {
ctx.defer(DeferredAction::ClosePopup);
InputResult::Consumed
}
KeyCode::Up | KeyCode::Char('k') if event.modifiers.is_empty() => {
if let Some(popup) = self.top_mut() {
popup.select_prev();
}
InputResult::Consumed
}
KeyCode::Down | KeyCode::Char('j') if event.modifiers.is_empty() => {
if let Some(popup) = self.top_mut() {
popup.select_next();
}
InputResult::Consumed
}
KeyCode::PageUp => {
if let Some(popup) = self.top_mut() {
popup.page_up();
}
InputResult::Consumed
}
KeyCode::PageDown => {
if let Some(popup) = self.top_mut() {
popup.page_down();
}
InputResult::Consumed
}
KeyCode::Tab if event.modifiers.is_empty() => {
if let Some(popup) = self.top_mut() {
popup.select_next();
}
InputResult::Consumed
}
KeyCode::BackTab => {
if let Some(popup) = self.top_mut() {
popup.select_prev();
}
InputResult::Consumed
}
_ => InputResult::Consumed,
}
}
fn is_modal(&self) -> bool {
self.is_visible()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::view::popup::{Popup, PopupListItem};
use crate::view::theme::Theme;
use crossterm::event::KeyModifiers;
fn key(code: KeyCode) -> KeyEvent {
KeyEvent::new(code, KeyModifiers::NONE)
}
fn create_popup_with_items(count: usize) -> PopupManager {
let theme = Theme::dark();
let items: Vec<PopupListItem> = (0..count)
.map(|i| PopupListItem::new(format!("Item {}", i)))
.collect();
let popup = Popup::list(items, &theme);
let mut manager = PopupManager::new();
manager.show(popup);
manager
}
#[test]
fn test_popup_navigation() {
let mut manager = create_popup_with_items(5);
let mut ctx = InputContext::new();
assert_eq!(
manager.top().unwrap().selected_item().unwrap().text,
"Item 0"
);
manager.handle_key_event(&key(KeyCode::Down), &mut ctx);
assert_eq!(
manager.top().unwrap().selected_item().unwrap().text,
"Item 1"
);
manager.handle_key_event(&key(KeyCode::Up), &mut ctx);
assert_eq!(
manager.top().unwrap().selected_item().unwrap().text,
"Item 0"
);
manager.handle_key_event(
&KeyEvent::new(KeyCode::Char('j'), KeyModifiers::NONE),
&mut ctx,
);
assert_eq!(
manager.top().unwrap().selected_item().unwrap().text,
"Item 1"
);
manager.handle_key_event(
&KeyEvent::new(KeyCode::Char('k'), KeyModifiers::NONE),
&mut ctx,
);
assert_eq!(
manager.top().unwrap().selected_item().unwrap().text,
"Item 0"
);
}
#[test]
fn test_popup_enter_confirms() {
let mut manager = create_popup_with_items(3);
let mut ctx = InputContext::new();
manager.handle_key_event(&key(KeyCode::Enter), &mut ctx);
assert!(ctx
.deferred_actions
.iter()
.any(|a| matches!(a, DeferredAction::ConfirmPopup)));
}
#[test]
fn test_popup_escape_cancels() {
let mut manager = create_popup_with_items(3);
let mut ctx = InputContext::new();
manager.handle_key_event(&key(KeyCode::Esc), &mut ctx);
assert!(ctx
.deferred_actions
.iter()
.any(|a| matches!(a, DeferredAction::ClosePopup)));
}
#[test]
fn test_popup_is_modal_when_visible() {
let mut manager = PopupManager::new();
assert!(!manager.is_modal());
let theme = Theme::dark();
manager.show(Popup::text(vec!["test".to_string()], &theme));
assert!(manager.is_modal());
}
#[test]
fn test_popup_ignored_when_empty() {
let mut manager = PopupManager::new();
let mut ctx = InputContext::new();
let result = manager.handle_key_event(&key(KeyCode::Down), &mut ctx);
assert_eq!(result, InputResult::Ignored);
}
}