-
Notifications
You must be signed in to change notification settings - Fork 567
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Keyboard event rework #1049
Keyboard event rework #1049
Conversation
As of this checkpoint, we generate an keyboard-types KeyboardEvent, but don't plumb it through as the actual druid-shell keyboard event. Also, there is no dead-key handling at this point. Work towards #1040
WIP, modifiers in mouse event aren't there.
Use the new keyboard-types KeyboardEvent for all of druid, including text input. This is still a mac-only implementation. There are a few loose ends and discussion questions.
This adds a Windows implementation. There are some missing pieces: Conversion of KeyCompare to ACCEL has a number of holes. Basically I don't think KeyCompare is the right type - I think it can be replaced entirely by keyboard_types::Key, but am still investigating. There are compiler warnings in the places where rework is needed.
Refine implementation of menu hotkeys etc.
Make the menu implementation on mac use Key instead of Code, and get rid of references to KeyCompare.
Make menus on gtk use Key instead of KeyCompare. This should be a better implementation in any case, as Key is better than Code for identifying non-printing keys.
This improves the x11 key event handling beyond what it was before, though there is still a hardcoded keyboard map.
Fix clippy lint. Improve internal docs some. Also add changelog.
Also minor touchups to mac implementations, and formatting.
Switch web implementation of keyboard events to keyboard-types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, this looks great.
The main question I have is around whether we want to use KeyboardEvent
and Modifiers
from keyboard_types
directly; I could see some advantages to having a wrapper for Modifiers
, and maybe a slightly different version of KeyboardEvent
. This is largely about giving us control over the API, and also making it easier for us to make changes down the road without needing to worry about working with the upstream crate.
use crate::keycodes::KeyCode; | ||
use crate::keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers}; | ||
|
||
// TODO: fix docstring |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤓
equal => KeyCode::Equals, | ||
BackSpace => KeyCode::Backspace, | ||
#[allow(clippy::just_underscores_and_digits, non_upper_case_globals)] | ||
pub fn raw_key_to_key(raw: RawKey) -> Option<Key> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this stuff could live in another crate or get upstreamed to keyboard_types
or something? It does seem pretty broadly useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like a dialog. Is this something that there would be interest in? So far my toes dipped in these waters have met a chilly reception. The major client would be servo, but they're on winit, for which adopting any of this stuff would be a fairly major change.
I think the best way to go about this is a blog and then discussion on reddit or whatnot.
druid/src/widget/textbox.rs
Outdated
@@ -134,7 +135,7 @@ impl TextBox { | |||
EditAction::ModifySelection(movement) => self.move_selection(movement, text, true), | |||
EditAction::SelectAll => self.selection.all(text), | |||
EditAction::Click(action) => { | |||
if action.mods.shift { | |||
if action.mods.contains(Modifiers::SHIFT) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love the ergonomics here, would be nice if we could just do action.has_shift()
or something, or at least action.modifiers.includes_shift()
.
This is maybe related to the idea of still having our own KeyboardEvent
type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you that the bitflags ergonomics are a downgrade, but one reason I went with it is that it's very much standard Rust practice. I'm also worried about too much timtowtdi, especially if we start having methods on both Modifiers
and types that contain it.
WM_KEYDOWN, WM_KEYUP, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, | ||
}; | ||
|
||
use winapi::um::winuser::{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be combined with the block above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be, but I had the vk's in their own block so that it wouldn't be just one mass of symbols. I'll make the change though, because it can create confusion.
/// | ||
/// There should be one of these per window. It loads the current keyboard | ||
/// layout and retains some mapping information from it. | ||
pub fn new() -> KeyboardState { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be pub(crate)
? here and below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason this is not pub(crate) is that I want to keep the same code in win-win, where it's publicly exported. But maybe that should be a diff.
use crate::keyboard_types::{Code, Key, Location, Modifiers}; | ||
use x11rb::protocol::xproto::Keycode; | ||
|
||
// TODO: there's duplication here with gtk, as hardware keycodes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe worth doing now? I would have a shared
module in platform
that is cfg'd
for linux.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's also sharing between mac and x11. I'm worried the cfg stuff will get tangly, but it's probably worth it because more than one source of truth is a real problem.
Minor cleanups in response to review. This does not include the creation of shared keyboard utilities, and doesn't touch the type questions.
27ebcee
to
0c8cfa3
Compare
Also clean up some files that should have been deleted.
01abeb6
to
47667d4
Compare
As per discussion in PR and Zulip
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finding this diff a bit hard to read, but this looks good; I think there's one little oversight but it seems good.
I'd like to just test out porting over runebender and see if I run into any headaches, then I'll feel confident approving. 😁
Okay, I played around with moving runebender over to this, and I do have a few notes.
If we're moving away from direct field access, it might make sense to use the same names we currently use, but to mark them as deprecated. Playing around, I ended up with something like, #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct KeyEvent {
/// Whether the key is pressed or released.
pub state: KeyState,
/// Logical key value.
#[deprecated(since = "0.7.0", note = "use key() method instead")]
//FIXME: this should be named `key`; we use key_code to ease migration from 0.6
pub key_code: KbKey,
/// Physical key position.
code: Code,
/// Location for keys with multiple instances on common keyboards.
location: Location,
/// Flags for pressed modifier keys.
#[deprecated(since = "0.7.0", note = "use modifiers() method instead")]
pub mods: Modifiers,
/// True if the key is currently auto-repeated.
#[deprecated(since = "0.7.0", note = "use is_repeat() method instead")]
pub is_repeat: bool,
/// Events with this flag should be ignored in a text editor
/// and instead composition events should be used.
is_composing: bool,
}
#[allow(deprecated)]
impl KeyEvent {
/// Flags for pressed modifier keys.
pub fn modifiers(&self) -> Modifiers {
self.mods
}
/// Return the `KbKey` for this event.
#[inline]
pub fn key(&self) -> &KbKey {
&self.key_code
}
/// Physical key position.
pub fn code(&self) -> Code {
self.code
}
/// Location for keys with multiple instances on common keyboards.
pub fn location(&self) -> Location {
self.location
}
// etc
} This doesn't feel great, and may not be worth it, but maybe it is? I'm not sure. The idea would be that after the next release we would make these fields private. In any case, offering this up for discussion. If some of this is compelling, I can put up a patch. |
The "modifiers" field of KeyEvent becomes "mods", and there are deprecated aliases for KeyCode and KeyModifiers, to help with migration.
I'm not sure how much a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, happy to get this in.
On KeyCompare
and allocation: in this world HotKey
wouldn't always construct a Key
; it might just hold on to a Cow<str>
or something, and then we would use that when matching the Key
.
In any case this can be future work, I don't want to try and speculate on a design without having played with it more. :)
Yes, it is I believe possible to improve the implementation of HotKey to allocate less without much changing the external interface, and the pattern of HotKey.matches() pattern seems like it might be the best way to express the match (obviously the other choice is to have a method on KeyEvent or possibly KbKey, the latter of which would require an extension trait or upstream impl). |
As of this checkpoint, we generate an keyboard-types KeyboardEvent,
but don't plumb it through as the actual druid-shell keyboard event.
Also, there is no dead-key handling at this point.
Work towards #1040