How can I add a global keydown event listener in Dioxus)?

928 views Asked by At

I'm currently trying out dioxus for rust, and I'm trying to figure out how to handle a global keyboard down event.

I want to use the arrow keys to move images back and forth:

Here's my code so far:

use dioxus::{events::*, prelude::*};
use log::{info, LevelFilter};

/**
 * Specify <link data-trunk rel="copy-dir" href="src/assets" />
 * in the index.html to copy the files!!
 *
 * You'll see them in the dist directory!
 */

fn main() {
    dioxus_logger::init(LevelFilter::Info).expect("failed to init logger");
    dioxus::web::launch(app);
}

fn app(cx: Scope) -> Element {
    let mut index = use_state(&cx, || 1);

    let change_evt = move |evt: KeyboardEvent| match evt.key.as_str() {
        "ArrowRight" => index += 1,
        "ArrowLeft" => index -= 1,
        _ => {}
    };

    let url = format!("/assets/img/wallpaper/1042/0{}.jpg", index);
    cx.render(rsx!(img {
        src: "{url}",
        onkeydown: change_evt,
    }))
}

In JavaScript would've been something like

document.addEventListener('keydown', (evt) => {
 // Do magic
}

I've tried following the calculator example but can't get it to work.

Any ideas?

3

There are 3 answers

5
susitsm On

onkeydown does not seem to work as a callback passed to an image. Wrap it in a div.

I placed an extra button there because, for some reason, the keyboard event callbacks did not register until I interacted with the app somehow (tried it in the browser).

fn app(cx: Scope) -> Element {
    let mut index = use_state(&cx, || 1);

    let change_evt = move |evt: KeyboardEvent| {
        log::info!("{index}{}", evt.key);
        match evt.key.as_str() {
            "ArrowRight" => index += 1,
            "ArrowLeft" => index -= 1,
            _ => {}
        }
    };

    let url = format!("/assets/img/wallpaper/1042/0{}.jpg", index);
    cx.render(rsx!(
        img {
            src: "{url}",
        }
        div {
            class: "display",
            onkeydown: change_evt,
            button {
                class: "but",
                onclick: move |evt| {
                    println!("{evt:?}");
                    info!("{evt:?}");
                },
                "Press me!"
            },
        },
    ))   
}
1
Jose A On

This is what I ended up doing in Yew.

I used wasm_bindgen and web_sys. This could potentially work for Dioxus

use wasm_bindgen::{prelude::Closure, JsCast, UnwrapThrowExt};
use web_sys::window;

    use_effect(move || {
        let callback = Closure::wrap(Box::new(move |e: web_sys::KeyboardEvent| {
            let key = e.key();
            let key_str = key.as_str();
            match key_str {
                "ArrowLeft" => {
                  // Do something here
                }
                "ArrowRight" => {
                  // Do something here
                }
                _ => {}
            }
        }) as Box<dyn FnMut(_)>);

        win.add_event_listener_with_callback("keydown", callback.as_ref().unchecked_ref())
            .unwrap();

        move || {
            win.remove_event_listener_with_callback("keydown", callback.as_ref().unchecked_ref())
                .unwrap();
        }
    });

Here's part of the cargo.toml

[dependencies]
web-sys = { version = "0.3.60", features = ['console'] }
wasm-bindgen = { version = "0.2.83" }
2
Brian Edwards On

Cargo.toml

[dependencies]
active-win-pos-rs = "0.8.3"
dioxus = "0.4.3"
dioxus-desktop = "0.4.3"
dioxus-html = { version = "0.4.3", features = ["serialize", "native-bind"] }
wry = { version = "0.28.0", default-features = false, features = ["protocol", "file-drop"] }

main.rs

use active_win_pos_rs::get_active_window;
use dioxus::prelude::*;
use dioxus_desktop;
use wry::application::keyboard::ModifiersState;

fn main() {
    dioxus_desktop::launch(App);
}

#[allow(non_snake_case)]
fn App(cx: Scope) -> Element {
    dioxus_desktop::use_global_shortcut(cx, (ModifiersState::SUPER, KeyCode::Q), || {
        match get_active_window() {
            Ok(active_window) => {
                if active_window.process_id == std::process::id() as u64 {
                    std::process::exit(0);
                }
            }
            Err(e) => panic!("{:#?}", e),
        }
    });

    render!(div{"hello"})
}