Skip to content

Commit

Permalink
Add support for tracking modifierFlags on key events (#138)
Browse files Browse the repository at this point in the history
* add support for getting modifierFlags

to track which modifier keys are currently pressed with the ability to differentiate left vs. right

* add custom display format for EventModifierBitFlag, printing key symbols
  • Loading branch information
eugenesvk authored Dec 28, 2024
1 parent d36c5c9 commit 6ba7e7d
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
81 changes: 81 additions & 0 deletions src/appkit/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use objc::{class, msg_send, msg_send_id, sel};
use crate::events::EventType;
use crate::foundation::{id, nil, NSInteger, NSPoint, NSString};

mod test;

/// An EventMask describes the type of event.
#[bitmask(u64)]
pub enum EventMask {
Expand Down Expand Up @@ -83,6 +85,18 @@ impl Event {
characters.to_string()
}

/// An integer bit field that indicates the pressed modifier keys
pub fn modifier_flags(&self) -> EventModifierBitFlag {
let flags: NSUInteger = unsafe { msg_send![&*self.0, modifierFlags] };

flags.into()
}

/// A raw integer bit field that indicates the pressed modifier keys
pub fn modifier_flags_raw(&self) -> NSUInteger {
unsafe { msg_send![&*self.0, modifierFlags] }
}

/// The indices of the currently pressed mouse buttons.
pub fn pressed_mouse_buttons() -> NSUInteger {
unsafe { msg_send![class!(NSEvent), pressedMouseButtons] }
Expand Down Expand Up @@ -209,3 +223,70 @@ impl From<&EventModifierFlag> for NSUInteger {
}
}
}

// #[cfg(target_pointer_width = "32")] //@TODO contitional compilation fails, so always use 64
// #[bitmask(u32)]
// #[cfg(target_pointer_width = "64")]
/// Flags that indicate which modifier keys are in the mix for an event.
#[bitmask(u64)]
#[bitmask_config(inverted_flags)]
pub enum EventModifierBitFlag {
CapsLock = 1 << 16,
LeftShift =(1 << 17) + (1 << 1),
RightShift =(1 << 17) + (1 << 2),
LeftControl =(1 << 18) + (1 << 0),
RightControl =(1 << 18) + (1 << 13),
LeftOption =(1 << 19) + (1 << 5),
RightOption =(1 << 19) + (1 << 6),
LeftCommand =(1 << 20) + (1 << 3),
RightCommand =(1 << 20) + (1 << 4),
Shift = Self::LeftShift .bits | Self::RightShift .bits,
Control = Self::LeftControl.bits | Self::RightControl.bits,
Option = Self::LeftOption .bits | Self::RightOption .bits,
Command = Self::LeftCommand.bits | Self::RightCommand.bits,
LeftModi = Self::LeftShift .bits | Self::LeftControl .bits | Self::LeftOption .bits | Self::LeftCommand .bits,
RightModi = Self::RightShift .bits | Self::RightControl.bits | Self::RightOption.bits | Self::RightCommand.bits,
LRModi = Self::LeftModi .bits | Self::RightModi .bits,
Numpad = 1 << 21,
Help = 1 << 22,
Function = 1 << 23,
DeviceIndependentFlagsMask = 0xffff0000,
}

use std::fmt;
impl fmt::Display for EventModifierBitFlag {
fn fmt(&self, f:&mut fmt::Formatter) -> fmt::Result {
// key combos with flagmasks don't make sense? so just print the mask and return, ignoring other bits
if self.contains(EventModifierBitFlag::DeviceIndependentFlagsMask) {write!(f,"!🖮")?;return fmt::Result::Ok(())};

if self.contains(EventModifierBitFlag::CapsLock ) {write!(f,"⇪")?;}
if self.contains(EventModifierBitFlag::Shift ) {write!(f,"‹⇧›")?;
} else {
if self.contains(EventModifierBitFlag::LeftShift ) {write!(f,"‹⇧")?;}
if self.contains(EventModifierBitFlag::RightShift ) {write!(f,"⇧›")?;}
} ;
if self.contains(EventModifierBitFlag::Control ) {write!(f,"‹⌃›")?;
} else {
if self.contains(EventModifierBitFlag::LeftControl ) {write!(f,"‹⌃")?;}
if self.contains(EventModifierBitFlag::RightControl ) {write!(f,"⌃›")?;}
} ;
if self.contains(EventModifierBitFlag::Option ) {write!(f,"‹⌥›")?;
} else {
if self.contains(EventModifierBitFlag::LeftOption ) {write!(f,"‹⌥")?;}
if self.contains(EventModifierBitFlag::RightOption ) {write!(f,"⌥›")?;}
} ;
if self.contains(EventModifierBitFlag::Command ) {write!(f,"‹⌘›")?;
} else {
if self.contains(EventModifierBitFlag::LeftCommand ) {write!(f,"‹⌘")?;}
if self.contains(EventModifierBitFlag::RightCommand ) {write!(f,"⌘›")?;}
} ;
if self.contains(EventModifierBitFlag::Function ) {
if f.alternate() {write!(f,"🌐")?; // when it's a modifier key
} else {write!(f,"ƒ")?;}}
if self.contains(EventModifierBitFlag::Numpad ) {
if f.alternate() {write!(f,"⇭")?; // when it's a modifier key
} else {write!(f,"🔢")?;}}
if self.contains(EventModifierBitFlag::Help ) {write!(f,"ℹ")?;}
fmt::Result::Ok(())
}
}
21 changes: 21 additions & 0 deletions src/appkit/event/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#[cfg(test)]
mod event_test {
use crate::appkit::EventModifierBitFlag;
#[test]
fn test_event_modifier_bit_flag_display() {
assert_eq!("‹⇧" , format!("{}",EventModifierBitFlag::LeftShift));
assert_eq!("‹⇧›" , format!("{}",EventModifierBitFlag::Shift));
assert_eq!("⇧›" , format!("{}",EventModifierBitFlag::RightShift));
assert_eq!("‹⇧‹⌃‹⌥‹⌘" , format!("{}",EventModifierBitFlag::LeftModi));
assert_eq!("⇧›⌃›⌥›⌘›" , format!("{}",EventModifierBitFlag::RightModi));
assert_eq!("‹⇧›‹⌃›‹⌥›‹⌘›" , format!("{}",EventModifierBitFlag::LRModi));
assert_eq!("🌐" , format!("{:#}",EventModifierBitFlag::Function)); // when it's a modifier key
assert_eq!("ƒ" , format!("{}",EventModifierBitFlag::Function));
assert_eq!("⇭" , format!("{:#}",EventModifierBitFlag::Numpad)); // when it's a modifier key
assert_eq!("🔢" , format!("{}",EventModifierBitFlag::Numpad));
assert_eq!("ℹ" , format!("{}",EventModifierBitFlag::Help));
assert_eq!("!🖮" , format!("{}",EventModifierBitFlag::DeviceIndependentFlagsMask));
// Shift ignored, only flagmask remains
assert_eq!("!🖮" , format!("{}",EventModifierBitFlag::DeviceIndependentFlagsMask | EventModifierBitFlag::RightShift));
}
}

0 comments on commit 6ba7e7d

Please sign in to comment.