I’m writing a GUI app in Rust, trying to get it working with Tauri’s tooling, using Yew to generate the actual user-facing code. I thought it would be easy to get a thing that notices when I hit a key, and do stuff with it. Pre-yew-0.19 you could do this with a KeyboardService
, now it’s a whole thing with gloo
.
The example they provide doesn’t indicate how to do this without binding it to a HTML element. Turns out, you just have to hold your tongue differently and bind it to the document. It’s even neater at the end for me, because I loathe the magic behind their function_component
implementations.
Someone pointed me to this issue in the yewstack repo which involved adding gloo to my app so I could use the EventListener
functionality.
This took me days to find a working example of, provided by a very helpful person on Tauri’s Discord server… so here’s the code:
use gloo::events::EventListener;
use web_sys::KeyboardEvent;
// Yew component messages
enum Msg{
KeyEvent { event: KeyboardEvent },
}
pub struct App {
/// Holds the listener once it's stood up. Can't be done before rendering because... the document doesn't exist yet!
pub kbd_listener: Option<EventListener>,
}
impl Component for App {
//<snip other stuff>
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::KeyEvent { event } => {
let logline = &format!(
"Key event in browser, no action required. Pressed: {:?}",
event.key_code()
);
true
}
}
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
// we only need to run the below stuff the first time
if !first_render {
return;
}
let document = web_sys::window().unwrap().document().unwrap();
let ct = ctx.link().to_owned();
let listener = EventListener::new(&document, "keydown", move |event| {
let event = event
.dyn_ref::<KeyboardEvent>()
.unwrap_throw()
.to_owned();
ct.send_message(Msg::KeyEvent { event });
});
self.kbd_listener.replace(listener);
}
}