ui/src/keyboard/mod.rs

503 lines
17 KiB
Rust
Raw Normal View History

2023-01-16 09:53:52 +00:00
use crate::prelude::*;
use anyhow::Result;
2025-04-05 08:24:07 +00:00
use ecs::*;
2023-01-16 09:53:52 +00:00
use std::any::Any;
use std::collections::HashMap;
use std::{
ops::Deref,
sync::{Arc, RwLock},
};
2024-05-18 05:54:06 +00:00
#[derive(Debug)]
2023-01-16 09:53:52 +00:00
enum KeyboardMode {
LowerCase,
UpperCase,
Specials,
}
pub struct Keyboard {
text_field_gui: Arc<GuiBuilder>,
text_field: Arc<TextField>,
lower_case: Arc<GuiBuilder>,
upper_case: Arc<GuiBuilder>,
specials: Arc<GuiBuilder>,
mode: Arc<RwLock<KeyboardMode>>,
2025-03-04 17:18:08 +00:00
decline_callback: Arc<RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>>,
accept_callback:
Arc<RwLock<Option<Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>>>>,
2023-01-16 09:53:52 +00:00
elements: HashMap<String, UiElement>,
}
impl Keyboard {
2025-03-05 06:59:38 +00:00
pub fn new(world: &mut World) -> Result<Arc<Self>> {
2023-01-16 09:53:52 +00:00
let text_field_gui: Arc<GuiBuilder> =
2025-03-05 06:59:38 +00:00
GuiBuilder::from_str(world, include_str!("text_field.xml"))?;
2023-01-16 09:53:52 +00:00
let text_field: Arc<TextField> = text_field_gui.element("field")?;
2025-03-04 13:25:35 +00:00
let decline_callback: Arc<
2025-03-04 17:18:08 +00:00
RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>,
2025-03-04 13:25:35 +00:00
> = Arc::new(RwLock::new(None));
2023-01-16 09:53:52 +00:00
let accept_callback: Arc<
2025-03-04 17:18:08 +00:00
RwLock<Option<Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>>>,
2023-01-16 09:53:52 +00:00
> = Arc::new(RwLock::new(None));
let mode = Arc::new(RwLock::new(KeyboardMode::UpperCase));
let (lower_case, upper_case, specials) = Self::setup(
2025-03-05 06:59:38 +00:00
world,
2023-01-16 09:53:52 +00:00
text_field.clone(),
&mode,
decline_callback.clone(),
accept_callback.clone(),
)?;
let mut elements = HashMap::new();
elements.insert(
"text".to_string(),
UiElement::TextField(Arc::downgrade(&text_field)),
);
Ok(Arc::new(Keyboard {
text_field_gui,
text_field,
lower_case,
upper_case,
specials,
mode,
decline_callback,
accept_callback,
elements,
}))
}
fn setup(
2025-03-05 06:59:38 +00:00
world: &mut World,
2023-01-16 09:53:52 +00:00
textfield: Arc<TextField>,
mode: &Arc<RwLock<KeyboardMode>>,
2025-03-04 17:18:08 +00:00
decline_callback: Arc<RwLock<Option<Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>>>>,
accept_callback: Arc<
RwLock<Option<Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>>>,
2025-03-04 13:25:35 +00:00
>,
2023-01-16 09:53:52 +00:00
) -> Result<(Arc<GuiBuilder>, Arc<GuiBuilder>, Arc<GuiBuilder>)> {
2025-03-05 06:59:38 +00:00
let lower_case = GuiBuilder::from_str(world, include_str!("lower_case.xml"))?;
let upper_case = GuiBuilder::from_str(world, include_str!("upper_case.xml"))?;
let specials = GuiBuilder::from_str(world, include_str!("specials.xml"))?;
2023-01-16 09:53:52 +00:00
// first row
Self::set_text_callback(&lower_case, "q", textfield.clone())?;
Self::set_text_callback(&lower_case, "w", textfield.clone())?;
Self::set_text_callback(&lower_case, "e", textfield.clone())?;
Self::set_text_callback(&lower_case, "r", textfield.clone())?;
Self::set_text_callback(&lower_case, "t", textfield.clone())?;
Self::set_text_callback(&lower_case, "y", textfield.clone())?;
Self::set_text_callback(&lower_case, "u", textfield.clone())?;
Self::set_text_callback(&lower_case, "i", textfield.clone())?;
Self::set_text_callback(&lower_case, "o", textfield.clone())?;
Self::set_text_callback(&lower_case, "p", textfield.clone())?;
// second row
Self::set_text_callback(&lower_case, "a", textfield.clone())?;
Self::set_text_callback(&lower_case, "s", textfield.clone())?;
Self::set_text_callback(&lower_case, "d", textfield.clone())?;
Self::set_text_callback(&lower_case, "f", textfield.clone())?;
Self::set_text_callback(&lower_case, "g", textfield.clone())?;
Self::set_text_callback(&lower_case, "h", textfield.clone())?;
Self::set_text_callback(&lower_case, "j", textfield.clone())?;
Self::set_text_callback(&lower_case, "k", textfield.clone())?;
Self::set_text_callback(&lower_case, "l", textfield.clone())?;
// third row
Self::set_text_callback(&lower_case, "z", textfield.clone())?;
Self::set_text_callback(&lower_case, "x", textfield.clone())?;
Self::set_text_callback(&lower_case, "c", textfield.clone())?;
Self::set_text_callback(&lower_case, "v", textfield.clone())?;
Self::set_text_callback(&lower_case, "b", textfield.clone())?;
Self::set_text_callback(&lower_case, "n", textfield.clone())?;
Self::set_text_callback(&lower_case, "m", textfield.clone())?;
// first row
Self::set_text_callback(&upper_case, "Q", textfield.clone())?;
Self::set_text_callback(&upper_case, "W", textfield.clone())?;
Self::set_text_callback(&upper_case, "E", textfield.clone())?;
Self::set_text_callback(&upper_case, "R", textfield.clone())?;
Self::set_text_callback(&upper_case, "T", textfield.clone())?;
Self::set_text_callback(&upper_case, "Y", textfield.clone())?;
Self::set_text_callback(&upper_case, "U", textfield.clone())?;
Self::set_text_callback(&upper_case, "I", textfield.clone())?;
Self::set_text_callback(&upper_case, "O", textfield.clone())?;
Self::set_text_callback(&upper_case, "P", textfield.clone())?;
// second row
Self::set_text_callback(&upper_case, "A", textfield.clone())?;
Self::set_text_callback(&upper_case, "S", textfield.clone())?;
Self::set_text_callback(&upper_case, "D", textfield.clone())?;
Self::set_text_callback(&upper_case, "F", textfield.clone())?;
Self::set_text_callback(&upper_case, "G", textfield.clone())?;
Self::set_text_callback(&upper_case, "H", textfield.clone())?;
Self::set_text_callback(&upper_case, "J", textfield.clone())?;
Self::set_text_callback(&upper_case, "K", textfield.clone())?;
Self::set_text_callback(&upper_case, "L", textfield.clone())?;
// third row
Self::set_text_callback(&upper_case, "Z", textfield.clone())?;
Self::set_text_callback(&upper_case, "X", textfield.clone())?;
Self::set_text_callback(&upper_case, "C", textfield.clone())?;
Self::set_text_callback(&upper_case, "V", textfield.clone())?;
Self::set_text_callback(&upper_case, "B", textfield.clone())?;
Self::set_text_callback(&upper_case, "N", textfield.clone())?;
Self::set_text_callback(&upper_case, "M", textfield.clone())?;
// first row
Self::set_text_callback(&specials, "1", textfield.clone())?;
Self::set_text_callback(&specials, "2", textfield.clone())?;
Self::set_text_callback(&specials, "3", textfield.clone())?;
Self::set_text_callback(&specials, "4", textfield.clone())?;
Self::set_text_callback(&specials, "5", textfield.clone())?;
Self::set_text_callback(&specials, "6", textfield.clone())?;
Self::set_text_callback(&specials, "7", textfield.clone())?;
Self::set_text_callback(&specials, "8", textfield.clone())?;
Self::set_text_callback(&specials, "9", textfield.clone())?;
Self::set_text_callback(&specials, "0", textfield.clone())?;
// second row
Self::set_text_callback(&specials, "!", textfield.clone())?;
Self::set_text_callback(&specials, "^", textfield.clone())?;
Self::set_text_callback(&specials, "\\", textfield.clone())?;
Self::set_text_callback(&specials, "*", textfield.clone())?;
Self::set_text_callback(&specials, "%", textfield.clone())?;
Self::set_text_callback(&specials, "~", textfield.clone())?;
Self::set_text_callback(&specials, "/", textfield.clone())?;
Self::set_text_callback(&specials, "(", textfield.clone())?;
Self::set_text_callback(&specials, ")", textfield.clone())?;
Self::set_text_callback(&specials, "=", textfield.clone())?;
// third row
Self::set_text_callback(&specials, "#", textfield.clone())?;
Self::set_text_callback(&specials, "+", textfield.clone())?;
Self::set_text_callback(&specials, "{", textfield.clone())?;
Self::set_text_callback(&specials, "}", textfield.clone())?;
Self::set_text_callback(&specials, ":", textfield.clone())?;
Self::set_text_callback(&specials, ";", textfield.clone())?;
Self::set_text_callback(&specials, "-", textfield.clone())?;
Self::set_text_callback(&specials, ",", textfield.clone())?;
Self::set_text_callback(&specials, ".", textfield.clone())?;
Self::set_text_callback(&specials, "_", textfield.clone())?;
2025-03-04 17:18:08 +00:00
let back = Box::new(move |world: &mut World| {
2023-01-16 09:53:52 +00:00
if let Some(callback) = decline_callback.read().unwrap().as_ref() {
2025-03-04 17:18:08 +00:00
(callback)(world)?;
2023-01-16 09:53:52 +00:00
}
Ok(())
});
let accept = {
let weak_textfield = Arc::downgrade(&textfield);
let weak_accept = Arc::downgrade(&accept_callback);
2025-03-04 17:18:08 +00:00
Box::new(move |world: &mut World| {
2023-01-16 09:53:52 +00:00
if let Some(textfield) = weak_textfield.upgrade() {
if let Some(accept_callback) = weak_accept.upgrade() {
if let Some(text) = textfield.text() {
if let Some(callback) = accept_callback.read().unwrap().as_ref() {
2025-03-04 17:18:08 +00:00
(callback)(world, &text)?;
2023-01-16 09:53:52 +00:00
}
}
}
}
Ok(())
})
};
let switch_to_upper = {
let mode = mode.clone();
let weak_lower = Arc::downgrade(&lower_case);
let weak_upper = Arc::downgrade(&upper_case);
2025-03-04 17:18:08 +00:00
Box::new(move |world: &mut World| {
2023-01-16 09:53:52 +00:00
if let Some(lower) = weak_lower.upgrade() {
if let Some(upper) = weak_upper.upgrade() {
let mut mode = mode.write().unwrap();
if let KeyboardMode::LowerCase = mode.deref() {
*mode = KeyboardMode::UpperCase;
2025-03-04 17:18:08 +00:00
2025-03-05 06:59:38 +00:00
lower.disable(world)?;
upper.enable(world)?;
2023-01-16 09:53:52 +00:00
}
}
}
Ok(())
})
};
let switch_to_special = {
let mode = mode.clone();
let weak_upper = Arc::downgrade(&upper_case);
let weak_specials = Arc::downgrade(&specials);
2025-03-04 17:18:08 +00:00
Box::new(move |world: &mut World| {
2023-01-16 09:53:52 +00:00
if let Some(specials) = weak_specials.upgrade() {
if let Some(upper) = weak_upper.upgrade() {
let mut mode = mode.write().unwrap();
if let KeyboardMode::UpperCase = mode.deref() {
*mode = KeyboardMode::Specials;
2025-03-04 17:18:08 +00:00
2025-03-05 06:59:38 +00:00
upper.disable(world)?;
specials.enable(world)?;
2023-01-16 09:53:52 +00:00
}
}
}
Ok(())
})
};
let switch_to_lower = {
let mode = mode.clone();
let weak_lower = Arc::downgrade(&lower_case);
let weak_specials = Arc::downgrade(&specials);
2025-03-04 17:18:08 +00:00
Box::new(move |world: &mut World| {
2023-01-16 09:53:52 +00:00
if let Some(lower) = weak_lower.upgrade() {
if let Some(specials) = weak_specials.upgrade() {
let mut mode = mode.write().unwrap();
if let KeyboardMode::Specials = mode.deref() {
*mode = KeyboardMode::LowerCase;
2025-03-04 17:18:08 +00:00
2025-03-05 06:59:38 +00:00
specials.disable(world)?;
lower.enable(world)?;
2023-01-16 09:53:52 +00:00
}
}
}
Ok(())
})
};
let space_bar = {
let weak_textfield = Arc::downgrade(&textfield);
2025-03-04 17:18:08 +00:00
Box::new(move |world: &mut World| {
2023-01-16 09:53:52 +00:00
if let Some(text_field) = weak_textfield.upgrade() {
2025-04-05 08:24:07 +00:00
text_field.add_letter(world.resources.get_mut()?, ' ')?;
2023-01-16 09:53:52 +00:00
}
Ok(())
})
};
lower_case.set_click_callbacks(vec![
("back", back.clone()),
("enter", accept.clone()),
("switch", switch_to_upper),
("space_bar", space_bar.clone()),
])?;
specials.set_click_callbacks(vec![
("back", back.clone()),
("enter", accept.clone()),
("switch", switch_to_lower),
("space_bar", space_bar.clone()),
])?;
upper_case.set_click_callbacks(vec![
("back", back),
("enter", accept),
("switch", switch_to_special),
("space_bar", space_bar),
])?;
Ok((lower_case, upper_case, specials))
}
fn set_text_callback(
builder: &Arc<GuiBuilder>,
letter: &str,
textfield: Arc<TextField>,
) -> Result<()> {
let button_ref: Arc<Button> = builder.element(letter)?;
let weak_textfield = Arc::downgrade(&textfield);
let weak_button = Arc::downgrade(&button_ref);
2025-03-04 17:18:08 +00:00
button_ref.set_callback(Box::new(move |world: &mut World| {
2023-01-16 09:53:52 +00:00
if let Some(textfield) = weak_textfield.upgrade() {
if let Some(button) = weak_button.upgrade() {
if let Some(text) = button.text()? {
2025-04-05 08:24:07 +00:00
textfield
.add_letter(world.resources.get_mut()?, text.as_bytes()[0] as char)?;
2023-01-16 09:53:52 +00:00
}
}
}
Ok(())
}));
Ok(())
}
}
impl TopGui for Keyboard {
2025-03-04 17:18:08 +00:00
fn decline(&self, world: &mut World) -> Result<()> {
2023-01-16 09:53:52 +00:00
if let Some(callback) = self.decline_callback.read().unwrap().as_ref() {
2025-03-04 17:18:08 +00:00
(callback)(world)?;
2023-01-16 09:53:52 +00:00
}
Ok(())
}
2025-03-04 17:18:08 +00:00
fn next_tab(&self, _world: &mut World, _: bool) -> Result<()> {
2023-01-16 09:53:52 +00:00
Ok(())
}
2025-03-04 17:18:08 +00:00
fn previous_tab(&self, world: &mut World, second_level: bool) -> Result<()> {
2023-01-16 09:53:52 +00:00
// abuse event
if !second_level {
2025-04-05 08:24:07 +00:00
self.text_field.remove_last(world.resources.get_mut()?)?;
2023-01-16 09:53:52 +00:00
}
Ok(())
}
}
impl Visibility for Keyboard {
fn visible(&self) -> bool {
let mode = self.mode.read().unwrap();
match mode.deref() {
KeyboardMode::LowerCase => self.lower_case.visible(),
KeyboardMode::UpperCase => self.upper_case.visible(),
KeyboardMode::Specials => self.specials.visible(),
}
}
2025-03-05 06:59:38 +00:00
fn set_visibility(&self, world: &mut World, visibility: bool) -> Result<()> {
2023-01-16 09:53:52 +00:00
let mode = self.mode.read().unwrap();
2024-05-18 05:54:06 +00:00
let gui = match mode.deref() {
KeyboardMode::LowerCase => &self.lower_case,
KeyboardMode::UpperCase => &self.upper_case,
KeyboardMode::Specials => &self.specials,
};
2023-01-16 09:53:52 +00:00
2024-05-18 05:54:06 +00:00
if visibility {
2025-03-05 06:59:38 +00:00
gui.enable(world)?;
self.text_field_gui.enable(world)?;
2024-05-18 05:54:06 +00:00
} else {
2025-03-05 06:59:38 +00:00
gui.disable(world)?;
self.text_field_gui.disable(world)?;
2024-05-18 05:54:06 +00:00
}
2023-01-16 09:53:52 +00:00
Ok(())
}
}
impl GuiElementTraits for Keyboard {
fn gridable(&self) -> Option<&dyn Gridable> {
None
}
fn visibility(&self) -> Option<&dyn Visibility> {
Some(self)
}
fn downcast<'a>(&'a self) -> Option<GuiElement<'a>> {
None
}
}
impl TopLevelGui for Keyboard {
fn gui_traits(&self) -> &dyn GuiElementTraits {
self
}
fn top_gui(&self) -> Option<&dyn TopGui> {
Some(self)
}
fn elements(&self) -> Option<&HashMap<String, UiElement>> {
Some(&self.elements)
}
fn functionality(&self) -> Option<&dyn Functionality> {
Some(self)
}
2025-03-05 06:59:38 +00:00
fn enable(&self, world: &mut World) -> Result<()> {
self.set_visibility(world, true)?;
2025-04-05 08:24:07 +00:00
self.text_field.focus_input(world.resources.get_mut()?)?;
2023-01-16 09:53:52 +00:00
Ok(())
}
2025-03-05 06:59:38 +00:00
fn disable(&self, world: &mut World) -> Result<()> {
self.set_visibility(world, false)?;
2023-01-16 09:53:52 +00:00
Ok(())
}
}
impl Functionality for Keyboard {
fn set_click_callbacks(
&self,
2025-03-04 17:18:08 +00:00
callbacks: Vec<(&str, Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>)>,
2023-01-16 09:53:52 +00:00
) -> Result<()> {
2025-03-04 17:18:08 +00:00
if let Some((_, callback)) = callbacks.into_iter().find(|(name, _)| *name == "decline") {
*self.decline_callback.write().unwrap() = Some(callback);
2023-01-16 09:53:52 +00:00
}
Ok(())
}
2025-03-04 17:18:08 +00:00
fn set_select_callbacks(
&self,
_: Vec<(
&str,
Box<dyn Fn(&mut World, bool) -> Result<()> + Send + Sync>,
)>,
) -> Result<()> {
2023-01-16 09:53:52 +00:00
Ok(())
}
fn set_custom_callbacks(
&self,
_: Vec<(
&str,
2025-03-04 17:18:08 +00:00
Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>,
2023-01-16 09:53:52 +00:00
)>,
) -> Result<()> {
Ok(())
}
fn set_vec_callbacks(
&self,
2025-03-04 17:18:08 +00:00
callbacks: Vec<(
&str,
Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>,
)>,
2023-01-16 09:53:52 +00:00
) -> Result<()> {
for (name, callback) in callbacks {
if name == "accept" {
*self.accept_callback.write().unwrap() = Some(callback);
}
}
Ok(())
}
}