[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 {
|
||||
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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue