[web/glium] better text input filtering

CapsLock and function keys (F1, F2, ...) are now ignored on web.

All special keys are ignored on glium.
This commit is contained in:
Emil Ernerfeldt 2020-07-30 11:54:42 +02:00
parent 554e6e7120
commit f693a558c5
4 changed files with 60 additions and 39 deletions

View file

@ -155,7 +155,8 @@ impl Default for MouseInput {
pub enum Event {
Copy,
Cut,
/// Text input, e.g. via keyboard or paste action
/// Text input, e.g. via keyboard or paste action.
/// Do not pass '\n', '\r' here, but send `Key::Enter` instead.
Text(String),
Key {
key: Key,
@ -179,7 +180,8 @@ pub enum Key {
Logo,
PageDown,
PageUp,
Return,
/// Enter/Return key
Enter,
Right,
Shift,
// Space,

View file

@ -97,7 +97,18 @@ impl<'t> Widget for TextEdit<'t> {
ui.ctx().output().copied_text = text.clone();
}
Event::Text(text_to_insert) => {
insert_text(&mut cursor, text, text_to_insert);
// newlines are handled by `Key::Enter`.
if text_to_insert != "\n" && text_to_insert != "\r" {
insert_text(&mut cursor, text, text_to_insert);
}
}
Event::Key {
key: Key::Enter,
pressed: true,
} => {
if multiline {
insert_text(&mut cursor, text, "\n");
}
}
Event::Key { key, pressed: true } => {
on_key_press(&mut cursor, text, *key);
@ -190,6 +201,7 @@ fn on_key_press(cursor: &mut usize, text: &mut String, key: Key) {
new_text.extend(char_it.skip(1));
*text = new_text;
}
Key::Enter => {} // handled earlier
Key::Home => {
// To start of paragraph:
let pos = line_col_from_char_idx(text, *cursor);

View file

@ -57,12 +57,8 @@ pub fn input_to_egui(
raw_input.mouse_pos = None;
}
ReceivedCharacter(ch) => {
if !should_ignore_char(ch) {
if ch == '\r' {
raw_input.events.push(Event::Text("\n".to_owned()));
} else {
raw_input.events.push(Event::Text(ch.to_string()));
}
if printable_char(ch) {
raw_input.events.push(Event::Text(ch.to_string()));
}
}
KeyboardInput { input, .. } => {
@ -116,24 +112,16 @@ pub fn input_to_egui(
}
}
fn should_ignore_char(chr: char) -> bool {
// Glium sends some keys as chars:
match chr {
'\u{7f}' | // backspace
'\u{f728}' | // delete
'\u{f700}' | // up
'\u{f701}' | // down
'\u{f702}' | // left
'\u{f703}' | // right
'\u{f729}' | // home
'\u{f72b}' | // end
'\u{f72c}' | // page up
'\u{f72d}' | // page down
'\u{f710}' | // print screen
'\u{f704}' | '\u{f705}' // F1, F2, ...
=> true,
_ => false,
}
/// Glium sends special keys (backspace, delete, F1, ...) as characters.
/// Ignore those.
/// We also ignore '\r', '\n', '\t'.
/// Newlines are handled by the `Key::Enter` event.
fn printable_char(chr: char) -> bool {
let is_in_private_use_area = '\u{e000}' <= chr && chr <= '\u{f8ff}'
|| '\u{f0000}' <= chr && chr <= '\u{ffffd}'
|| '\u{100000}' <= chr && chr <= '\u{10fffd}';
!is_in_private_use_area && !chr.is_ascii_control()
}
pub fn translate_virtual_key_code(key: VirtualKeyCode) -> Option<egui::Key> {
@ -152,7 +140,7 @@ pub fn translate_virtual_key_code(key: VirtualKeyCode) -> Option<egui::Key> {
Right => Key::Right,
Down => Key::Down,
Back => Key::Backspace,
Return => Key::Return,
Return => Key::Enter,
// Space => Key::Space,
Tab => Key::Tab,

View file

@ -184,6 +184,19 @@ pub fn location_hash() -> Option<String> {
web_sys::window()?.location().hash().ok()
}
/// Web sends all all keys as strings, so it is up to us to figure out if it is
/// a real text input or the name of a key.
fn should_ignore_key(key: &str) -> bool {
let is_function_key = key.starts_with("F") && key.len() > 1;
is_function_key
|| matches!(
key,
"CapsLock" | "ContextMenu" | "NumLock" | "Pause" | "ScrollLock"
)
}
/// Web sends all all keys as strings, so it is up to us to figure out if it is
/// a real text input or the name of a key.
pub fn translate_key(key: &str) -> Option<egui::Key> {
match key {
"Alt" => Some(egui::Key::Alt),
@ -192,14 +205,14 @@ pub fn translate_key(key: &str) -> Option<egui::Key> {
"Delete" => Some(egui::Key::Delete),
"ArrowDown" => Some(egui::Key::Down),
"End" => Some(egui::Key::End),
"Escape" => Some(egui::Key::Escape),
"Esc" | "Escape" => Some(egui::Key::Escape),
"Home" => Some(egui::Key::Home),
"Help" => Some(egui::Key::Insert),
"Help" | "Insert" => Some(egui::Key::Insert),
"ArrowLeft" => Some(egui::Key::Left),
"Meta" => Some(egui::Key::Logo),
"PageDown" => Some(egui::Key::PageDown),
"PageUp" => Some(egui::Key::PageUp),
"Enter" => Some(egui::Key::Return),
"Enter" => Some(egui::Key::Enter),
"ArrowRight" => Some(egui::Key::Right),
"Shift" => Some(egui::Key::Shift),
"Tab" => Some(egui::Key::Tab),
@ -247,17 +260,23 @@ fn install_document_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
// keydown
let runner_ref = runner_ref.clone();
let closure = Closure::wrap(Box::new(move |event: web_sys::KeyboardEvent| {
if event.is_composing() || event.key_code() == 229 {
// https://www.fxsitecompat.dev/en-CA/docs/2018/keydown-and-keyup-events-are-now-fired-during-ime-composition/
return;
}
let mut runner_lock = runner_ref.0.lock();
let key = event.key();
if let Some(key) = translate_key(&key) {
runner_lock
.web_input
.events
.push(egui::Event::Key { key, pressed: true });
} else {
runner_lock.web_input.events.push(egui::Event::Text(key));
if !should_ignore_key(&key) {
if let Some(key) = translate_key(&key) {
runner_lock
.web_input
.events
.push(egui::Event::Key { key, pressed: true });
} else {
runner_lock.web_input.events.push(egui::Event::Text(key));
}
runner_lock.needs_repaint = true;
}
runner_lock.needs_repaint = true;
}) as Box<dyn FnMut(_)>);
document.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())?;
closure.forget();