Add modifier keys and implement moving cursors one word at a time
This commit is contained in:
parent
7494026139
commit
c4ed507d63
6 changed files with 234 additions and 106 deletions
|
@ -16,6 +16,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
* Pressing enter in a single-line `TextEdit` will now surrender keyboard focus for it.
|
* Pressing enter in a single-line `TextEdit` will now surrender keyboard focus for it.
|
||||||
* You must now be explicit when creating a `TextEdit` if you want it to be singeline or multiline.
|
* You must now be explicit when creating a `TextEdit` if you want it to be singeline or multiline.
|
||||||
* Improved automatic `Id` generation, making `Id` clashes less likely.
|
* Improved automatic `Id` generation, making `Id` clashes less likely.
|
||||||
|
* Egui now requires modifier key state from the integration
|
||||||
|
* Renamed and removed some keys in the `Key` enum.
|
||||||
|
|
||||||
### Fixed 🐛
|
### Fixed 🐛
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ impl Default for MouseInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An input event. Only covers events used by Egui.
|
/// An input event. Only covers events used by Egui.
|
||||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Copy,
|
Copy,
|
||||||
Cut,
|
Cut,
|
||||||
|
@ -161,33 +161,45 @@ pub enum Event {
|
||||||
Key {
|
Key {
|
||||||
key: Key,
|
key: Key,
|
||||||
pressed: bool,
|
pressed: bool,
|
||||||
|
modifiers: Modifiers,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Keyboard key name. Only covers keys used by Egui.
|
/// State of the modifier keys. These must be fed to Egui.
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct Modifiers {
|
||||||
|
/// Either of the alt keys are down (option ⌥ on Mac)
|
||||||
|
pub alt: bool,
|
||||||
|
/// Either of the control keys are down
|
||||||
|
pub ctrl: bool,
|
||||||
|
/// Either of the shift keys are down
|
||||||
|
pub shift: bool,
|
||||||
|
/// The Mac ⌘ Command key. Should always be set to `false` on other platforms.
|
||||||
|
pub mac_cmd: bool,
|
||||||
|
/// On Mac, this should be set whenever one of the ⌘ Command keys are down (same as `mac_cmd`).
|
||||||
|
/// On Windows and Linux, set this to the same value as `ctrl`.
|
||||||
|
/// This is so that Egui can, for instance, select all text by checking for `command + A`
|
||||||
|
/// and it will work on both Mac and Windows.
|
||||||
|
pub command: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Keyboard key name. Only covers keys used by Egui (mostly for text editing).
|
||||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
pub enum Key {
|
pub enum Key {
|
||||||
Alt,
|
ArrowDown,
|
||||||
|
ArrowLeft,
|
||||||
|
ArrowRight,
|
||||||
|
ArrowUp,
|
||||||
Backspace,
|
Backspace,
|
||||||
Control,
|
|
||||||
Delete,
|
Delete,
|
||||||
Down,
|
|
||||||
End,
|
End,
|
||||||
|
Enter,
|
||||||
Escape,
|
Escape,
|
||||||
Home,
|
Home,
|
||||||
Insert,
|
Insert,
|
||||||
Left,
|
|
||||||
/// Windows key or Mac Command key
|
|
||||||
Logo,
|
|
||||||
PageDown,
|
PageDown,
|
||||||
PageUp,
|
PageUp,
|
||||||
/// Enter/Return key
|
|
||||||
Enter,
|
|
||||||
Right,
|
|
||||||
Shift,
|
|
||||||
// Space,
|
|
||||||
Tab,
|
Tab,
|
||||||
Up,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputState {
|
impl InputState {
|
||||||
|
@ -227,7 +239,8 @@ impl InputState {
|
||||||
event,
|
event,
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key,
|
key,
|
||||||
pressed: true
|
pressed: true,
|
||||||
|
..
|
||||||
} if *key == desired_key
|
} if *key == desired_key
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -240,7 +253,8 @@ impl InputState {
|
||||||
event,
|
event,
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key,
|
key,
|
||||||
pressed: false
|
pressed: false,
|
||||||
|
..
|
||||||
} if *key == desired_key
|
} if *key == desired_key
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -621,6 +621,39 @@ impl Galley {
|
||||||
column: self.rows[cursor.rcursor.row].char_count_excluding_newline(),
|
column: self.rows[cursor.rcursor.row].char_count_excluding_newline(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cursor_next_word(&self, cursor: &Cursor) -> Cursor {
|
||||||
|
self.from_ccursor(CCursor {
|
||||||
|
index: next_word(self.text.chars(), cursor.ccursor.index),
|
||||||
|
prefer_next_row: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cursor_previous_word(&self, cursor: &Cursor) -> Cursor {
|
||||||
|
let num_chars = self.text.chars().count();
|
||||||
|
self.from_ccursor(CCursor {
|
||||||
|
index: num_chars - next_word(self.text.chars().rev(), num_chars - cursor.ccursor.index),
|
||||||
|
prefer_next_row: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_word(it: impl Iterator<Item = char>, mut index: usize) -> usize {
|
||||||
|
let mut it = it.skip(index);
|
||||||
|
if let Some(_first) = it.next() {
|
||||||
|
index += 1;
|
||||||
|
|
||||||
|
if let Some(second) = it.next() {
|
||||||
|
index += 1;
|
||||||
|
for next in it {
|
||||||
|
if next.is_alphabetic() != second.is_alphabetic() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
@ -176,7 +176,7 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
} else {
|
} else {
|
||||||
Sense::nothing()
|
Sense::nothing()
|
||||||
};
|
};
|
||||||
let response = ui.interact(rect, id, sense); // TODO: implement drag-select
|
let response = ui.interact(rect, id, sense);
|
||||||
|
|
||||||
if response.clicked && enabled {
|
if response.clicked && enabled {
|
||||||
ui.memory().request_kb_focus(id);
|
ui.memory().request_kb_focus(id);
|
||||||
|
@ -228,6 +228,7 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Enter,
|
key: Key::Enter,
|
||||||
pressed: true,
|
pressed: true,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
if multiline {
|
if multiline {
|
||||||
let mut ccursor = cursor.ccursor;
|
let mut ccursor = cursor.ccursor;
|
||||||
|
@ -242,13 +243,16 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Escape,
|
key: Key::Escape,
|
||||||
pressed: true,
|
pressed: true,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
ui.memory().surrender_kb_focus(id);
|
ui.memory().surrender_kb_focus(id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Event::Key { key, pressed: true } => {
|
Event::Key {
|
||||||
on_key_press(&mut cursor, text, &galley, *key)
|
key,
|
||||||
}
|
pressed: true,
|
||||||
|
modifiers,
|
||||||
|
} => on_key_press(&mut cursor, text, &galley, *key, modifiers),
|
||||||
Event::Key { .. } => None,
|
Event::Key { .. } => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -334,18 +338,35 @@ fn on_key_press(
|
||||||
text: &mut String,
|
text: &mut String,
|
||||||
galley: &Galley,
|
galley: &Galley,
|
||||||
key: Key,
|
key: Key,
|
||||||
|
modifiers: &Modifiers,
|
||||||
) -> Option<CCursor> {
|
) -> Option<CCursor> {
|
||||||
|
// TODO: cursor position preview on mouse hover
|
||||||
|
// TODO: drag-select
|
||||||
|
// TODO: double-click to select whole word
|
||||||
|
// TODO: triple-click to select whole paragraph
|
||||||
|
// TODO: drag selected text to either move or clone (ctrl on windows, alt on mac)
|
||||||
|
// TODO: ctrl-U to clear paragraph before the cursor
|
||||||
|
// TODO: ctrl-W to delete previous word
|
||||||
|
// TODO: alt/ctrl + backspace to delete previous word (alt on mac, ctrl on windows)
|
||||||
|
// TODO: alt/ctrl + delete to delete next word (alt on mac, ctrl on windows)
|
||||||
|
// TODO: cmd-A to select all
|
||||||
|
// TODO: shift modifier to only move half of the cursor to select things
|
||||||
|
|
||||||
match key {
|
match key {
|
||||||
Key::Backspace if cursor.ccursor.index > 0 => {
|
Key::Backspace => {
|
||||||
*cursor = galley.from_ccursor(cursor.ccursor - 1);
|
if cursor.ccursor.index > 0 {
|
||||||
let mut char_it = text.chars();
|
*cursor = galley.from_ccursor(cursor.ccursor - 1);
|
||||||
let mut new_text = String::with_capacity(text.capacity());
|
let mut char_it = text.chars();
|
||||||
for _ in 0..cursor.ccursor.index {
|
let mut new_text = String::with_capacity(text.capacity());
|
||||||
new_text.push(char_it.next().unwrap())
|
for _ in 0..cursor.ccursor.index {
|
||||||
|
new_text.push(char_it.next().unwrap())
|
||||||
|
}
|
||||||
|
new_text.extend(char_it.skip(1));
|
||||||
|
*text = new_text;
|
||||||
|
Some(cursor.ccursor)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
new_text.extend(char_it.skip(1));
|
|
||||||
*text = new_text;
|
|
||||||
Some(cursor.ccursor)
|
|
||||||
}
|
}
|
||||||
Key::Delete => {
|
Key::Delete => {
|
||||||
let mut char_it = text.chars();
|
let mut char_it = text.chars();
|
||||||
|
@ -357,32 +378,69 @@ fn on_key_press(
|
||||||
*text = new_text;
|
*text = new_text;
|
||||||
Some(cursor.ccursor)
|
Some(cursor.ccursor)
|
||||||
}
|
}
|
||||||
Key::Enter => unreachable!("Should have been handled earlier"),
|
|
||||||
|
Key::ArrowLeft => {
|
||||||
|
if modifiers.alt || modifiers.ctrl {
|
||||||
|
// alt on mac, ctrl on windows
|
||||||
|
*cursor = galley.cursor_previous_word(cursor);
|
||||||
|
} else if modifiers.mac_cmd {
|
||||||
|
*cursor = galley.cursor_begin_of_row(cursor);
|
||||||
|
} else {
|
||||||
|
*cursor = galley.cursor_left_one_character(cursor);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Key::ArrowRight => {
|
||||||
|
if modifiers.alt || modifiers.ctrl {
|
||||||
|
// alt on mac, ctrl on windows
|
||||||
|
*cursor = galley.cursor_next_word(cursor);
|
||||||
|
} else if modifiers.mac_cmd {
|
||||||
|
*cursor = galley.cursor_end_of_row(cursor);
|
||||||
|
} else {
|
||||||
|
*cursor = galley.cursor_right_one_character(cursor);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Key::ArrowUp => {
|
||||||
|
if modifiers.command {
|
||||||
|
// mac and windows behavior
|
||||||
|
*cursor = Cursor::default();
|
||||||
|
} else {
|
||||||
|
*cursor = galley.cursor_up_one_row(cursor);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Key::ArrowDown => {
|
||||||
|
if modifiers.command {
|
||||||
|
// mac and windows behavior
|
||||||
|
*cursor = galley.end();
|
||||||
|
} else {
|
||||||
|
*cursor = galley.cursor_down_one_row(cursor);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
Key::Home => {
|
Key::Home => {
|
||||||
*cursor = galley.cursor_begin_of_row(cursor);
|
if modifiers.ctrl {
|
||||||
|
// windows behavior
|
||||||
|
*cursor = Cursor::default();
|
||||||
|
} else {
|
||||||
|
*cursor = galley.cursor_begin_of_row(cursor);
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Key::End => {
|
Key::End => {
|
||||||
*cursor = galley.cursor_end_of_row(cursor);
|
if modifiers.ctrl {
|
||||||
|
// windows behavior
|
||||||
|
*cursor = galley.end();
|
||||||
|
} else {
|
||||||
|
*cursor = galley.cursor_end_of_row(cursor);
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Key::Left => {
|
|
||||||
*cursor = galley.cursor_left_one_character(cursor);
|
Key::Enter | Key::Escape => unreachable!("Handled outside this function"),
|
||||||
None
|
|
||||||
}
|
Key::Insert | Key::PageDown | Key::PageUp | Key::Tab => None,
|
||||||
Key::Right => {
|
|
||||||
*cursor = galley.cursor_right_one_character(cursor);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Key::Up => {
|
|
||||||
*cursor = galley.cursor_up_one_row(cursor);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Key::Down => {
|
|
||||||
*cursor = galley.cursor_down_one_row(cursor);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,7 @@ pub use clipboard::ClipboardContext; // TODO: remove
|
||||||
|
|
||||||
pub struct GliumInputState {
|
pub struct GliumInputState {
|
||||||
raw: egui::RawInput,
|
raw: egui::RawInput,
|
||||||
|
modifiers: egui::Modifiers,
|
||||||
/// Command modifier key.
|
|
||||||
/// Mac command key on Mac, ctrl on Window/Linux.
|
|
||||||
cmd: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GliumInputState {
|
impl GliumInputState {
|
||||||
|
@ -33,7 +30,7 @@ impl GliumInputState {
|
||||||
pixels_per_point: Some(pixels_per_point),
|
pixels_per_point: Some(pixels_per_point),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
cmd: false,
|
modifiers: Default::default(), // cmd: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,40 +60,48 @@ pub fn input_to_egui(
|
||||||
input_state.raw.mouse_pos = None;
|
input_state.raw.mouse_pos = None;
|
||||||
}
|
}
|
||||||
ReceivedCharacter(ch) => {
|
ReceivedCharacter(ch) => {
|
||||||
if !input_state.cmd && printable_char(ch) {
|
if printable_char(ch) && !input_state.modifiers.ctrl && !input_state.modifiers.mac_cmd {
|
||||||
input_state.raw.events.push(Event::Text(ch.to_string()));
|
input_state.raw.events.push(Event::Text(ch.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyboardInput { input, .. } => {
|
KeyboardInput { input, .. } => {
|
||||||
if let Some(virtual_keycode) = input.virtual_keycode {
|
if let Some(keycode) = input.virtual_keycode {
|
||||||
let is_command_key = if cfg!(target_os = "macos") {
|
let pressed = input.state == glutin::event::ElementState::Pressed;
|
||||||
matches!(virtual_keycode, VirtualKeyCode::LWin | VirtualKeyCode::RWin)
|
|
||||||
} else {
|
|
||||||
matches!(
|
|
||||||
virtual_keycode,
|
|
||||||
VirtualKeyCode::LControl | VirtualKeyCode::RControl
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_command_key {
|
if matches!(keycode, VirtualKeyCode::LAlt | VirtualKeyCode::RAlt) {
|
||||||
input_state.cmd = input.state == glutin::event::ElementState::Pressed;
|
input_state.modifiers.alt = pressed;
|
||||||
|
}
|
||||||
|
if matches!(keycode, VirtualKeyCode::LControl | VirtualKeyCode::RControl) {
|
||||||
|
input_state.modifiers.ctrl = pressed;
|
||||||
|
if !cfg!(target_os = "macos") {
|
||||||
|
input_state.modifiers.command = pressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matches!(keycode, VirtualKeyCode::LShift | VirtualKeyCode::RShift) {
|
||||||
|
input_state.modifiers.shift = pressed;
|
||||||
|
}
|
||||||
|
if cfg!(target_os = "macos")
|
||||||
|
&& matches!(keycode, VirtualKeyCode::LWin | VirtualKeyCode::RWin)
|
||||||
|
{
|
||||||
|
input_state.modifiers.mac_cmd = pressed;
|
||||||
|
input_state.modifiers.command = pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.state == glutin::event::ElementState::Pressed {
|
if pressed {
|
||||||
if cfg!(target_os = "macos")
|
if cfg!(target_os = "macos")
|
||||||
&& input_state.cmd
|
&& input_state.modifiers.mac_cmd
|
||||||
&& virtual_keycode == VirtualKeyCode::Q
|
&& keycode == VirtualKeyCode::Q
|
||||||
{
|
{
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VirtualKeyCode::Paste etc in winit are broken/untrustworthy,
|
// VirtualKeyCode::Paste etc in winit are broken/untrustworthy,
|
||||||
// so we detect these things manually:
|
// so we detect these things manually:
|
||||||
if input_state.cmd && virtual_keycode == VirtualKeyCode::X {
|
if input_state.modifiers.command && keycode == VirtualKeyCode::X {
|
||||||
input_state.raw.events.push(Event::Cut);
|
input_state.raw.events.push(Event::Cut);
|
||||||
} else if input_state.cmd && virtual_keycode == VirtualKeyCode::C {
|
} else if input_state.modifiers.command && keycode == VirtualKeyCode::C {
|
||||||
input_state.raw.events.push(Event::Copy);
|
input_state.raw.events.push(Event::Copy);
|
||||||
} else if input_state.cmd && virtual_keycode == VirtualKeyCode::V {
|
} else if input_state.modifiers.command && keycode == VirtualKeyCode::V {
|
||||||
if let Some(clipboard) = clipboard {
|
if let Some(clipboard) = clipboard {
|
||||||
match clipboard.get_contents() {
|
match clipboard.get_contents() {
|
||||||
Ok(contents) => {
|
Ok(contents) => {
|
||||||
|
@ -107,10 +112,11 @@ pub fn input_to_egui(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(key) = translate_virtual_key_code(virtual_keycode) {
|
} else if let Some(key) = translate_virtual_key_code(keycode) {
|
||||||
input_state.raw.events.push(Event::Key {
|
input_state.raw.events.push(Event::Key {
|
||||||
key,
|
key,
|
||||||
pressed: input.state == glutin::event::ElementState::Pressed,
|
pressed,
|
||||||
|
modifiers: input_state.modifiers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,20 +163,13 @@ pub fn translate_virtual_key_code(key: VirtualKeyCode) -> Option<egui::Key> {
|
||||||
End => Key::End,
|
End => Key::End,
|
||||||
PageDown => Key::PageDown,
|
PageDown => Key::PageDown,
|
||||||
PageUp => Key::PageUp,
|
PageUp => Key::PageUp,
|
||||||
Left => Key::Left,
|
Left => Key::ArrowLeft,
|
||||||
Up => Key::Up,
|
Up => Key::ArrowUp,
|
||||||
Right => Key::Right,
|
Right => Key::ArrowRight,
|
||||||
Down => Key::Down,
|
Down => Key::ArrowDown,
|
||||||
Back => Key::Backspace,
|
Back => Key::Backspace,
|
||||||
Return => Key::Enter,
|
Return => Key::Enter,
|
||||||
// Space => Key::Space,
|
|
||||||
Tab => Key::Tab,
|
Tab => Key::Tab,
|
||||||
|
|
||||||
LAlt | RAlt => Key::Alt,
|
|
||||||
LShift | RShift => Key::Shift,
|
|
||||||
LControl | RControl => Key::Control,
|
|
||||||
LWin | RWin => Key::Logo,
|
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,14 +187,22 @@ 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
|
/// Web sends 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.
|
/// a real text input or the name of a key.
|
||||||
fn should_ignore_key(key: &str) -> bool {
|
fn should_ignore_key(key: &str) -> bool {
|
||||||
let is_function_key = key.starts_with('F') && key.len() > 1;
|
let is_function_key = key.starts_with('F') && key.len() > 1;
|
||||||
is_function_key
|
is_function_key
|
||||||
|| matches!(
|
|| matches!(
|
||||||
key,
|
key,
|
||||||
"CapsLock" | "ContextMenu" | "NumLock" | "Pause" | "ScrollLock"
|
"Alt"
|
||||||
|
| "CapsLock"
|
||||||
|
| "ContextMenu"
|
||||||
|
| "Control"
|
||||||
|
| "Meta"
|
||||||
|
| "NumLock"
|
||||||
|
| "Pause"
|
||||||
|
| "ScrollLock"
|
||||||
|
| "Shift"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,24 +210,20 @@ fn should_ignore_key(key: &str) -> bool {
|
||||||
/// a real text input or the name of a key.
|
/// 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),
|
"ArrowDown" => Some(egui::Key::ArrowDown),
|
||||||
|
"ArrowLeft" => Some(egui::Key::ArrowLeft),
|
||||||
|
"ArrowRight" => Some(egui::Key::ArrowRight),
|
||||||
|
"ArrowUp" => Some(egui::Key::ArrowUp),
|
||||||
"Backspace" => Some(egui::Key::Backspace),
|
"Backspace" => Some(egui::Key::Backspace),
|
||||||
"Control" => Some(egui::Key::Control),
|
|
||||||
"Delete" => Some(egui::Key::Delete),
|
"Delete" => Some(egui::Key::Delete),
|
||||||
"ArrowDown" => Some(egui::Key::Down),
|
|
||||||
"End" => Some(egui::Key::End),
|
"End" => Some(egui::Key::End),
|
||||||
|
"Enter" => Some(egui::Key::Enter),
|
||||||
"Esc" | "Escape" => Some(egui::Key::Escape),
|
"Esc" | "Escape" => Some(egui::Key::Escape),
|
||||||
"Home" => Some(egui::Key::Home),
|
|
||||||
"Help" | "Insert" => Some(egui::Key::Insert),
|
"Help" | "Insert" => Some(egui::Key::Insert),
|
||||||
"ArrowLeft" => Some(egui::Key::Left),
|
"Home" => Some(egui::Key::Home),
|
||||||
"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::Enter),
|
|
||||||
"ArrowRight" => Some(egui::Key::Right),
|
|
||||||
"Shift" => Some(egui::Key::Shift),
|
|
||||||
"Tab" => Some(egui::Key::Tab),
|
"Tab" => Some(egui::Key::Tab),
|
||||||
"ArrowUp" => Some(egui::Key::Up),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,15 +273,16 @@ fn install_document_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
||||||
}
|
}
|
||||||
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 !should_ignore_key(&key) {
|
|
||||||
if let Some(key) = translate_key(&key) {
|
if let Some(key) = translate_key(&key) {
|
||||||
runner_lock
|
runner_lock.web_input.events.push(egui::Event::Key {
|
||||||
.web_input
|
key,
|
||||||
.events
|
pressed: true,
|
||||||
.push(egui::Event::Key { key, pressed: true });
|
modifiers: modifiers_from_event(&event),
|
||||||
} else {
|
});
|
||||||
runner_lock.web_input.events.push(egui::Event::Text(key));
|
runner_lock.needs_repaint = true;
|
||||||
}
|
} else if !should_ignore_key(&key) {
|
||||||
|
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(_)>);
|
||||||
|
@ -295,6 +300,7 @@ fn install_document_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
||||||
runner_lock.web_input.events.push(egui::Event::Key {
|
runner_lock.web_input.events.push(egui::Event::Key {
|
||||||
key,
|
key,
|
||||||
pressed: false,
|
pressed: false,
|
||||||
|
modifiers: modifiers_from_event(&event),
|
||||||
});
|
});
|
||||||
runner_lock.needs_repaint = true;
|
runner_lock.needs_repaint = true;
|
||||||
}
|
}
|
||||||
|
@ -315,6 +321,22 @@ fn install_document_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn modifiers_from_event(event: &web_sys::KeyboardEvent) -> egui::Modifiers {
|
||||||
|
egui::Modifiers {
|
||||||
|
alt: event.alt_key(),
|
||||||
|
ctrl: event.ctrl_key(),
|
||||||
|
shift: event.shift_key(),
|
||||||
|
|
||||||
|
// Ideally we should know if we are running or mac or not,
|
||||||
|
// but this works good enough for now.
|
||||||
|
mac_cmd: event.meta_key(),
|
||||||
|
|
||||||
|
// Ideally we should know if we are running or mac or not,
|
||||||
|
// but this works good enough for now.
|
||||||
|
command: event.ctrl_key() || event.meta_key(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
fn install_canvas_events(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
let canvas = canvas_element(runner_ref.0.lock().canvas_id()).unwrap();
|
let canvas = canvas_element(runner_ref.0.lock().canvas_id()).unwrap();
|
||||||
|
|
Loading…
Reference in a new issue