use schemars::JsonSchema;use serde::{Deserialize, Serialize};use std::{ error::Error, fmt::{Display, Write},}; use crate::PlatformKeyboardMapper; /// This is a helper trait so that we can simplify the implementation of some functionspub trait AsKeystroke { /// Returns the GPUI representation of the keystroke. fn as_keystroke(&self) -> &Keystroke;} /// A keystroke and associated metadata generated by the platform#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)]pub struct Keystroke { /// the state of the modifier keys at the time the keystroke was generated pub modifiers: Modifiers, /// key is the character printed on the key that was pressed /// e.g. for option-s, key is "s" /// On layouts that do not have ascii keys (e.g. Thai) /// this will be the ASCII-equivalent character (q instead of ๆ), /// and the typed character will be present in key_char. pub key: String, /// key_char is the character that could have been typed when /// this binding was pressed. /// e.g. for s this is "s", for option-s "ß", and cmd-s None pub key_char: Option<String>,} /// Represents a keystroke that can be used in keybindings and displayed to the user.#[derive(Debug, Clone, Eq, PartialEq, Hash)]pub struct KeybindingKeystroke { /// The GPUI representation of the keystroke. inner: Keystroke, /// The modifiers to display. #[cfg(target_os = "windows")] display_modifiers: Modifiers, /// The key to display. #[cfg(target_os = "windows")] display_key: String,} /// Error type for `Keystroke::parse`. This is used instead of `anyhow::Error` so that Zed can use/// markdown to display it.#[derive(Debug)]pub struct InvalidKeystrokeError { /// The invalid keystroke. pub keystroke: String,} impl Error for InvalidKeystrokeError {} impl Display for InvalidKeystrokeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "Invalid keystroke \"{}\". {}", self.keystroke, KEYSTROKE_PARSE_EXPECTED_MESSAGE ) }} /// Sentence explaining what keystroke parser expects, starting with "Expected ..."pub const KEYSTROKE_PARSE_EXPECTED_MESSAGE: &str = "Expected a sequence of modifiers \ (`ctrl`, `alt`, `shift`, `fn`, `cmd`, `super`, or `win`) \ followed by a key, separated by `-`."; impl Keystroke { /// When matching a key we cannot know whether the user intended to type /// the key_char or the key itself. On some non-US keyboards keys we use in our /// bindings are behind option (for example `$` is typed `alt-ç` on a Czech keyboard), /// and on some keyboards the IME handler converts a sequence of keys into a /// specific character (for example `"` is typed as `" space` on a brazilian keyboard). /// /// This method assumes that `self` was typed and `target' is in the keymap, and checks /// both possibilities for self against the target. pub fn should_match(&self, target: &KeybindingKeystroke) -> bool { #[cfg(not(target_os = "windows"))] if let Some(key_char) = self .key_char .as_ref() .filter(|key_char| key_char != &&self.key) { let ime_modifiers = Modifiers { control: self.modifiers.control, platform: self.modifiers.platform, ..Default::default() }; if &target.inner.key == key_char && target.inner.modifiers == ime_modifiers { return true; } } #[cfg(target_os = "windows")] if let Some(key_char) = self .key_char .as_ref() .filter(|key_char| key_char != &&self.key) { // On Windows, if key_char is set, then the typed keystroke produced the key_char if &target.inner.key == key_char && target.inner.modifiers == Modifiers::none() { return true; } } target.inner.modifiers == self.modifiers && target.inner.key == self.key } /// key syntax is: /// [secondary-][ctrl-][alt-][shift-][cmd-][fn-]key[->key_char] /// key_char syntax is only used for generating test events, /// secondary means "cmd" on macOS and "ctrl" on other platforms /// when matching a key with an key_char set will be matched without it. pub fn parse(source: &str) -> std::result::Result<Self, InvalidKeystrokeError> { let mut modifiers = Modifiers::none(); let mut key = None; let mut key_char = None; let mut components = source.split('-').peekable(); while let Some(component) = components.next() { if component.eq_ignore_ascii_case("ctrl") { modifiers.control = true; continue; } if component.eq_ignore_ascii_case("alt") { modifiers.alt = true; continue; } if component.eq_ignore_ascii_case("shift") { modifiers.shift = true; continue; } if component.eq_ignore_ascii_case("fn") { modifiers.function = true; continue; } if component.eq_ignore_ascii_case("secondary") { if cfg!(target_os = "macos") { modifiers.platform = true; } else { modifiers.control = true; }; continue; } let is_platform = component.eq_ignore_ascii_case("cmd") || component.eq_ignore_ascii_case("super") || component.eq_ignore_ascii_case("win"); if is_platform { modifiers.platform = true; continue; } let mut key_str = component.to_string(); if let Some(next) = components.peek() { if next.is_empty() && source.ends_with('-') { key = Some(String::from("-")); break; } else if next.len() > 1 && next.starts_with('>') { key = Some(key_str); key_char = Some(String::from(&next[1..])); components.next(); } else { return Err(InvalidKeystrokeError { keystroke: source.to_owned(), }); } continue; } if component.len() == 1 && component.as_bytes()[0].is_ascii_uppercase() { // Convert to shift + lowercase char modifiers.shift = true; key_str.make_ascii_lowercase(); } else { // convert ascii chars to lowercase so that named keys like "tab" and "enter" // are accepted case insensitively and stored how we expect so they are matched properly key_str.make_ascii_lowercase() } key = Some(key_str); } // Allow for the user to specify a keystroke modifier as the key itself // This sets the `key` to the modifier, and disables the modifier key = key.or_else(|| { use std::mem; // std::mem::take clears bool incase its true if mem::take(&mut modifiers.shift) { Some("shift".to_string()) } else if mem::take(&mut modifiers.control) { Some("control".to_string()) } else if mem::take(&mut modifiers.alt) { Some("alt".to_string()) } else if mem::take(&mut modifiers.platform) { Some("platform".to_string()) } else if mem::take(&mut modifiers.function) { Some("function".to_string()) } else { None } }); let key = key.ok_or_else(|| InvalidKeystrokeError { keystroke: source.to_owned(), })?; Ok(Keystroke { modifiers, key, key_char, }) } /// Produces a representation of this key that Parse can understand. pub fn unparse(&self) -> String { unparse(&self.modifiers, &self.key) } /// Returns true if this keystroke left /// the ime system in an incomplete state. pub fn is_ime_in_progress(&self) -> bool { self.key_char.is_none() && (is_printable_key(&self.key) || self.key.is_empty()) && !(self.modifiers.platform || self.modifiers.control || self.modifiers.function || self.modifiers.alt) } /// Returns a new keystroke with the key_char filled. /// This is used for dispatch_keystroke where we want users to /// be able to simulate typing "space", etc. pub fn with_simulated_ime(mut self) -> Self { if self.key_char.is_none() && !self.modifiers.platform && !self.modifiers.control && !self.modifiers.function && !self.modifiers.alt { self.key_char = match self.key.as_str() { "space" => Some(" ".into()), "tab" => Some("\t".into()), "enter" => Some("\n".into()), key if !is_printable_key(key) || key.is_empty() => None, key => { if self.modifiers.shift { Some(key.to_uppercase()) } else { Some(key.into()) } } } } self }} impl KeybindingKeystroke { #[cfg(target_os = "windows")] #[expect(missing_docs)] pub fn new(inner: Keystroke, display_modifiers: Modifiers, display_key: String) -> Self { KeybindingKeystroke { inner, display_modifiers, display_key, } } /// Create a new keybinding keystroke from the given keystroke using the given keyboard mapper. pub fn new_with_mapper( inner: Keystroke, use_key_equivalents: bool, keyboard_mapper: &dyn PlatformKeyboardMapper, ) -> Self { keyboard_mapper.map_key_equivalent(inner, use_key_equivalents) } /// Create a new keybinding keystroke from the given keystroke, without any platform-specific mapping. pub fn from_keystroke(keystroke: Keystroke) -> Self { #[cfg(target_os = "windows")] { let key = keystroke.key.clone(); let modifiers = keystroke.modifiers; KeybindingKeystroke { inner: keystroke, display_modifiers: modifiers, display_key: key, } } #[cfg(not(target_os = "windows"))] { KeybindingKeystroke { inner: keystroke } } } /// Returns the GPUI representation of the keystroke. pub fn inner(&self) -> &Keystroke { &self.inner } /// Returns the modifiers. /// /// Platform-specific behavior: /// - On macOS and Linux, this modifiers is the same as `inner.modifiers`, which is the GPUI representation of the keystroke. /// - On Windows, this modifiers is the display modifiers, for example, a `ctrl-@` keystroke will have `inner.modifiers` as /// `Modifiers::control()` and `display_modifiers` as `Modifiers::control_shift()`. pub fn modifiers(&self) -> &Modifiers { #[cfg(target_os = "windows")] { &self.display_modifiers } #[cfg(not(target_os = "windows"))] { &self.inner.modifiers } } /// Returns the key. /// /// Platform-specific behavior: /// - On macOS and Linux, this key is the same as `inner.key`, which is the GPUI representation of the keystroke. /// - On Windows, this key is the display key, for example, a `ctrl-@` keystroke will have `inner.key` as `@` and `display_key` as `2`. pub fn key(&self) -> &str { #[cfg(target_os = "windows")] { &self.display_key } #[cfg(not(target_os = "windows"))] { &self.inner.key } } /// Sets the modifiers. On Windows this modifies both `inner.modifiers` and `display_modifiers`. pub fn set_modifiers(&mut self, modifiers: Modifiers) { self.inner.modifiers = modifiers; #[cfg(target_os = "windows")] { self.display_modifiers = modifiers; } } /// Sets the key. On Windows this modifies both `inner.key` and `display_key`. pub fn set_key(&mut self, key: String) { #[cfg(target_os = "windows")] { self.display_key = key.clone(); } self.inner.key = key; } /// Produces a representation of this key that Parse can understand. pub fn unparse(&self) -> String { #[cfg(target_os = "windows")] { unparse(&self.display_modifiers, &self.display_key) } #[cfg(not(target_os = "windows"))] { unparse(&self.inner.modifiers, &self.inner.key) } } /// Removes the key_char pub fn remove_key_char(&mut self) { self.inner.key_char = None; }} fn is_printable_key(key: &str) -> bool { !matches!( key, "f1" | "f2" | "f3" | "f4" | "f5" | "f6" | "f7" | "f8" | "f9" | "f10" | "f11" | "f12" | "f13" | "f14" | "f15" | "f16" | "f17" | "f18" | "f19" | "f20" | "f21" | "f22" | "f23" | "f24" | "f25" | "f26" | "f27" | "f28" | "f29" | "f30" | "f31" | "f32" | "f33" | "f34" | "f35" | "backspace" | "delete" | "left" | "right" | "up" | "down" | "pageup" | "pagedown" | "insert" | "home" | "end" | "back" | "forward" | "escape" )} impl std::fmt::Display for Keystroke { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { display_modifiers(&self.modifiers, f)?; display_key(&self.key, f) }} impl std::fmt::Display for KeybindingKeystroke { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { display_modifiers(self.modifiers(), f)?; display_key(self.key(), f) }} /// The state of the modifier keys at some point in time#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize, Hash, JsonSchema)]pub struct Modifiers { /// The control key #[serde(default)] pub control: bool, /// The alt key /// Sometimes also known as the 'meta' key #[serde(default)] pub alt: bool, /// The shift key #[serde(default)] pub shift: bool, /// The command key, on macos /// the windows key, on windows /// the super key, on linux #[serde(default)] pub platform: bool, /// The function key #[serde(default)] pub function: bool,} impl Modifiers { /// Returns whether any modifier key is pressed. pub fn modified(&self) -> bool { self.control || self.alt || self.shift || self.platform || self.function } /// Whether the semantically 'secondary' modifier key is pressed. /// /// On macOS, this is the command key. /// On Linux and Windows, this is the control key. pub fn secondary(&self) -> bool { #[cfg(target_os = "macos")] { self.platform } #[cfg(not(target_os = "macos"))] { self.control } } /// Returns how many modifier keys are pressed. pub fn number_of_modifiers(&self) -> u8 { self.control as u8 + self.alt as u8 + self.shift as u8 + self.platform as u8Lines 1–500 of 776. Load more
use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::{ error::Error, fmt::{Display, Write}, }; use crate::PlatformKeyboardMapper; /// This is a helper trait so that we can simplify the implementation of some functions pub trait AsKeystroke { /// Returns the GPUI representation of the keystroke. fn as_keystroke(&self) -> &Keystroke; } /// A keystroke and associated metadata generated by the platform #[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)] pub struct Keystroke { /// the state of the modifier keys at the time the keystroke was generated pub modifiers: Modifiers, /// key is the character printed on the key that was pressed /// e.g. for option-s, key is "s" /// On layouts that do not have ascii keys (e.g. Thai) /// this will be the ASCII-equivalent character (q instead of ๆ), /// and the typed character will be present in key_char. pub key: String, /// key_char is the character that could have been typed when /// this binding was pressed. /// e.g. for s this is "s", for option-s "ß", and cmd-s None pub key_char: Option<String>, } /// Represents a keystroke that can be used in keybindings and displayed to the user. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeybindingKeystroke { /// The GPUI representation of the keystroke. inner: Keystroke, /// The modifiers to display. #[cfg(target_os = "windows")] display_modifiers: Modifiers, /// The key to display. #[cfg(target_os = "windows")] display_key: String, } /// Error type for `Keystroke::parse`. This is used instead of `anyhow::Error` so that Zed can use /// markdown to display it. #[derive(Debug)] pub struct InvalidKeystrokeError { /// The invalid keystroke. pub keystroke: String, } impl Error for InvalidKeystrokeError {} impl Display for InvalidKeystrokeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "Invalid keystroke \"{}\". {}", self.keystroke, KEYSTROKE_PARSE_EXPECTED_MESSAGE ) } } /// Sentence explaining what keystroke parser expects, starting with "Expected ..." pub const KEYSTROKE_PARSE_EXPECTED_MESSAGE: &str = "Expected a sequence of modifiers \ (`ctrl`, `alt`, `shift`, `fn`, `cmd`, `super`, or `win`) \ followed by a key, separated by `-`."; impl Keystroke { /// When matching a key we cannot know whether the user intended to type /// the key_char or the key itself. On some non-US keyboards keys we use in our /// bindings are behind option (for example `$` is typed `alt-ç` on a Czech keyboard), /// and on some keyboards the IME handler converts a sequence of keys into a /// specific character (for example `"` is typed as `" space` on a brazilian keyboard). /// /// This method assumes that `self` was typed and `target' is in the keymap, and checks /// both possibilities for self against the target. pub fn should_match(&self, target: &KeybindingKeystroke) -> bool { #[cfg(not(target_os = "windows"))] if let Some(key_char) = self .key_char .as_ref() .filter(|key_char| key_char != &&self.key) { let ime_modifiers = Modifiers { control: self.modifiers.control, platform: self.modifiers.platform, ..Default::default() }; if &target.inner.key == key_char && target.inner.modifiers == ime_modifiers { return true; } } #[cfg(target_os = "windows")] if let Some(key_char) = self .key_char .as_ref() .filter(|key_char| key_char != &&self.key) { // On Windows, if key_char is set, then the typed keystroke produced the key_char if &target.inner.key == key_char && target.inner.modifiers == Modifiers::none() { return true; } } target.inner.modifiers == self.modifiers && target.inner.key == self.key } /// key syntax is: /// [secondary-][ctrl-][alt-][shift-][cmd-][fn-]key[->key_char] /// key_char syntax is only used for generating test events, /// secondary means "cmd" on macOS and "ctrl" on other platforms /// when matching a key with an key_char set will be matched without it. pub fn parse(source: &str) -> std::result::Result<Self, InvalidKeystrokeError> { let mut modifiers = Modifiers::none(); let mut key = None; let mut key_char = None; let mut components = source.split('-').peekable(); while let Some(component) = components.next() { if component.eq_ignore_ascii_case("ctrl") { modifiers.control = true; continue; } if component.eq_ignore_ascii_case("alt") { modifiers.alt = true; continue; } if component.eq_ignore_ascii_case("shift") { modifiers.shift = true; continue; } if component.eq_ignore_ascii_case("fn") { modifiers.function = true; continue; } if component.eq_ignore_ascii_case("secondary") { if cfg!(target_os = "macos") { modifiers.platform = true; } else { modifiers.control = true; }; continue; } let is_platform = component.eq_ignore_ascii_case("cmd") || component.eq_ignore_ascii_case("super") || component.eq_ignore_ascii_case("win"); if is_platform { modifiers.platform = true; continue; } let mut key_str = component.to_string(); if let Some(next) = components.peek() { if next.is_empty() && source.ends_with('-') { key = Some(String::from("-")); break; } else if next.len() > 1 && next.starts_with('>') { key = Some(key_str); key_char = Some(String::from(&next[1..])); components.next(); } else { return Err(InvalidKeystrokeError { keystroke: source.to_owned(), }); } continue; } if component.len() == 1 && component.as_bytes()[0].is_ascii_uppercase() { // Convert to shift + lowercase char modifiers.shift = true; key_str.make_ascii_lowercase(); } else { // convert ascii chars to lowercase so that named keys like "tab" and "enter" // are accepted case insensitively and stored how we expect so they are matched properly key_str.make_ascii_lowercase() } key = Some(key_str); } // Allow for the user to specify a keystroke modifier as the key itself // This sets the `key` to the modifier, and disables the modifier key = key.or_else(|| { use std::mem; // std::mem::take clears bool incase its true if mem::take(&mut modifiers.shift) { Some("shift".to_string()) } else if mem::take(&mut modifiers.control) { Some("control".to_string()) } else if mem::take(&mut modifiers.alt) { Some("alt".to_string()) } else if mem::take(&mut modifiers.platform) { Some("platform".to_string()) } else if mem::take(&mut modifiers.function) { Some("function".to_string()) } else { None } }); let key = key.ok_or_else(|| InvalidKeystrokeError { keystroke: source.to_owned(), })?; Ok(Keystroke { modifiers, key, key_char, }) } /// Produces a representation of this key that Parse can understand. pub fn unparse(&self) -> String { unparse(&self.modifiers, &self.key) } /// Returns true if this keystroke left /// the ime system in an incomplete state. pub fn is_ime_in_progress(&self) -> bool { self.key_char.is_none() && (is_printable_key(&self.key) || self.key.is_empty()) && !(self.modifiers.platform || self.modifiers.control || self.modifiers.function || self.modifiers.alt) } /// Returns a new keystroke with the key_char filled. /// This is used for dispatch_keystroke where we want users to /// be able to simulate typing "space", etc. pub fn with_simulated_ime(mut self) -> Self { if self.key_char.is_none() && !self.modifiers.platform && !self.modifiers.control && !self.modifiers.function && !self.modifiers.alt { self.key_char = match self.key.as_str() { "space" => Some(" ".into()), "tab" => Some("\t".into()), "enter" => Some("\n".into()), key if !is_printable_key(key) || key.is_empty() => None, key => { if self.modifiers.shift { Some(key.to_uppercase()) } else { Some(key.into()) } } } } self } } impl KeybindingKeystroke { #[cfg(target_os = "windows")] #[expect(missing_docs)] pub fn new(inner: Keystroke, display_modifiers: Modifiers, display_key: String) -> Self { KeybindingKeystroke { inner, display_modifiers, display_key, } } /// Create a new keybinding keystroke from the given keystroke using the given keyboard mapper. pub fn new_with_mapper( inner: Keystroke, use_key_equivalents: bool, keyboard_mapper: &dyn PlatformKeyboardMapper, ) -> Self { keyboard_mapper.map_key_equivalent(inner, use_key_equivalents) } /// Create a new keybinding keystroke from the given keystroke, without any platform-specific mapping. pub fn from_keystroke(keystroke: Keystroke) -> Self { #[cfg(target_os = "windows")] { let key = keystroke.key.clone(); let modifiers = keystroke.modifiers; KeybindingKeystroke { inner: keystroke, display_modifiers: modifiers, display_key: key, } } #[cfg(not(target_os = "windows"))] { KeybindingKeystroke { inner: keystroke } } } /// Returns the GPUI representation of the keystroke. pub fn inner(&self) -> &Keystroke { &self.inner } /// Returns the modifiers. /// /// Platform-specific behavior: /// - On macOS and Linux, this modifiers is the same as `inner.modifiers`, which is the GPUI representation of the keystroke. /// - On Windows, this modifiers is the display modifiers, for example, a `ctrl-@` keystroke will have `inner.modifiers` as /// `Modifiers::control()` and `display_modifiers` as `Modifiers::control_shift()`. pub fn modifiers(&self) -> &Modifiers { #[cfg(target_os = "windows")] { &self.display_modifiers } #[cfg(not(target_os = "windows"))] { &self.inner.modifiers } } /// Returns the key. /// /// Platform-specific behavior: /// - On macOS and Linux, this key is the same as `inner.key`, which is the GPUI representation of the keystroke. /// - On Windows, this key is the display key, for example, a `ctrl-@` keystroke will have `inner.key` as `@` and `display_key` as `2`. pub fn key(&self) -> &str { #[cfg(target_os = "windows")] { &self.display_key } #[cfg(not(target_os = "windows"))] { &self.inner.key } } /// Sets the modifiers. On Windows this modifies both `inner.modifiers` and `display_modifiers`. pub fn set_modifiers(&mut self, modifiers: Modifiers) { self.inner.modifiers = modifiers; #[cfg(target_os = "windows")] { self.display_modifiers = modifiers; } } /// Sets the key. On Windows this modifies both `inner.key` and `display_key`. pub fn set_key(&mut self, key: String) { #[cfg(target_os = "windows")] { self.display_key = key.clone(); } self.inner.key = key; } /// Produces a representation of this key that Parse can understand. pub fn unparse(&self) -> String { #[cfg(target_os = "windows")] { unparse(&self.display_modifiers, &self.display_key) } #[cfg(not(target_os = "windows"))] { unparse(&self.inner.modifiers, &self.inner.key) } } /// Removes the key_char pub fn remove_key_char(&mut self) { self.inner.key_char = None; } } fn is_printable_key(key: &str) -> bool { !matches!( key, "f1" | "f2" | "f3" | "f4" | "f5" | "f6" | "f7" | "f8" | "f9" | "f10" | "f11" | "f12" | "f13" | "f14" | "f15" | "f16" | "f17" | "f18" | "f19" | "f20" | "f21" | "f22" | "f23" | "f24" | "f25" | "f26" | "f27" | "f28" | "f29" | "f30" | "f31" | "f32" | "f33" | "f34" | "f35" | "backspace" | "delete" | "left" | "right" | "up" | "down" | "pageup" | "pagedown" | "insert" | "home" | "end" | "back" | "forward" | "escape" ) } impl std::fmt::Display for Keystroke { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { display_modifiers(&self.modifiers, f)?; display_key(&self.key, f) } } impl std::fmt::Display for KeybindingKeystroke { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { display_modifiers(self.modifiers(), f)?; display_key(self.key(), f) } } /// The state of the modifier keys at some point in time #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize, Hash, JsonSchema)] pub struct Modifiers { /// The control key #[serde(default)] pub control: bool, /// The alt key /// Sometimes also known as the 'meta' key #[serde(default)] pub alt: bool, /// The shift key #[serde(default)] pub shift: bool, /// The command key, on macos /// the windows key, on windows /// the super key, on linux #[serde(default)] pub platform: bool, /// The function key #[serde(default)] pub function: bool, } impl Modifiers { /// Returns whether any modifier key is pressed. pub fn modified(&self) -> bool { self.control || self.alt || self.shift || self.platform || self.function } /// Whether the semantically 'secondary' modifier key is pressed. /// /// On macOS, this is the command key. /// On Linux and Windows, this is the control key. pub fn secondary(&self) -> bool { #[cfg(target_os = "macos")] { self.platform } #[cfg(not(target_os = "macos"))] { self.control } } /// Returns how many modifier keys are pressed. pub fn number_of_modifiers(&self) -> u8 { self.control as u8 + self.alt as u8 + self.shift as u8 + self.platform as u8