use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use super::effect_event::use_effect_event;
use super::event::use_event;
pub fn use_keyboard<F>(handler: F)
where
F: Fn(KeyEvent) + Send + Sync + 'static,
{
let stable_handler = use_effect_event(move |key_event: KeyEvent| {
handler(key_event);
});
if let Some(Event::Key(key_event)) = use_event() {
stable_handler.call(key_event);
}
}
pub fn use_keyboard_press<F>(handler: F)
where
F: Fn(KeyEvent) + Send + Sync + 'static,
{
use_keyboard(move |key_event| {
if key_event.is_press() {
handler(key_event);
}
});
}
pub fn use_keyboard_shortcut<F>(key_code: KeyCode, modifiers: KeyModifiers, handler: F)
where
F: Fn() + Send + Sync + 'static,
{
use_keyboard_press(move |key_event| {
if key_event.code == key_code && key_event.modifiers == modifiers {
handler();
}
});
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::{clear_current_event, set_current_event};
use crate::fiber_tree::{FiberTree, clear_fiber_tree, set_fiber_tree, with_fiber_tree_mut};
use crossterm::event::{KeyEventKind, KeyEventState};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use std::sync::Arc;
use std::sync::atomic::{AtomicI32, Ordering};
static TEST_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
fn setup_test_fiber() -> crate::fiber::FiberId {
let mut tree = FiberTree::new();
let fiber_id = tree.mount(None, None);
tree.begin_render(fiber_id);
set_fiber_tree(tree);
fiber_id
}
fn cleanup_test() {
with_fiber_tree_mut(|tree| {
tree.end_render();
});
clear_fiber_tree();
clear_current_event();
}
fn create_key_event(code: KeyCode, modifiers: KeyModifiers, kind: KeyEventKind) -> Event {
Event::Key(KeyEvent {
code,
modifiers,
kind,
state: KeyEventState::NONE,
})
}
fn create_press_event(code: KeyCode, modifiers: KeyModifiers) -> Event {
create_key_event(code, modifiers, KeyEventKind::Press)
}
#[test]
fn test_use_keyboard_receives_key_event() {
let _lock = TEST_MUTEX.lock();
cleanup_test();
let _fiber_id = setup_test_fiber();
let call_count = Arc::new(AtomicI32::new(0));
let call_count_clone = call_count.clone();
let event = create_press_event(KeyCode::Char('a'), KeyModifiers::NONE);
set_current_event(Some(Arc::new(event)));
use_keyboard(move |key_event| {
assert_eq!(key_event.code, KeyCode::Char('a'));
call_count_clone.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(call_count.load(Ordering::SeqCst), 1);
cleanup_test();
}
#[test]
fn test_use_keyboard_ignores_non_key_events() {
let _lock = TEST_MUTEX.lock();
cleanup_test();
let _fiber_id = setup_test_fiber();
let call_count = Arc::new(AtomicI32::new(0));
let call_count_clone = call_count.clone();
let event = Event::Mouse(crossterm::event::MouseEvent {
kind: crossterm::event::MouseEventKind::Down(crossterm::event::MouseButton::Left),
column: 0,
row: 0,
modifiers: KeyModifiers::NONE,
});
set_current_event(Some(Arc::new(event)));
use_keyboard(move |_key_event| {
call_count_clone.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(call_count.load(Ordering::SeqCst), 0);
cleanup_test();
}
#[test]
fn test_use_keyboard_press_only_handles_press() {
let _lock = TEST_MUTEX.lock();
cleanup_test();
let fiber_id = setup_test_fiber();
let call_count = Arc::new(AtomicI32::new(0));
let press_event =
create_key_event(KeyCode::Char('a'), KeyModifiers::NONE, KeyEventKind::Press);
set_current_event(Some(Arc::new(press_event)));
let call_count_clone = call_count.clone();
use_keyboard_press(move |_| {
call_count_clone.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(call_count.load(Ordering::SeqCst), 1);
with_fiber_tree_mut(|tree| {
tree.end_render();
tree.begin_render(fiber_id);
});
clear_current_event();
let release_event = create_key_event(
KeyCode::Char('a'),
KeyModifiers::NONE,
KeyEventKind::Release,
);
set_current_event(Some(Arc::new(release_event)));
let call_count_clone2 = call_count.clone();
use_keyboard_press(move |_| {
call_count_clone2.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(call_count.load(Ordering::SeqCst), 1);
cleanup_test();
}
#[test]
fn test_use_keyboard_shortcut_matches_exact() {
let _lock = TEST_MUTEX.lock();
cleanup_test();
let _fiber_id = setup_test_fiber();
let call_count = Arc::new(AtomicI32::new(0));
let call_count_clone = call_count.clone();
let event = create_press_event(KeyCode::Char('s'), KeyModifiers::CONTROL);
set_current_event(Some(Arc::new(event)));
use_keyboard_shortcut(KeyCode::Char('s'), KeyModifiers::CONTROL, move || {
call_count_clone.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(call_count.load(Ordering::SeqCst), 1);
cleanup_test();
}
#[test]
fn test_use_keyboard_shortcut_ignores_wrong_modifier() {
let _lock = TEST_MUTEX.lock();
cleanup_test();
let _fiber_id = setup_test_fiber();
let call_count = Arc::new(AtomicI32::new(0));
let call_count_clone = call_count.clone();
let event = create_press_event(KeyCode::Char('s'), KeyModifiers::ALT);
set_current_event(Some(Arc::new(event)));
use_keyboard_shortcut(KeyCode::Char('s'), KeyModifiers::CONTROL, move || {
call_count_clone.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(call_count.load(Ordering::SeqCst), 0);
cleanup_test();
}
#[test]
fn test_use_keyboard_shortcut_ignores_wrong_key() {
let _lock = TEST_MUTEX.lock();
cleanup_test();
let _fiber_id = setup_test_fiber();
let call_count = Arc::new(AtomicI32::new(0));
let call_count_clone = call_count.clone();
let event = create_press_event(KeyCode::Char('a'), KeyModifiers::CONTROL);
set_current_event(Some(Arc::new(event)));
use_keyboard_shortcut(KeyCode::Char('s'), KeyModifiers::CONTROL, move || {
call_count_clone.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(call_count.load(Ordering::SeqCst), 0);
cleanup_test();
}
#[test]
fn test_use_keyboard_no_event() {
let _lock = TEST_MUTEX.lock();
cleanup_test();
let _fiber_id = setup_test_fiber();
let call_count = Arc::new(AtomicI32::new(0));
let call_count_clone = call_count.clone();
clear_current_event();
use_keyboard(move |_| {
call_count_clone.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(call_count.load(Ordering::SeqCst), 0);
cleanup_test();
}
#[test]
fn test_use_keyboard_shortcut_with_combined_modifiers() {
let _lock = TEST_MUTEX.lock();
cleanup_test();
let _fiber_id = setup_test_fiber();
let call_count = Arc::new(AtomicI32::new(0));
let call_count_clone = call_count.clone();
let event = create_press_event(
KeyCode::Char('p'),
KeyModifiers::CONTROL | KeyModifiers::SHIFT,
);
set_current_event(Some(Arc::new(event)));
use_keyboard_shortcut(
KeyCode::Char('p'),
KeyModifiers::CONTROL | KeyModifiers::SHIFT,
move || {
call_count_clone.fetch_add(1, Ordering::SeqCst);
},
);
assert_eq!(call_count.load(Ordering::SeqCst), 1);
cleanup_test();
}
}