[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:
parent
554e6e7120
commit
f693a558c5
4 changed files with 60 additions and 39 deletions
|
@ -155,7 +155,8 @@ impl Default for MouseInput {
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Copy,
|
Copy,
|
||||||
Cut,
|
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),
|
Text(String),
|
||||||
Key {
|
Key {
|
||||||
key: Key,
|
key: Key,
|
||||||
|
@ -179,7 +180,8 @@ pub enum Key {
|
||||||
Logo,
|
Logo,
|
||||||
PageDown,
|
PageDown,
|
||||||
PageUp,
|
PageUp,
|
||||||
Return,
|
/// Enter/Return key
|
||||||
|
Enter,
|
||||||
Right,
|
Right,
|
||||||
Shift,
|
Shift,
|
||||||
// Space,
|
// Space,
|
||||||
|
|
|
@ -97,7 +97,18 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
ui.ctx().output().copied_text = text.clone();
|
ui.ctx().output().copied_text = text.clone();
|
||||||
}
|
}
|
||||||
Event::Text(text_to_insert) => {
|
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 } => {
|
Event::Key { key, pressed: true } => {
|
||||||
on_key_press(&mut cursor, text, *key);
|
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));
|
new_text.extend(char_it.skip(1));
|
||||||
*text = new_text;
|
*text = new_text;
|
||||||
}
|
}
|
||||||
|
Key::Enter => {} // handled earlier
|
||||||
Key::Home => {
|
Key::Home => {
|
||||||
// To start of paragraph:
|
// To start of paragraph:
|
||||||
let pos = line_col_from_char_idx(text, *cursor);
|
let pos = line_col_from_char_idx(text, *cursor);
|
||||||
|
|
|
@ -57,12 +57,8 @@ pub fn input_to_egui(
|
||||||
raw_input.mouse_pos = None;
|
raw_input.mouse_pos = None;
|
||||||
}
|
}
|
||||||
ReceivedCharacter(ch) => {
|
ReceivedCharacter(ch) => {
|
||||||
if !should_ignore_char(ch) {
|
if printable_char(ch) {
|
||||||
if ch == '\r' {
|
raw_input.events.push(Event::Text(ch.to_string()));
|
||||||
raw_input.events.push(Event::Text("\n".to_owned()));
|
|
||||||
} else {
|
|
||||||
raw_input.events.push(Event::Text(ch.to_string()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyboardInput { input, .. } => {
|
KeyboardInput { input, .. } => {
|
||||||
|
@ -116,24 +112,16 @@ pub fn input_to_egui(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_ignore_char(chr: char) -> bool {
|
/// Glium sends special keys (backspace, delete, F1, ...) as characters.
|
||||||
// Glium sends some keys as chars:
|
/// Ignore those.
|
||||||
match chr {
|
/// We also ignore '\r', '\n', '\t'.
|
||||||
'\u{7f}' | // backspace
|
/// Newlines are handled by the `Key::Enter` event.
|
||||||
'\u{f728}' | // delete
|
fn printable_char(chr: char) -> bool {
|
||||||
'\u{f700}' | // up
|
let is_in_private_use_area = '\u{e000}' <= chr && chr <= '\u{f8ff}'
|
||||||
'\u{f701}' | // down
|
|| '\u{f0000}' <= chr && chr <= '\u{ffffd}'
|
||||||
'\u{f702}' | // left
|
|| '\u{100000}' <= chr && chr <= '\u{10fffd}';
|
||||||
'\u{f703}' | // right
|
|
||||||
'\u{f729}' | // home
|
!is_in_private_use_area && !chr.is_ascii_control()
|
||||||
'\u{f72b}' | // end
|
|
||||||
'\u{f72c}' | // page up
|
|
||||||
'\u{f72d}' | // page down
|
|
||||||
'\u{f710}' | // print screen
|
|
||||||
'\u{f704}' | '\u{f705}' // F1, F2, ...
|
|
||||||
=> true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn translate_virtual_key_code(key: VirtualKeyCode) -> Option<egui::Key> {
|
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,
|
Right => Key::Right,
|
||||||
Down => Key::Down,
|
Down => Key::Down,
|
||||||
Back => Key::Backspace,
|
Back => Key::Backspace,
|
||||||
Return => Key::Return,
|
Return => Key::Enter,
|
||||||
// Space => Key::Space,
|
// Space => Key::Space,
|
||||||
Tab => Key::Tab,
|
Tab => Key::Tab,
|
||||||
|
|
||||||
|
|
|
@ -184,6 +184,19 @@ pub fn location_hash() -> Option<String> {
|
||||||
web_sys::window()?.location().hash().ok()
|
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> {
|
pub fn translate_key(key: &str) -> Option<egui::Key> {
|
||||||
match key {
|
match key {
|
||||||
"Alt" => Some(egui::Key::Alt),
|
"Alt" => Some(egui::Key::Alt),
|
||||||
|
@ -192,14 +205,14 @@ pub fn translate_key(key: &str) -> Option<egui::Key> {
|
||||||
"Delete" => Some(egui::Key::Delete),
|
"Delete" => Some(egui::Key::Delete),
|
||||||
"ArrowDown" => Some(egui::Key::Down),
|
"ArrowDown" => Some(egui::Key::Down),
|
||||||
"End" => Some(egui::Key::End),
|
"End" => Some(egui::Key::End),
|
||||||
"Escape" => Some(egui::Key::Escape),
|
"Esc" | "Escape" => Some(egui::Key::Escape),
|
||||||
"Home" => Some(egui::Key::Home),
|
"Home" => Some(egui::Key::Home),
|
||||||
"Help" => Some(egui::Key::Insert),
|
"Help" | "Insert" => Some(egui::Key::Insert),
|
||||||
"ArrowLeft" => Some(egui::Key::Left),
|
"ArrowLeft" => Some(egui::Key::Left),
|
||||||
"Meta" => Some(egui::Key::Logo),
|
"Meta" => Some(egui::Key::Logo),
|
||||||
"PageDown" => Some(egui::Key::PageDown),
|
"PageDown" => Some(egui::Key::PageDown),
|
||||||
"PageUp" => Some(egui::Key::PageUp),
|
"PageUp" => Some(egui::Key::PageUp),
|
||||||
"Enter" => Some(egui::Key::Return),
|
"Enter" => Some(egui::Key::Enter),
|
||||||
"ArrowRight" => Some(egui::Key::Right),
|
"ArrowRight" => Some(egui::Key::Right),
|
||||||
"Shift" => Some(egui::Key::Shift),
|
"Shift" => Some(egui::Key::Shift),
|
||||||
"Tab" => Some(egui::Key::Tab),
|
"Tab" => Some(egui::Key::Tab),
|
||||||
|
@ -247,17 +260,23 @@ fn install_document_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
||||||
// keydown
|
// keydown
|
||||||
let runner_ref = runner_ref.clone();
|
let runner_ref = runner_ref.clone();
|
||||||
let closure = Closure::wrap(Box::new(move |event: web_sys::KeyboardEvent| {
|
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 mut runner_lock = runner_ref.0.lock();
|
||||||
let key = event.key();
|
let key = event.key();
|
||||||
if let Some(key) = translate_key(&key) {
|
if !should_ignore_key(&key) {
|
||||||
runner_lock
|
if let Some(key) = translate_key(&key) {
|
||||||
.web_input
|
runner_lock
|
||||||
.events
|
.web_input
|
||||||
.push(egui::Event::Key { key, pressed: true });
|
.events
|
||||||
} else {
|
.push(egui::Event::Key { key, pressed: true });
|
||||||
runner_lock.web_input.events.push(egui::Event::Text(key));
|
} 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(_)>);
|
}) as Box<dyn FnMut(_)>);
|
||||||
document.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())?;
|
document.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())?;
|
||||||
closure.forget();
|
closure.forget();
|
||||||
|
|
Loading…
Reference in a new issue