Skip to content

Commit

Permalink
Wrapper types for keyboard events
Browse files Browse the repository at this point in the history
As per discussion in PR and Zulip

WIP, it's Windows only for now
  • Loading branch information
raphlinus committed Jun 26, 2020
1 parent a431c81 commit 01abeb6
Show file tree
Hide file tree
Showing 17 changed files with 461 additions and 301 deletions.
5 changes: 2 additions & 3 deletions druid-shell/examples/perftest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ use time::Instant;
use piet_common::kurbo::{Line, Rect, Size};
use piet_common::{Color, FontBuilder, Piet, RenderContext, Text, TextLayoutBuilder};

use druid_shell::keyboard_types::KeyboardEvent;
use druid_shell::{Application, WinHandler, WindowBuilder, WindowHandle};
use druid_shell::{Application, KeyEvent, WinHandler, WindowBuilder, WindowHandle};

const BG_COLOR: Color = Color::rgb8(0x27, 0x28, 0x22);
const FG_COLOR: Color = Color::rgb8(0xf0, 0xf0, 0xea);
Expand Down Expand Up @@ -115,7 +114,7 @@ impl WinHandler for PerfTest {
}
}

fn key_down(&mut self, event: KeyboardEvent) -> bool {
fn key_down(&mut self, event: KeyEvent) -> bool {
println!("keydown: {:?}", event);
false
}
Expand Down
7 changes: 3 additions & 4 deletions druid-shell/examples/shello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ use std::any::Any;
use druid_shell::kurbo::{Line, Rect, Size};
use druid_shell::piet::{Color, RenderContext};

use druid_shell::keyboard_types::KeyboardEvent;
use druid_shell::{
Application, Cursor, FileDialogOptions, FileSpec, HotKey, Menu, MouseEvent, SysMods,
Application, Cursor, FileDialogOptions, FileSpec, HotKey, KeyEvent, Menu, MouseEvent, SysMods,
TimerToken, WinHandler, WindowBuilder, WindowHandle,
};

Expand Down Expand Up @@ -63,12 +62,12 @@ impl WinHandler for HelloState {
}
}

fn key_down(&mut self, event: KeyboardEvent) -> bool {
fn key_down(&mut self, event: KeyEvent) -> bool {
println!("keydown: {:?}", event);
false
}

fn key_up(&mut self, event: KeyboardEvent) {
fn key_up(&mut self, event: KeyEvent) {
println!("keyup: {:?}", event);
}

Expand Down
89 changes: 19 additions & 70 deletions druid-shell/src/hotkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::borrow::Borrow;

use log::warn;

use crate::keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers};
use crate::{IntoKey, KbKey, KeyEvent, Modifiers};

// TODO: fix docstring

Expand All @@ -32,47 +32,33 @@ use crate::keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, Modifi
/// [`SysMods`] matches the Command key on macOS and Ctrl elsewhere:
///
/// ```
/// use druid_shell::{key_event_for_test, HotKey, RawMods, SysMods};
/// use druid_shell::keyboard_types::Key;
/// use druid_shell::{HotKey, KbKey, KeyEvent, RawMods, SysMods};
///
/// let hotkey = HotKey::new(SysMods::Cmd, "a");
///
/// #[cfg(target_os = "macos")]
/// assert!(hotkey.matches(key_event_for_test(RawMods::Meta, "a")));
/// assert!(hotkey.matches(KeyEvent::for_test(RawMods::Meta, "a")));
///
/// #[cfg(target_os = "windows")]
/// assert!(hotkey.matches(key_event_for_test(RawMods::Ctrl, "a")));
/// assert!(hotkey.matches(KeyEvent::for_test(RawMods::Ctrl, "a")));
/// ```
///
/// `None` matches only the key without modifiers:
///
/// ```
/// use druid_shell::{key_event_for_test, HotKey, RawMods, SysMods};
/// use druid_shell::keyboard_types::Key;
/// use druid_shell::{HotKey, KbKey, KeyEvent, RawMods, SysMods};
///
/// let hotkey = HotKey::new(None, Key::ArrowLeft);
/// let hotkey = HotKey::new(None, KbKey::ArrowLeft);
///
/// assert!(hotkey.matches(key_event_for_test(RawMods::None, Key::ArrowLeft)));
/// assert!(!hotkey.matches(key_event_for_test(RawMods::Ctrl, Key::ArrowLeft)));
/// assert!(hotkey.matches(KeyEvent::for_test(RawMods::None, KbKey::ArrowLeft)));
/// assert!(!hotkey.matches(KeyEvent::for_test(RawMods::Ctrl, KbKey::ArrowLeft)));
/// ```
///
/// [`SysMods`]: enum.SysMods.html
#[derive(Debug, Clone)]
pub struct HotKey {
pub(crate) mods: RawMods,
pub(crate) key: Key,
}

/// A convenience trait for creating Key objects.
///
/// This trait is implemented by [`Key`] itself and also strings, which are
/// converted into the `Character` variant. It is defined this way and not
/// using the standard `Into` mechanism because `Key` is a type in an external
/// crate.
///
/// [`Key`]: keyboard_types::Key
pub trait IntoKey {
fn into_key(self) -> Key;
pub(crate) key: KbKey,
}

impl HotKey {
Expand All @@ -83,16 +69,15 @@ impl HotKey {
/// 'Command' key on macOS with the 'Ctrl' key on other platforms.
///
/// The second argument describes the non-modifier key. This can be either
/// a `&str` or a [`Key`]; the former is merely a convenient
/// shorthand for `Key::Character()`.
/// a `&str` or a [`KbKey`]; the former is merely a convenient
/// shorthand for `KbKey::Character()`.
///
/// # Examples
/// ```
/// use druid_shell::{HotKey, RawMods, SysMods};
/// use druid_shell::keyboard_types::Key;
/// use druid_shell::{HotKey, KbKey, RawMods, SysMods};
///
/// let select_all = HotKey::new(SysMods::Cmd, "a");
/// let esc = HotKey::new(None, Key::Escape);
/// let esc = HotKey::new(None, KbKey::Escape);
/// let macos_fullscreen = HotKey::new(RawMods::CtrlMeta, "f");
/// ```
///
Expand All @@ -109,7 +94,7 @@ impl HotKey {

//TODO: figure out if we need to be normalizing case or something?
fn warn_if_needed(self) -> Self {
if let Key::Character(s) = &self.key {
if let KbKey::Character(s) = &self.key {
let km: Modifiers = self.mods.into();
if km.contains(Modifiers::SHIFT) && s.chars().any(|c| c.is_uppercase()) {
warn!(
Expand All @@ -125,7 +110,7 @@ impl HotKey {
/// Returns `true` if this [`KeyboardEvent`] matches this `HotKey`.
///
/// [`KeyboardEvent`]: keyboard_types::KeyEvent
pub fn matches(&self, event: impl Borrow<KeyboardEvent>) -> bool {
pub fn matches(&self, event: impl Borrow<KeyEvent>) -> bool {
let event = event.borrow();
self.mods == event.modifiers && self.key == event.key
}
Expand Down Expand Up @@ -221,18 +206,10 @@ impl From<RawMods> for Modifiers {
RawMods::AltCtrlMetaShift => (true, true, true, true),
};
let mut mods = Modifiers::empty();
if alt {
mods |= Modifiers::ALT;
}
if ctrl {
mods |= Modifiers::CONTROL;
}
if meta {
mods |= Modifiers::META;
}
if shift {
mods |= Modifiers::SHIFT;
}
mods.set(Modifiers::ALT, alt);
mods.set(Modifiers::CONTROL, ctrl);
mods.set(Modifiers::META, meta);
mods.set(Modifiers::SHIFT, shift);
mods
}
}
Expand Down Expand Up @@ -266,31 +243,3 @@ impl From<SysMods> for RawMods {
}
}
}

impl IntoKey for Key {
fn into_key(self) -> Key {
self
}
}

impl IntoKey for &str {
fn into_key(self) -> Key {
Key::Character(self.into())
}
}

#[allow(unused)]
/// Create a key event for testing purposes.
pub fn key_event_for_test(mods: impl Into<Modifiers>, key: impl IntoKey) -> KeyboardEvent {
let modifiers = mods.into();
let key = key.into_key();
KeyboardEvent {
key,
code: Code::Unidentified,
location: Location::Standard,
state: KeyState::Down,
modifiers,
is_composing: false,
repeat: false,
}
}
Loading

0 comments on commit 01abeb6

Please sign in to comment.