Implement keyboard and blur/focus events

This commit is contained in:
Héctor Ramón Jiménez 2019-06-25 21:01:13 +02:00
parent d5368d7979
commit 8ad078b964
7 changed files with 303 additions and 74 deletions

View file

@ -7,9 +7,8 @@ pub use self::proxy::Proxy;
pub use self::window_target::WindowTarget; pub use self::window_target::WindowTarget;
use super::{backend, device, monitor, window}; use super::{backend, device, monitor, window};
use crate::event::{DeviceId, ElementState, Event, KeyboardInput, WindowEvent}; use crate::event::Event;
use crate::event_loop as root; use crate::event_loop as root;
use crate::window::WindowId;
use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque}; use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque};
use std::marker::PhantomData; use std::marker::PhantomData;
@ -45,54 +44,10 @@ impl<T> EventLoop<T> {
_marker: PhantomData, _marker: PhantomData,
}; };
let runner = self.elw.p.run(Box::new(move |event, flow| { self.elw.p.run(Box::new(move |event, flow| {
event_handler(event, &target, flow) event_handler(event, &target, flow)
})); }));
backend::Document::on_blur(|| {
runner.send_event(Event::WindowEvent {
window_id: WindowId(window::Id),
event: WindowEvent::Focused(false),
});
});
backend::Document::on_focus(|| {
runner.send_event(Event::WindowEvent {
window_id: WindowId(window::Id),
event: WindowEvent::Focused(true),
});
});
backend::Document::on_key_down(|scancode, virtual_keycode, modifiers| {
runner.send_event(Event::WindowEvent {
window_id: WindowId(window::Id),
event: WindowEvent::KeyboardInput {
device_id: DeviceId(unsafe { device::Id::dummy() }),
input: KeyboardInput {
scancode,
state: ElementState::Pressed,
virtual_keycode,
modifiers,
},
},
});
});
backend::Document::on_key_up(|scancode, virtual_keycode, modifiers| {
runner.send_event(Event::WindowEvent {
window_id: WindowId(window::Id),
event: WindowEvent::KeyboardInput {
device_id: DeviceId(unsafe { device::Id::dummy() }),
input: KeyboardInput {
scancode,
state: ElementState::Released,
virtual_keycode,
modifiers,
},
},
});
});
// Throw an exception to break out of Rust exceution and use unreachable to tell the // Throw an exception to break out of Rust exceution and use unreachable to tell the
// compiler this function won't return, giving it a return type of '!' // compiler this function won't return, giving it a return type of '!'
backend::throw( backend::throw(

View file

@ -1,5 +1,5 @@
use super::{backend, device, proxy::Proxy, runner, window}; use super::{backend, device, proxy::Proxy, runner, window};
use crate::event::{DeviceId, ElementState, Event, TouchPhase, WindowEvent}; use crate::event::{DeviceId, ElementState, Event, KeyboardInput, TouchPhase, WindowEvent};
use crate::event_loop::ControlFlow; use crate::event_loop::ControlFlow;
use crate::window::WindowId; use crate::window::WindowId;
use std::clone::Clone; use std::clone::Clone;
@ -27,15 +27,59 @@ impl<T> WindowTarget<T> {
Proxy::new(self.runner.clone()) Proxy::new(self.runner.clone())
} }
pub fn run( pub fn run(&self, event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>) {
&self,
event_handler: Box<dyn FnMut(Event<T>, &mut ControlFlow)>,
) -> &runner::Shared<T> {
self.runner.set_listener(event_handler); self.runner.set_listener(event_handler);
&self.runner
} }
pub fn register(&self, canvas: &mut backend::Canvas) { pub fn register(&self, canvas: &mut backend::Canvas) {
let runner = self.runner.clone();
canvas.on_blur(move || {
runner.send_event(Event::WindowEvent {
window_id: WindowId(window::Id),
event: WindowEvent::Focused(false),
});
});
let runner = self.runner.clone();
canvas.on_focus(move || {
runner.send_event(Event::WindowEvent {
window_id: WindowId(window::Id),
event: WindowEvent::Focused(true),
});
});
let runner = self.runner.clone();
canvas.on_key_down(move |scancode, virtual_keycode, modifiers| {
runner.send_event(Event::WindowEvent {
window_id: WindowId(window::Id),
event: WindowEvent::KeyboardInput {
device_id: DeviceId(unsafe { device::Id::dummy() }),
input: KeyboardInput {
scancode,
state: ElementState::Pressed,
virtual_keycode,
modifiers,
},
},
});
});
let runner = self.runner.clone();
canvas.on_key_up(move |scancode, virtual_keycode, modifiers| {
runner.send_event(Event::WindowEvent {
window_id: WindowId(window::Id),
event: WindowEvent::KeyboardInput {
device_id: DeviceId(unsafe { device::Id::dummy() }),
input: KeyboardInput {
scancode,
state: ElementState::Released,
virtual_keycode,
modifiers,
},
},
});
});
let runner = self.runner.clone(); let runner = self.runner.clone();
canvas.on_mouse_out(move |pointer_id| { canvas.on_mouse_out(move |pointer_id| {
runner.send_event(Event::WindowEvent { runner.send_event(Event::WindowEvent {

View file

@ -1,15 +1,19 @@
use super::event; use super::event;
use crate::dpi::{LogicalPosition, LogicalSize}; use crate::dpi::{LogicalPosition, LogicalSize};
use crate::error::OsError as RootOE; use crate::error::OsError as RootOE;
use crate::event::{ModifiersState, MouseButton, MouseScrollDelta}; use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode};
use crate::platform_impl::OsError; use crate::platform_impl::OsError;
use wasm_bindgen::{closure::Closure, JsCast}; use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{HtmlCanvasElement, PointerEvent, WheelEvent}; use web_sys::{FocusEvent, HtmlCanvasElement, KeyboardEvent, PointerEvent, WheelEvent};
pub struct Canvas { pub struct Canvas {
raw: HtmlCanvasElement, raw: HtmlCanvasElement,
on_redraw: Closure<dyn Fn()>, on_redraw: Closure<dyn Fn()>,
on_focus: Option<Closure<dyn FnMut(FocusEvent)>>,
on_blur: Option<Closure<dyn FnMut(FocusEvent)>>,
on_key_up: Option<Closure<dyn FnMut(KeyboardEvent)>>,
on_key_down: Option<Closure<dyn FnMut(KeyboardEvent)>>,
on_mouse_out: Option<Closure<dyn FnMut(PointerEvent)>>, on_mouse_out: Option<Closure<dyn FnMut(PointerEvent)>>,
on_mouse_over: Option<Closure<dyn FnMut(PointerEvent)>>, on_mouse_over: Option<Closure<dyn FnMut(PointerEvent)>>,
on_mouse_up: Option<Closure<dyn FnMut(PointerEvent)>>, on_mouse_up: Option<Closure<dyn FnMut(PointerEvent)>>,
@ -43,9 +47,18 @@ impl Canvas {
.append_child(&canvas) .append_child(&canvas)
.map_err(|_| os_error!(OsError("Failed to append canvas".to_owned())))?; .map_err(|_| os_error!(OsError("Failed to append canvas".to_owned())))?;
// TODO: Set up unique ids
canvas
.set_attribute("tabindex", "0")
.expect("Failed to set a tabindex");
Ok(Canvas { Ok(Canvas {
raw: canvas, raw: canvas,
on_redraw: Closure::wrap(Box::new(on_redraw) as Box<dyn Fn()>), on_redraw: Closure::wrap(Box::new(on_redraw) as Box<dyn Fn()>),
on_blur: None,
on_focus: None,
on_key_up: None,
on_key_down: None,
on_mouse_out: None, on_mouse_out: None,
on_mouse_over: None, on_mouse_over: None,
on_mouse_up: None, on_mouse_up: None,
@ -86,7 +99,53 @@ impl Canvas {
pub fn request_redraw(&self) { pub fn request_redraw(&self) {
let window = web_sys::window().expect("Failed to obtain window"); let window = web_sys::window().expect("Failed to obtain window");
window.request_animation_frame(&self.on_redraw.as_ref().unchecked_ref()); window
.request_animation_frame(&self.on_redraw.as_ref().unchecked_ref())
.expect("Failed to request animation frame");
}
pub fn on_blur<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(),
{
self.on_blur = Some(self.add_event("blur", move |_: FocusEvent| {
handler();
}));
}
pub fn on_focus<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(),
{
self.on_focus = Some(self.add_event("focus", move |_: FocusEvent| {
handler();
}));
}
pub fn on_key_up<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
{
self.on_key_up = Some(self.add_event("keyup", move |event: KeyboardEvent| {
handler(
event::scan_code(&event),
event::virtual_key_code(&event),
event::keyboard_modifiers(&event),
);
}));
}
pub fn on_key_down<F>(&mut self, mut handler: F)
where
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
{
self.on_key_down = Some(self.add_event("keydown", move |event: KeyboardEvent| {
handler(
event::scan_code(&event),
event::virtual_key_code(&event),
event::keyboard_modifiers(&event),
);
}));
} }
pub fn on_mouse_out<F>(&mut self, mut handler: F) pub fn on_mouse_out<F>(&mut self, mut handler: F)
@ -124,7 +183,14 @@ impl Canvas {
where where
F: 'static + FnMut(i32, MouseButton, ModifiersState), F: 'static + FnMut(i32, MouseButton, ModifiersState),
{ {
let canvas = self.raw.clone();
self.on_mouse_down = Some(self.add_event("pointerdown", move |event: PointerEvent| { self.on_mouse_down = Some(self.add_event("pointerdown", move |event: PointerEvent| {
// We focus the canvas manually when the user clicks on it.
// This is necessary because we are preventing the default event behavior
// in `add_event`
canvas.focus().expect("Failed to focus canvas");
handler( handler(
event.pointer_id(), event.pointer_id(),
event::mouse_button(&event), event::mouse_button(&event),

View file

@ -1,13 +0,0 @@
pub struct Document;
impl Document {
pub fn set_title(title: &str) {}
pub fn on_blur<F>(f: F) {}
pub fn on_focus<F>(f: F) {}
pub fn on_key_up<F>(f: F) {}
pub fn on_key_down<F>(f: F) {}
}

View file

@ -1,8 +1,8 @@
use crate::dpi::LogicalPosition; use crate::dpi::LogicalPosition;
use crate::event::{ModifiersState, MouseButton, MouseScrollDelta}; use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode};
use std::convert::TryInto; use std::convert::TryInto;
use web_sys::{MouseEvent, WheelEvent}; use web_sys::{KeyboardEvent, MouseEvent, WheelEvent};
pub fn mouse_button(event: &MouseEvent) -> MouseButton { pub fn mouse_button(event: &MouseEvent) -> MouseButton {
match event.button() { match event.button() {
@ -39,3 +39,182 @@ pub fn mouse_scroll_delta(event: &WheelEvent) -> Option<MouseScrollDelta> {
_ => None, _ => None,
} }
} }
pub fn scan_code(event: &KeyboardEvent) -> ScanCode {
match event.key_code() {
0 => event.char_code(),
i => i,
}
}
pub fn virtual_key_code(event: &KeyboardEvent) -> Option<VirtualKeyCode> {
Some(match &event.code()[..] {
"Digit1" => VirtualKeyCode::Key1,
"Digit2" => VirtualKeyCode::Key2,
"Digit3" => VirtualKeyCode::Key3,
"Digit4" => VirtualKeyCode::Key4,
"Digit5" => VirtualKeyCode::Key5,
"Digit6" => VirtualKeyCode::Key6,
"Digit7" => VirtualKeyCode::Key7,
"Digit8" => VirtualKeyCode::Key8,
"Digit9" => VirtualKeyCode::Key9,
"Digit0" => VirtualKeyCode::Key0,
"KeyA" => VirtualKeyCode::A,
"KeyB" => VirtualKeyCode::B,
"KeyC" => VirtualKeyCode::C,
"KeyD" => VirtualKeyCode::D,
"KeyE" => VirtualKeyCode::E,
"KeyF" => VirtualKeyCode::F,
"KeyG" => VirtualKeyCode::G,
"KeyH" => VirtualKeyCode::H,
"KeyI" => VirtualKeyCode::I,
"KeyJ" => VirtualKeyCode::J,
"KeyK" => VirtualKeyCode::K,
"KeyL" => VirtualKeyCode::L,
"KeyM" => VirtualKeyCode::M,
"KeyN" => VirtualKeyCode::N,
"KeyO" => VirtualKeyCode::O,
"KeyP" => VirtualKeyCode::P,
"KeyQ" => VirtualKeyCode::Q,
"KeyR" => VirtualKeyCode::R,
"KeyS" => VirtualKeyCode::S,
"KeyT" => VirtualKeyCode::T,
"KeyU" => VirtualKeyCode::U,
"KeyV" => VirtualKeyCode::V,
"KeyW" => VirtualKeyCode::W,
"KeyX" => VirtualKeyCode::X,
"KeyY" => VirtualKeyCode::Y,
"KeyZ" => VirtualKeyCode::Z,
"Escape" => VirtualKeyCode::Escape,
"F1" => VirtualKeyCode::F1,
"F2" => VirtualKeyCode::F2,
"F3" => VirtualKeyCode::F3,
"F4" => VirtualKeyCode::F4,
"F5" => VirtualKeyCode::F5,
"F6" => VirtualKeyCode::F6,
"F7" => VirtualKeyCode::F7,
"F8" => VirtualKeyCode::F8,
"F9" => VirtualKeyCode::F9,
"F10" => VirtualKeyCode::F10,
"F11" => VirtualKeyCode::F11,
"F12" => VirtualKeyCode::F12,
"F13" => VirtualKeyCode::F13,
"F14" => VirtualKeyCode::F14,
"F15" => VirtualKeyCode::F15,
"F16" => VirtualKeyCode::F16,
"F17" => VirtualKeyCode::F17,
"F18" => VirtualKeyCode::F18,
"F19" => VirtualKeyCode::F19,
"F20" => VirtualKeyCode::F20,
"F21" => VirtualKeyCode::F21,
"F22" => VirtualKeyCode::F22,
"F23" => VirtualKeyCode::F23,
"F24" => VirtualKeyCode::F24,
"PrintScreen" => VirtualKeyCode::Snapshot,
"ScrollLock" => VirtualKeyCode::Scroll,
"Pause" => VirtualKeyCode::Pause,
"Insert" => VirtualKeyCode::Insert,
"Home" => VirtualKeyCode::Home,
"Delete" => VirtualKeyCode::Delete,
"End" => VirtualKeyCode::End,
"PageDown" => VirtualKeyCode::PageDown,
"PageUp" => VirtualKeyCode::PageUp,
"ArrowLeft" => VirtualKeyCode::Left,
"ArrowUp" => VirtualKeyCode::Up,
"ArrowRight" => VirtualKeyCode::Right,
"ArrowDown" => VirtualKeyCode::Down,
"Backspace" => VirtualKeyCode::Back,
"Enter" => VirtualKeyCode::Return,
"Space" => VirtualKeyCode::Space,
"Compose" => VirtualKeyCode::Compose,
"Caret" => VirtualKeyCode::Caret,
"NumLock" => VirtualKeyCode::Numlock,
"Numpad0" => VirtualKeyCode::Numpad0,
"Numpad1" => VirtualKeyCode::Numpad1,
"Numpad2" => VirtualKeyCode::Numpad2,
"Numpad3" => VirtualKeyCode::Numpad3,
"Numpad4" => VirtualKeyCode::Numpad4,
"Numpad5" => VirtualKeyCode::Numpad5,
"Numpad6" => VirtualKeyCode::Numpad6,
"Numpad7" => VirtualKeyCode::Numpad7,
"Numpad8" => VirtualKeyCode::Numpad8,
"Numpad9" => VirtualKeyCode::Numpad9,
"AbntC1" => VirtualKeyCode::AbntC1,
"AbntC2" => VirtualKeyCode::AbntC2,
"NumpadAdd" => VirtualKeyCode::Add,
"Quote" => VirtualKeyCode::Apostrophe,
"Apps" => VirtualKeyCode::Apps,
"At" => VirtualKeyCode::At,
"Ax" => VirtualKeyCode::Ax,
"Backslash" => VirtualKeyCode::Backslash,
"Calculator" => VirtualKeyCode::Calculator,
"Capital" => VirtualKeyCode::Capital,
"Semicolon" => VirtualKeyCode::Semicolon,
"Comma" => VirtualKeyCode::Comma,
"Convert" => VirtualKeyCode::Convert,
"NumpadDecimal" => VirtualKeyCode::Decimal,
"NumpadDivide" => VirtualKeyCode::Divide,
"Equal" => VirtualKeyCode::Equals,
"Backquote" => VirtualKeyCode::Grave,
"Kana" => VirtualKeyCode::Kana,
"Kanji" => VirtualKeyCode::Kanji,
"AltLeft" => VirtualKeyCode::LAlt,
"BracketLeft" => VirtualKeyCode::LBracket,
"ControlLeft" => VirtualKeyCode::LControl,
"ShiftLeft" => VirtualKeyCode::LShift,
"MetaLeft" => VirtualKeyCode::LWin,
"Mail" => VirtualKeyCode::Mail,
"MediaSelect" => VirtualKeyCode::MediaSelect,
"MediaStop" => VirtualKeyCode::MediaStop,
"Minus" => VirtualKeyCode::Minus,
"NumpadMultiply" => VirtualKeyCode::Multiply,
"Mute" => VirtualKeyCode::Mute,
"LaunchMyComputer" => VirtualKeyCode::MyComputer,
"NavigateForward" => VirtualKeyCode::NavigateForward,
"NavigateBackward" => VirtualKeyCode::NavigateBackward,
"NextTrack" => VirtualKeyCode::NextTrack,
"NoConvert" => VirtualKeyCode::NoConvert,
"NumpadComma" => VirtualKeyCode::NumpadComma,
"NumpadEnter" => VirtualKeyCode::NumpadEnter,
"NumpadEquals" => VirtualKeyCode::NumpadEquals,
"OEM102" => VirtualKeyCode::OEM102,
"Period" => VirtualKeyCode::Period,
"PlayPause" => VirtualKeyCode::PlayPause,
"Power" => VirtualKeyCode::Power,
"PrevTrack" => VirtualKeyCode::PrevTrack,
"AltRight" => VirtualKeyCode::RAlt,
"BracketRight" => VirtualKeyCode::RBracket,
"ControlRight" => VirtualKeyCode::RControl,
"ShiftRight" => VirtualKeyCode::RShift,
"MetaRight" => VirtualKeyCode::RWin,
"Slash" => VirtualKeyCode::Slash,
"Sleep" => VirtualKeyCode::Sleep,
"Stop" => VirtualKeyCode::Stop,
"NumpadSubtract" => VirtualKeyCode::Subtract,
"Sysrq" => VirtualKeyCode::Sysrq,
"Tab" => VirtualKeyCode::Tab,
"Underline" => VirtualKeyCode::Underline,
"Unlabeled" => VirtualKeyCode::Unlabeled,
"AudioVolumeDown" => VirtualKeyCode::VolumeDown,
"AudioVolumeUp" => VirtualKeyCode::VolumeUp,
"Wake" => VirtualKeyCode::Wake,
"WebBack" => VirtualKeyCode::WebBack,
"WebFavorites" => VirtualKeyCode::WebFavorites,
"WebForward" => VirtualKeyCode::WebForward,
"WebHome" => VirtualKeyCode::WebHome,
"WebRefresh" => VirtualKeyCode::WebRefresh,
"WebSearch" => VirtualKeyCode::WebSearch,
"WebStop" => VirtualKeyCode::WebStop,
"Yen" => VirtualKeyCode::Yen,
_ => return None,
})
}
pub fn keyboard_modifiers(event: &KeyboardEvent) -> ModifiersState {
ModifiersState {
shift: event.shift_key(),
ctrl: event.ctrl_key(),
alt: event.alt_key(),
logo: event.meta_key(),
}
}

View file

@ -1,10 +1,8 @@
mod canvas; mod canvas;
mod document;
mod event; mod event;
mod timeout; mod timeout;
pub use self::canvas::Canvas; pub use self::canvas::Canvas;
pub use self::document::Document;
pub use self::timeout::Timeout; pub use self::timeout::Timeout;
use crate::platform::web::WindowExtWebSys; use crate::platform::web::WindowExtWebSys;

View file

@ -57,7 +57,7 @@ impl Window {
} }
pub fn set_title(&self, title: &str) { pub fn set_title(&self, title: &str) {
backend::Document::set_title(title); self.canvas.set_attribute("alt", title);
} }
pub fn set_visible(&self, _visible: bool) { pub fn set_visible(&self, _visible: bool) {