Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New keyboard API for the web #1888

Closed
wants to merge 13 commits into from
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ features = [
'console',
"AddEventListenerOptions",
'CssStyleDeclaration',
'CompositionEvent',
'BeforeUnloadEvent',
'Document',
'DomRect',
Expand Down
2 changes: 1 addition & 1 deletion src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ pub enum DeviceEvent {
/// repeat or the initial keypress. An application may emulate this by, for
/// example keeping a Map/Set of pressed keys and determining whether a keypress
/// corresponds to an already pressed key.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RawKeyEvent {
pub physical_key: keyboard::KeyCode,
Expand Down
13 changes: 9 additions & 4 deletions src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,18 @@ mod modifiers_serde {
}

/// Contains the platform-native physical key identifier (aka scancode)
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum NativeKeyCode {
Unidentified,
Windows(u16),
MacOS(u32),
XKB(u32),
Web(String),
}
impl std::fmt::Debug for NativeKeyCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use NativeKeyCode::{MacOS, Unidentified, Windows, XKB};
use NativeKeyCode::{MacOS, Unidentified, Web, Windows, XKB};
let mut debug_tuple;
match self {
Unidentified => {
Expand All @@ -197,6 +198,10 @@ impl std::fmt::Debug for NativeKeyCode {
debug_tuple = f.debug_tuple(name_of!(XKB));
debug_tuple.field(v);
}
Web(v) => {
debug_tuple = f.debug_tuple(name_of!(Web));
debug_tuple.field(v);
}
}
debug_tuple.finish()
}
Expand All @@ -213,7 +218,7 @@ impl std::fmt::Debug for NativeKeyCode {
///
/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum KeyCode {
/// This variant is used when the key cannot be translated to any
Expand Down Expand Up @@ -673,7 +678,7 @@ pub enum KeyCode {
///
/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Key<'a> {
/// A key string that corresponds to the character typed by the user, taking into account the
Expand Down
150 changes: 108 additions & 42 deletions src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,49 @@
use super::{super::monitor, backend, device, proxy::Proxy, runner, window};
use crate::dpi::{PhysicalSize, Size};
use crate::event::{
DeviceEvent, DeviceId, ElementState, Event, KeyboardInput, TouchPhase, WindowEvent,
use super::{
super::{monitor, KeyEventExtra},
backend, device,
proxy::Proxy,
runner, window,
};
use crate::dpi::{PhysicalSize, Size};
use crate::event::{DeviceEvent, DeviceId, ElementState, Event, KeyEvent, TouchPhase, WindowEvent};
use crate::event_loop::ControlFlow;
use crate::keyboard::ModifiersState;
use crate::monitor::MonitorHandle as RootMH;
use crate::window::{Theme, WindowId};
use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use std::clone::Clone;
use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque};
use std::rc::Rc;

#[derive(Default)]
struct ModifiersShared(Rc<Cell<ModifiersState>>);

impl ModifiersShared {
fn set(&self, new: ModifiersState) {
self.0.set(new)
}

fn get(&self) -> ModifiersState {
self.0.get()
}
}

impl Clone for ModifiersShared {
fn clone(&self) -> Self {
Self(Rc::clone(&self.0))
}
}
Comment on lines +18 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be replaced with type ModifiersShared = Rc<Cell<ModifiersState>> instead of having to declare a newtype.


pub struct WindowTarget<T: 'static> {
pub(crate) runner: runner::Shared<T>,
modifiers: ModifiersShared,
}

impl<T> Clone for WindowTarget<T> {
fn clone(&self) -> Self {
WindowTarget {
runner: self.runner.clone(),
modifiers: self.modifiers.clone(),
}
}
}
Expand All @@ -27,6 +52,7 @@ impl<T> WindowTarget<T> {
pub fn new() -> Self {
WindowTarget {
runner: runner::Shared::new(),
modifiers: ModifiersShared::default(),
}
}

Expand Down Expand Up @@ -68,47 +94,87 @@ impl<T> WindowTarget<T> {
});

let runner = self.runner.clone();
canvas.on_keyboard_press(move |scancode, virtual_keycode, modifiers| {
#[allow(deprecated)]
runner.send_event(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::KeyboardInput {
device_id: DeviceId(unsafe { device::Id::dummy() }),
input: KeyboardInput {
scancode,
state: ElementState::Pressed,
virtual_keycode,
modifiers,
},
is_synthetic: false,
},
});
});
let modifiers = self.modifiers.clone();
canvas.on_keyboard_press(
move |physical_key, logical_key, text, location, repeat, new_modifiers| {
let active_modifiers = modifiers.get() | new_modifiers;
let modifiers_changed = if modifiers.get() != active_modifiers {
modifiers.set(active_modifiers);
Some(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers),
})
} else {
None
};

runner.send_events(
std::iter::once(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::KeyboardInput {
device_id: DeviceId(unsafe { device::Id::dummy() }),
event: KeyEvent {
physical_key,
logical_key,
text,
location,
state: ElementState::Pressed,
repeat,
platform_specific: KeyEventExtra,
},
is_synthetic: false,
},
})
.chain(modifiers_changed),
);
},
);

let runner = self.runner.clone();
canvas.on_keyboard_release(move |scancode, virtual_keycode, modifiers| {
#[allow(deprecated)]
runner.send_event(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::KeyboardInput {
device_id: DeviceId(unsafe { device::Id::dummy() }),
input: KeyboardInput {
scancode,
state: ElementState::Released,
virtual_keycode,
modifiers,
},
is_synthetic: false,
},
});
});
let modifiers = self.modifiers.clone();
canvas.on_keyboard_release(
move |physical_key, logical_key, text, location, repeat, new_modifiers| {
let active_modifiers = modifiers.get() & !new_modifiers;
let modifiers_changed = if modifiers.get() != active_modifiers {
modifiers.set(active_modifiers);
Some(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers),
})
} else {
None
};

runner.send_events(
std::iter::once(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::KeyboardInput {
device_id: DeviceId(unsafe { device::Id::dummy() }),
event: KeyEvent {
physical_key,
logical_key,
text,
location,
state: ElementState::Released,
repeat,
platform_specific: KeyEventExtra,
},
is_synthetic: false,
},
})
.chain(modifiers_changed),
)
},
);

let runner = self.runner.clone();
canvas.on_received_character(move |char_code| {
runner.send_event(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::ReceivedCharacter(char_code),
});
canvas.on_composition_end(move |data| {
if let Some(data) = data {
runner.send_event(Event::WindowEvent {
window_id: WindowId(id),
event: WindowEvent::ReceivedImeText(data),
});
}
});

let runner = self.runner.clone();
Expand Down
Loading