#Map keyboard inputs to buttons
1 messages · Page 1 of 1 (latest)
- use a common callback in both of them (you might need a simple wrapper like
|e: KeyboardEvent| common_action.emit(...)) html element :: click()on key, grab html element usingref=anduse_node_ref
Sorry, I am a beginner and I haven't done anything with events yet. Could you go into a little more detail?
i typed my message from a phone kek
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?
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 } />
}
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?
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
Like this?:
let msg = Msg::MyMessage;
html! {
<button
onclick={
let msg = msg.clone();
ctx.link().callback(move |_| msg)
}
onkeydown={ctx.link().callback(move |_| msg)}>
{ "Content" }
</button>
}
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)
Ok, so this would call on every keydown and not care about the key...
Just so we don't go down an unnecessary rabbit hole. With this implementation, would every button that has an onkeydown handler be able to listen to all KeyboardEvents?
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
Ok, I've read through it and I kind of understand. But how would I do it when using a struct based component?
state will be stored in the struct, running thing once would be done in create probably
im not a struct component dev really
Do I need to store the event?
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
@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 😁
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,
}
}
Winter break just ended, so I wasn't able to work on this for a while.
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);
}
});