use inset_embedder::{TextInputAction, TextInputConfiguration, TextInputType};
use winit::keyboard::{Key, ModifiersState, NamedKey};
use crate::keys;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct ActiveTextInput {
multiline: bool,
input_action: TextInputAction,
}
impl ActiveTextInput {
pub(crate) fn new(configuration: &TextInputConfiguration) -> Self {
ActiveTextInput {
multiline: configuration.input_type.index == TextInputType::MULTILINE.index,
input_action: configuration.input_action,
}
}
pub(crate) fn key_effect(
&self,
key: &Key,
text: Option<&str>,
modifiers: ModifiersState,
) -> TextInputKeyEffect {
if modifiers.control_key() || modifiers.super_key() {
return TextInputKeyEffect::None;
}
if *key == Key::Named(NamedKey::Enter) {
return self.newline_effect();
}
match keys::character_of(text) {
Some(text) => TextInputKeyEffect::Insert(text),
None => TextInputKeyEffect::None,
}
}
fn newline_effect(&self) -> TextInputKeyEffect {
let inserts = self.multiline && self.input_action == TextInputAction::Newline;
TextInputKeyEffect::Enter {
insert: inserts.then(|| "\n".to_owned()),
action: self.input_action,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum TextInputKeyEffect {
None,
Insert(String),
Enter {
insert: Option<String>,
action: TextInputAction,
},
}
#[cfg(test)]
mod tests {
use super::*;
fn single_line() -> ActiveTextInput {
ActiveTextInput {
multiline: false,
input_action: TextInputAction::Done,
}
}
fn multiline() -> ActiveTextInput {
ActiveTextInput {
multiline: true,
input_action: TextInputAction::Newline,
}
}
#[test]
fn a_character_key_inserts_its_text() {
let effect = single_line().key_effect(
&Key::Character("a".into()),
Some("a"),
ModifiersState::empty(),
);
assert_eq!(effect, TextInputKeyEffect::Insert("a".into()));
}
#[test]
fn shift_and_option_still_type() {
let effect = single_line().key_effect(
&Key::Character("å".into()),
Some("å"),
ModifiersState::SHIFT | ModifiersState::ALT,
);
assert_eq!(effect, TextInputKeyEffect::Insert("å".into()));
}
#[test]
fn command_and_control_chords_do_not_type() {
for modifiers in [ModifiersState::SUPER, ModifiersState::CONTROL] {
let effect =
single_line().key_effect(&Key::Character("c".into()), Some("c"), modifiers);
assert_eq!(effect, TextInputKeyEffect::None);
}
}
#[test]
fn control_characters_do_not_type() {
let effect = single_line().key_effect(
&Key::Named(NamedKey::Backspace),
Some("\u{8}"),
ModifiersState::empty(),
);
assert_eq!(effect, TextInputKeyEffect::None);
}
#[test]
fn enter_performs_the_action_and_only_a_multiline_field_gets_the_newline() {
let enter = Key::Named(NamedKey::Enter);
assert_eq!(
single_line().key_effect(&enter, Some("\r"), ModifiersState::empty()),
TextInputKeyEffect::Enter {
insert: None,
action: TextInputAction::Done,
}
);
assert_eq!(
multiline().key_effect(&enter, Some("\r"), ModifiersState::empty()),
TextInputKeyEffect::Enter {
insert: Some("\n".into()),
action: TextInputAction::Newline,
}
);
}
}