Web: AltGr and other named keys no longer type their names (#134) (#138)

In the browser, pressing AltGr while editing text on the canvas inserted
the word "AltGraph" into the story.

eframe's web backend (0.36.2, web/input.rs text_from_keyboard_event)
turns a keydown into a text event unless the key is on a fixed list of
names. Browsers report many more named keys than that list holds:
AltGraph, Dead, Unidentified, the media keys. Each one arrived as text.
Panel text fields are not affected, because a focused egui text field
takes its text from the browser's input events instead of keydown.

The web shell now drops a text event that is a key name (ASCII letters
and digits, starting with a capital, longer than one character) unless
an egui text field has the keyboard. One key press never types such a
word; a text field's input events can, so they are left alone.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
jahala 2026-10-10 06:13:59 +01:00 committed by GitHub
parent ce3deb651a
commit d323b4e702
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 34 additions and 1 deletions

View File

@ -104,7 +104,8 @@ impl eframe::App for WebShell {
self.app.logic(ctx);
}
fn raw_input_hook(&mut self, _ctx: &egui::Context, raw: &mut egui::RawInput) {
fn raw_input_hook(&mut self, ctx: &egui::Context, raw: &mut egui::RawInput) {
designcraft_ui_egui::drop_key_name_text(raw, ctx.text_edit_focused());
self.app.raw_input_hook(raw);
}

View File

@ -891,10 +891,42 @@ pub fn now_ms() -> f64 {
}
}
/// Is `text` the name of a key ("AltGraph", "Dead", "AudioVolumeUp") instead of typed text?
/// Browsers report a key press as a string: the character it types, or a name made of ASCII
/// letters and digits that starts with a capital. One key press never types such a word.
pub fn is_key_name(text: &str) -> bool {
text.len() > 1 && text.starts_with(|c: char| c.is_ascii_uppercase()) && text.chars().all(|c| c.is_ascii_alphanumeric())
}
/// Drop text events that are key names. eframe's web backend sends the name of every key it
/// doesn't know as text, so AltGr typed "AltGraph" into the story. `text_field_focused`: an
/// egui text field has the keyboard; its text comes from the browser's input events, which can
/// hold whole words, and is left alone.
pub fn drop_key_name_text(raw: &mut egui::RawInput, text_field_focused: bool) {
if !text_field_focused {
raw.events.retain(|e| !matches!(e, egui::Event::Text(t) if is_key_name(t)));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_names_sent_as_text_are_dropped() {
let text = |t: &str| egui::Event::Text(t.to_string());
let events = || {
vec![text("AltGraph"), text("é"), text("Dead"), text("A"), text("Unidentified"), text("ß"), text("AudioVolumeUp"), text("ab"), text("Æ")]
};
let mut raw = egui::RawInput { events: events(), ..Default::default() };
drop_key_name_text(&mut raw, false);
assert_eq!(raw.events, vec![text("é"), text("A"), text("ß"), text("ab"), text("Æ")]);
// A focused text field gets its text from input events, which can be whole words.
let mut raw = egui::RawInput { events: events(), ..Default::default() };
drop_key_name_text(&mut raw, true);
assert_eq!(raw.events, events());
}
#[test]
fn snap_view_uses_the_saved_switches() {
let mut ui = UiState::default();