use std::cell::RefCell;
use std::rc::{Rc, Weak};
use crate::key_event::KeyEvent;
pub trait FocusedTextFieldHandler {
fn handle_key(&self, event: &KeyEvent) -> bool;
fn insert_text(&self, text: &str);
fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize);
fn copy_selection(&self) -> Option<String>;
fn cut_selection(&self) -> Option<String>;
fn set_composition(&self, text: &str, cursor: Option<(usize, usize)>);
}
thread_local! {
static FOCUSED_FIELD: RefCell<Option<Weak<RefCell<bool>>>> = const { RefCell::new(None) };
static FOCUSED_HANDLER: RefCell<Option<Rc<dyn FocusedTextFieldHandler>>> = const { RefCell::new(None) };
}
pub fn request_focus(is_focused: Rc<RefCell<bool>>, handler: Rc<dyn FocusedTextFieldHandler>) {
FOCUSED_FIELD.with(|current| {
let mut current = current.borrow_mut();
if let Some(ref weak) = *current {
if let Some(old_focused) = weak.upgrade() {
*old_focused.borrow_mut() = false;
}
}
*is_focused.borrow_mut() = true;
*current = Some(Rc::downgrade(&is_focused));
});
FOCUSED_HANDLER.with(|h| {
*h.borrow_mut() = Some(handler);
});
crate::cursor_animation::start_cursor_blink();
crate::request_render_invalidation();
}
pub fn clear_focus() {
FOCUSED_FIELD.with(|current| {
let mut current = current.borrow_mut();
if let Some(ref weak) = *current {
if let Some(focused) = weak.upgrade() {
*focused.borrow_mut() = false;
}
}
*current = None;
});
FOCUSED_HANDLER.with(|h| {
*h.borrow_mut() = None;
});
crate::cursor_animation::stop_cursor_blink();
crate::request_render_invalidation();
}
pub fn has_focused_field() -> bool {
FOCUSED_FIELD.with(|current| {
let mut borrow = current.borrow_mut();
if let Some(ref weak) = *borrow {
if weak.upgrade().is_some() {
return true;
}
*borrow = None;
FOCUSED_HANDLER.with(|h| {
*h.borrow_mut() = None;
});
crate::cursor_animation::stop_cursor_blink();
}
false
})
}
pub fn dispatch_key_event(event: &KeyEvent) -> bool {
FOCUSED_HANDLER.with(|h| {
if let Some(handler) = h.borrow().as_ref() {
handler.handle_key(event)
} else {
false
}
})
}
pub fn dispatch_paste(text: &str) -> bool {
FOCUSED_HANDLER.with(|h| {
if let Some(handler) = h.borrow().as_ref() {
handler.insert_text(text);
true
} else {
false
}
})
}
pub fn dispatch_delete_surrounding(before_bytes: usize, after_bytes: usize) -> bool {
FOCUSED_HANDLER.with(|h| {
if let Some(handler) = h.borrow().as_ref() {
handler.delete_surrounding(before_bytes, after_bytes);
true
} else {
false
}
})
}
pub fn dispatch_copy() -> Option<String> {
FOCUSED_HANDLER.with(|h| {
if let Some(handler) = h.borrow().as_ref() {
handler.copy_selection()
} else {
None
}
})
}
pub fn dispatch_cut() -> Option<String> {
FOCUSED_HANDLER.with(|h| {
if let Some(handler) = h.borrow().as_ref() {
handler.cut_selection()
} else {
None
}
})
}
pub fn dispatch_ime_preedit(text: &str, cursor: Option<(usize, usize)>) -> bool {
FOCUSED_HANDLER.with(|h| {
if let Some(handler) = h.borrow().as_ref() {
handler.set_composition(text, cursor);
true
} else {
false
}
})
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
struct MockHandler;
impl FocusedTextFieldHandler for MockHandler {
fn handle_key(&self, _: &KeyEvent) -> bool {
false
}
fn insert_text(&self, _: &str) {}
fn delete_surrounding(&self, _: usize, _: usize) {}
fn copy_selection(&self) -> Option<String> {
None
}
fn cut_selection(&self) -> Option<String> {
None
}
fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {}
}
fn mock_handler() -> Rc<dyn FocusedTextFieldHandler> {
Rc::new(MockHandler)
}
#[test]
fn request_focus_sets_flag() {
let focus = Rc::new(RefCell::new(false));
request_focus(focus.clone(), mock_handler());
assert!(*focus.borrow());
clear_focus();
}
#[test]
fn request_focus_clears_previous() {
let focus1 = Rc::new(RefCell::new(false));
let focus2 = Rc::new(RefCell::new(false));
request_focus(focus1.clone(), mock_handler());
assert!(*focus1.borrow());
request_focus(focus2.clone(), mock_handler());
assert!(!*focus1.borrow()); assert!(*focus2.borrow()); clear_focus();
}
#[test]
fn clear_focus_unfocuses_current() {
let focus = Rc::new(RefCell::new(false));
request_focus(focus.clone(), mock_handler());
assert!(*focus.borrow());
clear_focus();
assert!(!*focus.borrow());
}
struct DeleteHandler {
last_delete: Cell<Option<(usize, usize)>>,
}
impl FocusedTextFieldHandler for DeleteHandler {
fn handle_key(&self, _: &KeyEvent) -> bool {
false
}
fn insert_text(&self, _: &str) {}
fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize) {
self.last_delete.set(Some((before_bytes, after_bytes)));
}
fn copy_selection(&self) -> Option<String> {
None
}
fn cut_selection(&self) -> Option<String> {
None
}
fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {}
}
#[test]
fn dispatch_delete_surrounding_calls_handler() {
let focus = Rc::new(RefCell::new(false));
let handler = Rc::new(DeleteHandler {
last_delete: Cell::new(None),
});
request_focus(focus, handler.clone());
assert!(dispatch_delete_surrounding(3, 1));
assert_eq!(handler.last_delete.get(), Some((3, 1)));
clear_focus();
}
}