#Map keyboard inputs to buttons

1 messages · Page 1 of 1 (latest)

keen night
#

Is there a simple way to simulate clicking a button when a key is pressed? I essentially want the same exact thing to happen whether I click the button element or press the key I associate with it.

#

Map keyboard inputs to buttons

elder hare
#
  1. use a common callback in both of them (you might need a simple wrapper like |e: KeyboardEvent| common_action.emit(...))
  2. html element :: click() on key, grab html element using ref= and use_node_ref
keen night
elder hare
#

i typed my message from a phone kek

keen night
#

The first one might be easier to implement. I am using struct components. Is there a way to call the update function with a specific Message when a key is pressed?

elder hare
#

the most obvious way to have events is just regular event attributes
for struct components then, you may have a callback (which is basically just an Rc<dyn Fn>) which would have your message sent as you wish
then you can move that callback into each event handling closure (but make sure you .clone() a value into each one, because the callback needs to be shared, luckily callback is just an rc so its very easy and performance/memory cheap to clone)
here is what i am thinking of:

let action = ctx.link().callback(|()| ...); // lets have a callback that takes in nothing, i.e. unit `()`
let onclick = {
    let action = action.clone();
    move |_: ClickEvent| action.emit(()) // same `()` here
};
let onkeydown = { // consider oninput for `<input type="text">` instead, also a bit harder to attach a global key listener for `window`!
    let action = action.clone();
    move |_: KeyboardEvent| action.emit(())
};
html! {
    <input type="text" { onclick } { onkeydown } />
}
keen night
# elder hare the most obvious way to have events is just regular [event attributes](<https://...

Ok, could I do something like this?:

let action = ctx.link().callback(|m: Msg| m);
let onclick = {
    let action = action.clone();
    move |_: ClickEvent| action.emit(/* some Msg */)
};
let onkeydown = {
    let action = action.clone();
    move |k: KeyboardEvent| {
        let m = match k {
            /* convert to a message */
            _ => Msg::None, // Some way to ignore the message?
        }
        action.emit(m)
    }
};
html! {
    <input type="text" { onclick } { onkeydown } />
}

So that I can handle different keys differently?

elder hare
#

a way to ignore message is return, that will shortcircuit the closure scope

#

if the common action is just sending message, why not just call ctx.link().callback(...) twice, that will skip the let ... = ....clone() bit

keen night
elder hare
#

if its the same message you want to post then yeah, might be not exact syntax but idea is there

#

but you said you wanted to replace that _ in 2nd one with a k: KeyboardEvent followed by a match k (k.something() actually)

keen night
keen night
elder hare
#

i have no idea

#

kek

#

i would assume that would be a global key listener

#

and that i would attach to a window instead

#

#1164567450692628610 message

keen night
elder hare
#

state will be stored in the struct, running thing once would be done in create probably

#

im not a struct component dev really

elder hare
#

The EventHandler is stopping listening when you Drop it, and storing it (or using forget) is what allows it to, well, not unsubscribe

#

with storing approach you can attach many many global key listeners to window from any component, and when that component drops, the listener will as well

keen night
#

@elder hare Ok, thanks for your help so far. I need to leave for a bit, but I think I have enough information to try to implement it myself. I'll come back again when I inevitably get stuck 😁

keen night
#

Ok, I am back. When I create an EventListener, is there a way for it to send a message to the component it is a field in?


fn create(_ctx: &Context<Self>) -> Self {
    let listener = EventListener::new(&window(), "keydown", |event| {
        let event = event.dyn_ref::<KeyboardEvent>().unwrap_throw();
        
        let msg = match event {
            // Convert to Msg
            _ => None,
        }
        
        if let Some(m) = msg {
            self.update(m); // This doesn't work, because "self" hasn't been created yet
        }
    });
    Self {
        // Other fields ...
        keydown_listener: listener,
    }
}
elder hare
#

you have a ctx, get a link and make a callback

#

or move in a link and send_message

keen night
#

Winter break just ended, so I wasn't able to work on this for a while.

keen night
# elder hare or move in a link and send_message

This worked! (I am pretty sure, I need to do a few more tests)

let listener = EventListener::new(&window(), "keydown", {
    let link = ctx.link().clone();
    move |event| {
        let _event: &KeyboardEvent = event.dyn_ref::<KeyboardEvent>().unwrap_throw();
    
        // TODO: process event and update self with corresponding message
        let msg = Msg::ClickNumber(1.0);

        link.send_message(msg);
    }
});