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
  • Loading branch information
raphlinus committed Jun 26, 2020
1 parent a431c81 commit 06f06c2
Show file tree
Hide file tree
Showing 25 changed files with 662 additions and 498 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 06f06c2

Please sign in to comment.