use std::{
cell::{Cell, RefCell},
rc::Rc,
};
pub trait PlatformTextInputHandler {
fn show_keyboard(&self);
fn hide_keyboard(&self);
}
pub(crate) struct PlatformTextInputState {
handler: RefCell<Option<Rc<dyn PlatformTextInputHandler>>>,
keyboard_requested: Cell<bool>,
keyboard_held: Cell<bool>,
}
impl PlatformTextInputState {
pub(crate) fn new() -> Self {
Self {
handler: RefCell::new(None),
keyboard_requested: Cell::new(false),
keyboard_held: Cell::new(false),
}
}
fn set_handler(&self, handler: Option<Rc<dyn PlatformTextInputHandler>>) {
*self.handler.borrow_mut() = handler;
self.keyboard_requested.set(false);
}
fn handler(&self) -> Option<Rc<dyn PlatformTextInputHandler>> {
self.handler.borrow().clone()
}
pub(crate) fn notify_focus_lost(&self) {
if !self.keyboard_requested.replace(false) {
return;
}
if let Some(handler) = self.handler() {
handler.hide_keyboard();
}
}
}
pub fn set_platform_text_input_handler(handler: Rc<dyn PlatformTextInputHandler>) {
crate::render_state::with_text_input_session(|state| state.set_handler(Some(handler)));
}
pub fn clear_platform_text_input_handler() {
crate::render_state::with_text_input_session(|state| state.set_handler(None));
}
pub(crate) fn notify_text_input_focus_gained() {
let handler = crate::render_state::with_text_input_session(|state| {
if state.keyboard_held.get() {
return None;
}
let handler = state.handler();
if handler.is_some() {
state.keyboard_requested.set(true);
}
handler
});
if let Some(handler) = handler {
handler.show_keyboard();
}
}
pub(crate) fn notify_text_input_focus_lost() {
crate::render_state::require_current_app_context("platform text input session access")
.text_input_session
.notify_focus_lost();
}
pub(crate) fn without_keyboard<R>(focus: impl FnOnce() -> R) -> R {
let held =
crate::render_state::with_text_input_session(|state| state.keyboard_held.replace(true));
let result = focus();
crate::render_state::with_text_input_session(|state| state.keyboard_held.set(held));
result
}
pub(crate) fn hide_software_keyboard() {
notify_text_input_focus_lost();
}
pub fn notify_app_paused() {
notify_text_input_focus_lost();
}
pub fn notify_app_resumed() -> bool {
let _ = crate::text_field_focus::has_focused_field();
false
}
#[cfg(test)]
#[path = "tests/text_input_session_tests.rs"]
mod tests;