1use std::fmt;
7
8use bitflags::bitflags;
9use zng_txt::{ToTxt, Txt};
10use zng_unique_id::static_id;
11use zng_var::{Var, impl_from_and_into_var};
12
13#[doc(hidden)]
14pub use zng_view_api::keyboard::{Key, KeyCode};
15
16use crate::event::{Command, CommandMetaVar, CommandMetaVarId};
17
18#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
29pub enum GestureKey {
30 Key(Key),
32 Code(KeyCode),
34}
35impl std::hash::Hash for GestureKey {
36 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
37 core::mem::discriminant(self).hash(state);
38 match self {
39 GestureKey::Key(k) => match k {
40 Key::Char(c) => {
41 for c in c.to_uppercase() {
42 c.hash(state);
43 }
44 }
45 Key::Str(s) => {
46 unicase::UniCase::new(s).hash(state);
47 }
48 k => k.hash(state),
49 },
50 GestureKey::Code(c) => c.hash(state),
51 }
52 }
53}
54impl Eq for GestureKey {}
55impl PartialEq for GestureKey {
56 fn eq(&self, other: &Self) -> bool {
57 match (self, other) {
58 (Self::Key(l0), Self::Key(r0)) => match (l0, r0) {
59 (Key::Char(l), Key::Char(r)) => {
60 let mut l = l.to_uppercase();
61 let mut r = r.to_uppercase();
62
63 while let (Some(l), Some(r)) = (l.next(), r.next()) {
64 if l != r {
65 return false;
66 }
67 }
68
69 l.next().is_none() && r.next().is_none()
70 }
71 (Key::Str(l), Key::Str(r)) => unicase::eq(l, r),
72 (l0, r0) => l0 == r0,
73 },
74 (Self::Code(l0), Self::Code(r0)) => l0 == r0,
75 _ => false,
76 }
77 }
78}
79impl fmt::Display for GestureKey {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 GestureKey::Key(k) => match k {
83 Key::Char(c) => write!(f, "{}", c.to_uppercase()),
84 Key::Str(s) => write!(f, "{s}"),
85 Key::ArrowLeft => write!(f, "←"),
86 Key::ArrowRight => write!(f, "→"),
87 Key::ArrowUp => write!(f, "↑"),
88 Key::ArrowDown => write!(f, "↑"),
89 k => write!(f, "{k:?}"),
90 },
91 GestureKey::Code(c) => write!(f, "{c:?}"),
92 }
93 }
94}
95impl GestureKey {
96 pub fn is_valid(&self) -> bool {
104 match self {
105 GestureKey::Key(k) => !k.is_modifier() && !k.is_composition() && *k != Key::Unidentified,
106 GestureKey::Code(k) => k.is_modifier() && !k.is_composition(),
107 }
108 }
109}
110
111impl TryFrom<Key> for GestureKey {
116 type Error = Key;
117
118 fn try_from(key: Key) -> Result<Self, Self::Error> {
119 if key.is_modifier() || key.is_composition() || key == Key::Unidentified {
120 Err(key)
121 } else {
122 Ok(Self::Key(key))
123 }
124 }
125}
126impl TryFrom<KeyCode> for GestureKey {
131 type Error = KeyCode;
132
133 fn try_from(key: KeyCode) -> Result<Self, Self::Error> {
134 if key.is_modifier() || key.is_composition() || key.is_unidentified() {
135 Err(key)
136 } else {
137 Ok(Self::Code(key))
138 }
139 }
140}
141impl std::str::FromStr for GestureKey {
142 type Err = ParseError;
143
144 fn from_str(s: &str) -> Result<Self, Self::Err> {
145 match Key::from_str(s) {
146 Key::Str(s) => match KeyCode::from_str(&s) {
147 Ok(k) => {
148 let key = k
149 .try_into()
150 .map_err(|e| ParseError::new(format!("key `{e:?}` cannot be used in gestures")))?;
151
152 Ok(key)
153 }
154 Err(_) => Ok(Self::Key(Key::Str(s))),
155 },
156 k => {
157 let key = k
158 .try_into()
159 .map_err(|e| ParseError::new(format!("key `{e:?}` cannot be used in gestures")))?;
160
161 Ok(key)
162 }
163 }
164 }
165}
166
167#[derive(Clone, serde::Serialize, serde::Deserialize)]
169pub struct KeyGesture {
170 pub modifiers: ModifiersState,
177 pub key: GestureKey,
179}
180impl PartialEq for KeyGesture {
181 fn eq(&self, other: &Self) -> bool {
182 self.modifiers.ambit() == other.modifiers.ambit() && self.key == other.key
183 }
184}
185impl Eq for KeyGesture {}
186impl std::hash::Hash for KeyGesture {
187 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
188 self.modifiers.ambit().hash(state);
189 self.key.hash(state);
190 }
191}
192impl KeyGesture {
193 pub fn new(modifiers: ModifiersState, key: GestureKey) -> Self {
195 KeyGesture { modifiers, key }
196 }
197
198 pub fn new_key(key: GestureKey) -> Self {
200 KeyGesture {
201 modifiers: ModifiersState::empty(),
202 key,
203 }
204 }
205
206 pub fn is_valid(&self) -> bool {
208 self.key.is_valid()
209 }
210}
211impl fmt::Debug for KeyGesture {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 if f.alternate() {
214 f.debug_struct("KeyGesture")
215 .field("modifiers", &self.modifiers)
216 .field("key", &self.key)
217 .finish()
218 } else {
219 write!(f, "{self}")
220 }
221 }
222}
223impl fmt::Display for KeyGesture {
224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225 if self.modifiers.has_super() {
226 write!(f, "Super+")?
227 }
228 if self.modifiers.has_ctrl() {
229 write!(f, "Ctrl+")?
230 }
231 if self.modifiers.has_shift() {
232 write!(f, "Shift+")?
233 }
234 if self.modifiers.has_alt() {
235 write!(f, "Alt+")?
236 }
237
238 write!(f, "{}", self.key)
239 }
240}
241
242#[derive(Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
244pub enum ModifierGesture {
245 Super,
247 Ctrl,
249 Shift,
251 Alt,
253}
254impl fmt::Debug for ModifierGesture {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 if f.alternate() {
257 write!(f, "ModifierGesture::")?;
258 }
259 write!(f, "{self}")
260 }
261}
262impl fmt::Display for ModifierGesture {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 match self {
265 ModifierGesture::Super => write!(f, "Super"),
266 ModifierGesture::Ctrl => write!(f, "Ctrl"),
267 ModifierGesture::Shift => write!(f, "Shift"),
268 ModifierGesture::Alt => write!(f, "Alt"),
269 }
270 }
271}
272impl<'a> TryFrom<&'a Key> for ModifierGesture {
273 type Error = &'a Key;
274 fn try_from(value: &'a Key) -> Result<Self, Self::Error> {
275 match value {
276 Key::Alt | Key::AltGraph => Ok(ModifierGesture::Alt),
277 Key::Ctrl => Ok(ModifierGesture::Ctrl),
278 Key::Shift => Ok(ModifierGesture::Shift),
279 Key::Super => Ok(ModifierGesture::Super),
280 key => Err(key),
281 }
282 }
283}
284impl TryFrom<KeyCode> for ModifierGesture {
285 type Error = KeyCode;
286 fn try_from(value: KeyCode) -> Result<Self, Self::Error> {
287 match value {
288 KeyCode::AltLeft | KeyCode::AltRight => Ok(ModifierGesture::Alt),
289 KeyCode::CtrlLeft | KeyCode::CtrlRight => Ok(ModifierGesture::Ctrl),
290 KeyCode::ShiftLeft | KeyCode::ShiftRight => Ok(ModifierGesture::Shift),
291 KeyCode::SuperLeft | KeyCode::SuperRight => Ok(ModifierGesture::Super),
292 key => Err(key),
293 }
294 }
295}
296impl ModifierGesture {
297 pub fn left_key(&self) -> (KeyCode, Key) {
299 match self {
300 ModifierGesture::Super => (KeyCode::SuperLeft, Key::Super),
301 ModifierGesture::Ctrl => (KeyCode::CtrlLeft, Key::Ctrl),
302 ModifierGesture::Shift => (KeyCode::ShiftLeft, Key::Shift),
303 ModifierGesture::Alt => (KeyCode::AltLeft, Key::Alt),
304 }
305 }
306 pub fn modifiers_state(&self) -> ModifiersState {
308 match self {
309 ModifierGesture::Super => ModifiersState::SUPER,
310 ModifierGesture::Ctrl => ModifiersState::CTRL,
311 ModifierGesture::Shift => ModifiersState::SHIFT,
312 ModifierGesture::Alt => ModifiersState::ALT,
313 }
314 }
315}
316
317#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
319pub struct KeyChord {
320 pub starter: KeyGesture,
322
323 pub complement: KeyGesture,
325}
326impl fmt::Debug for KeyChord {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 if f.alternate() {
329 f.debug_struct("KeyChord")
330 .field("starter", &self.starter)
331 .field("complement", &self.complement)
332 .finish()
333 } else {
334 write!(f, "{self}")
335 }
336 }
337}
338impl fmt::Display for KeyChord {
339 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
340 write!(f, "{} {}", self.starter, self.complement)
341 }
342}
343
344#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
348pub enum Shortcut {
349 Gesture(KeyGesture),
351 Chord(KeyChord),
353 Modifier(ModifierGesture),
355}
356impl fmt::Debug for Shortcut {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 if f.alternate() {
359 match self {
360 Shortcut::Gesture(g) => f.debug_tuple("Shortcut::Gesture").field(g).finish(),
361 Shortcut::Chord(c) => f.debug_tuple("Shortcut::Chord").field(c).finish(),
362 Shortcut::Modifier(m) => f.debug_tuple("Shortcut::Modifier").field(m).finish(),
363 }
364 } else {
365 write!(f, "{self}")
366 }
367 }
368}
369impl fmt::Display for Shortcut {
370 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
371 match self {
372 Shortcut::Gesture(g) => fmt::Display::fmt(g, f),
373 Shortcut::Chord(c) => fmt::Display::fmt(c, f),
374 Shortcut::Modifier(m) => fmt::Display::fmt(m, f),
375 }
376 }
377}
378impl Shortcut {
379 pub fn modifiers_state(&self) -> ModifiersState {
381 match self {
382 Shortcut::Gesture(g) => g.modifiers,
383 Shortcut::Chord(c) => c.complement.modifiers,
384 Shortcut::Modifier(m) => m.modifiers_state(),
385 }
386 }
387
388 pub fn is_valid(&self) -> bool {
390 match self {
391 Shortcut::Gesture(k) => k.is_valid(),
392 Shortcut::Chord(c) => c.starter.is_valid() && c.complement.is_valid(),
393 Shortcut::Modifier(_) => true,
394 }
395 }
396}
397impl_from_and_into_var! {
398 fn from(shortcut: Shortcut) -> Shortcuts {
399 Shortcuts(vec![shortcut])
400 }
401
402 fn from(key_gesture: KeyGesture) -> Shortcut {
403 Shortcut::Gesture(key_gesture)
404 }
405
406 fn from(key_chord: KeyChord) -> Shortcut {
407 Shortcut::Chord(key_chord)
408 }
409
410 fn from(modifier: ModifierGesture) -> Shortcut {
411 Shortcut::Modifier(modifier)
412 }
413
414 fn from(gesture_key: GestureKey) -> Shortcut {
415 KeyGesture::new_key(gesture_key).into()
416 }
417
418 fn from(gesture_key: GestureKey) -> Shortcuts {
419 Shortcuts(vec![gesture_key.into()])
420 }
421
422 fn from(key_gesture: KeyGesture) -> Shortcuts {
423 Shortcuts(vec![key_gesture.into()])
424 }
425
426 fn from(key_chord: KeyChord) -> Shortcuts {
427 Shortcuts(vec![key_chord.into()])
428 }
429
430 fn from(modifier: ModifierGesture) -> Shortcuts {
431 Shortcuts(vec![modifier.into()])
432 }
433
434 fn from(shortcuts: Vec<Shortcut>) -> Shortcuts {
435 Shortcuts(shortcuts)
436 }
437}
438impl<const N: usize> From<[Shortcut; N]> for Shortcuts {
439 fn from(a: [Shortcut; N]) -> Self {
440 Shortcuts(a.into())
441 }
442}
443impl<const N: usize> crate::var::IntoVar<Shortcuts> for [Shortcut; N] {
444 fn into_var(self) -> Var<Shortcuts> {
445 crate::var::const_var(self.into())
446 }
447}
448
449#[derive(Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
451pub struct Shortcuts(pub Vec<Shortcut>);
452impl Shortcuts {
453 pub const fn new() -> Self {
455 Self(vec![])
456 }
457
458 pub fn from_char(character: char) -> Result<Self, char> {
465 if character.is_control() {
466 Err(character)
467 } else {
468 Ok(Self(vec![Shortcut::Gesture(KeyGesture {
469 modifiers: ModifiersState::empty(),
470 key: GestureKey::Key(Key::Char(character)),
471 })]))
472 }
473 }
474
475 pub fn contains(&self, shortcut: &Shortcut) -> bool {
477 self.0.contains(shortcut)
478 }
479}
480impl TryFrom<char> for Shortcuts {
481 type Error = char;
482
483 fn try_from(value: char) -> Result<Self, Self::Error> {
485 Shortcuts::from_char(value)
486 }
487}
488impl fmt::Debug for Shortcuts {
489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490 if f.alternate() {
491 f.debug_tuple("Shortcuts").field(&self.0).finish()
492 } else {
493 write!(f, "[")?;
494 if !self.0.is_empty() {
495 if let Shortcut::Chord(c) = &self.0[0] {
496 write!(f, "({c:?})")?;
497 } else {
498 write!(f, "{:?}", self.0[0])?;
499 }
500 for shortcut in &self.0[1..] {
501 if let Shortcut::Chord(c) = shortcut {
502 write!(f, ", ({c:?})")?;
503 } else {
504 write!(f, ", {shortcut:?}")?;
505 }
506 }
507 }
508 write!(f, "]")
509 }
510 }
511}
512impl fmt::Display for Shortcuts {
513 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514 if !self.0.is_empty() {
515 write!(f, "{}", self.0[0])?;
516 for shortcut in &self.0[1..] {
517 write!(f, " | {shortcut}")?;
518 }
519 }
520 Ok(())
521 }
522}
523impl std::ops::Deref for Shortcuts {
524 type Target = Vec<Shortcut>;
525
526 fn deref(&self) -> &Self::Target {
527 &self.0
528 }
529}
530impl std::ops::DerefMut for Shortcuts {
531 fn deref_mut(&mut self) -> &mut Self::Target {
532 &mut self.0
533 }
534}
535
536#[derive(Debug, Clone, PartialEq, Eq)]
538#[non_exhaustive]
539pub struct ParseError {
540 pub error: String,
542}
543impl ParseError {
544 pub fn new(error: impl ToString) -> Self {
546 ParseError { error: error.to_string() }
547 }
548}
549impl fmt::Display for ParseError {
550 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
551 self.error.fmt(f)
552 }
553}
554impl std::error::Error for ParseError {}
555
556bitflags! {
557 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
561 #[serde(transparent)]
562 pub struct ModifiersState: u8 {
563 const L_SHIFT = 0b0000_0001;
565 const R_SHIFT = 0b0000_0010;
567 const SHIFT = 0b0000_0011;
569
570 const L_CTRL = 0b0000_0100;
572 const R_CTRL = 0b0000_1000;
574 const CTRL = 0b0000_1100;
576
577 const L_ALT = 0b0001_0000;
579 const R_ALT = 0b0010_0000;
581 const ALT = 0b0011_0000;
583
584 const L_SUPER = 0b0100_0000;
586 const R_SUPER = 0b1000_0000;
588 const SUPER = 0b1100_0000;
592 }
593}
594impl ModifiersState {
595 pub fn has_shift(self) -> bool {
597 self.intersects(Self::SHIFT)
598 }
599 pub fn has_ctrl(self) -> bool {
601 self.intersects(Self::CTRL)
602 }
603 pub fn has_alt(self) -> bool {
605 self.intersects(Self::ALT)
606 }
607 pub fn has_super(self) -> bool {
609 self.intersects(Self::SUPER)
610 }
611
612 pub fn is_only(self, part: ModifiersState) -> bool {
614 !self.is_empty() && (self - part).is_empty()
615 }
616
617 pub fn is_only_shift(self) -> bool {
619 self.is_only(ModifiersState::SHIFT)
620 }
621 pub fn is_only_ctrl(self) -> bool {
623 self.is_only(ModifiersState::CTRL)
624 }
625 pub fn is_only_alt(self) -> bool {
627 self.is_only(ModifiersState::ALT)
628 }
629 pub fn is_only_super(self) -> bool {
631 self.is_only(ModifiersState::SUPER)
632 }
633
634 pub fn take(&mut self, part: ModifiersState) -> bool {
636 let r = self.intersects(part);
637 if r {
638 self.remove(part);
639 }
640 r
641 }
642
643 pub fn take_shift(&mut self) -> bool {
645 self.take(ModifiersState::SHIFT)
646 }
647
648 pub fn take_ctrl(&mut self) -> bool {
650 self.take(ModifiersState::CTRL)
651 }
652
653 pub fn take_alt(&mut self) -> bool {
655 self.take(ModifiersState::ALT)
656 }
657
658 pub fn take_super(&mut self) -> bool {
660 self.take(ModifiersState::SUPER)
661 }
662
663 pub fn ambit(self) -> Self {
665 let mut r = Self::empty();
666 if self.has_alt() {
667 r |= Self::ALT;
668 }
669 if self.has_ctrl() {
670 r |= Self::CTRL;
671 }
672 if self.has_shift() {
673 r |= Self::SHIFT;
674 }
675 if self.has_super() {
676 r |= Self::SUPER;
677 }
678 r
679 }
680
681 pub fn into_alt(self) -> Self {
683 self & Self::ALT
684 }
685
686 pub fn into_ctrl(self) -> Self {
688 self & Self::CTRL
689 }
690
691 pub fn into_shift(self) -> Self {
693 self & Self::SHIFT
694 }
695
696 pub fn into_super(self) -> Self {
698 self & Self::SUPER
699 }
700
701 pub fn from_code(code: KeyCode) -> ModifiersState {
703 match code {
704 KeyCode::AltLeft => Self::L_ALT,
705 KeyCode::AltRight => Self::R_ALT,
706 KeyCode::CtrlLeft => Self::L_CTRL,
707 KeyCode::CtrlRight => Self::R_CTRL,
708 KeyCode::ShiftLeft => Self::L_SHIFT,
709 KeyCode::ShiftRight => Self::R_SHIFT,
710 KeyCode::SuperLeft => Self::L_SUPER,
711 KeyCode::SuperRight => Self::R_SUPER,
712 _ => Self::empty(),
713 }
714 }
715
716 pub fn from_key(key: Key) -> ModifiersState {
718 match key {
719 Key::Alt => Self::L_ALT,
720 Key::AltGraph => Self::R_ALT,
721 Key::Shift => Self::SHIFT,
722 Key::Ctrl => Self::CTRL,
723 Key::Super => Self::SUPER,
724 _ => Self::empty(),
725 }
726 }
727
728 pub fn codes(self) -> Vec<KeyCode> {
734 let mut r = vec![];
735
736 if self.contains(Self::L_SUPER) {
737 r.push(KeyCode::SuperLeft);
738 } else if self.contains(Self::R_SUPER) {
739 r.push(KeyCode::SuperRight);
740 }
741
742 if self.contains(Self::L_CTRL) {
743 r.push(KeyCode::CtrlLeft);
744 } else if self.contains(Self::R_CTRL) {
745 r.push(KeyCode::CtrlRight);
746 }
747
748 if self.contains(Self::L_SHIFT) {
749 r.push(KeyCode::ShiftLeft);
750 } else if self.contains(Self::R_SHIFT) {
751 r.push(KeyCode::ShiftRight);
752 }
753
754 if self.contains(Self::L_ALT) {
755 r.push(KeyCode::AltLeft);
756 } else if self.contains(Self::R_ALT) {
757 r.push(KeyCode::AltRight);
758 }
759
760 r
761 }
762
763 pub fn keys(self) -> Vec<Key> {
767 let mut r = vec![];
768
769 if self.intersects(Self::SUPER) {
770 r.push(Key::Super);
771 }
772
773 if self.intersects(Self::CTRL) {
774 r.push(Key::Ctrl);
775 }
776
777 if self.intersects(Self::SHIFT) {
778 r.push(Key::Shift);
779 }
780
781 if self.contains(Self::R_ALT) {
782 r.push(Key::AltGraph);
783 } else if self.contains(Self::R_ALT) {
784 r.push(Key::Alt);
785 }
786
787 r
788 }
789}
790
791pub trait CommandShortcutExt {
799 fn shortcut(self) -> CommandMetaVar<Shortcuts>;
801
802 fn shortcut_txt(self) -> Var<Txt>
804 where
805 Self: Sized,
806 {
807 self.shortcut().map(|c| if c.is_empty() { Txt::from("") } else { c[0].to_txt() })
808 }
809
810 fn shortcut_filter(self) -> CommandMetaVar<ShortcutFilter>;
814
815 fn init_shortcut(self, shortcut: impl Into<Shortcuts>) -> Self;
817
818 fn init_shortcut_filter(self, filter: impl Into<ShortcutFilter>) -> Self;
820}
821impl CommandShortcutExt for Command {
822 fn shortcut(self) -> CommandMetaVar<Shortcuts> {
823 self.with_meta(|m| m.get_var_or_default(*COMMAND_SHORTCUT_ID))
824 }
825
826 fn shortcut_filter(self) -> CommandMetaVar<ShortcutFilter> {
827 self.with_meta(|m| m.get_var_or_default(*COMMAND_SHORTCUT_FILTER_ID))
828 }
829
830 fn init_shortcut(self, shortcut: impl Into<Shortcuts>) -> Self {
831 self.with_meta(|m| m.init_var(*COMMAND_SHORTCUT_ID, shortcut.into()));
832 self
833 }
834
835 fn init_shortcut_filter(self, filter: impl Into<ShortcutFilter>) -> Self {
836 self.with_meta(|m| m.init_var(*COMMAND_SHORTCUT_FILTER_ID, filter.into()));
837 self
838 }
839}
840
841bitflags! {
842 #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
844 #[serde(transparent)]
845 pub struct ShortcutFilter: u8 {
846 const ENABLED = 0b001;
848 const FOCUSED = 0b010;
850 const CMD_ENABLED = 0b100;
852 }
853}
854
855static_id! {
856 static ref COMMAND_SHORTCUT_ID: CommandMetaVarId<Shortcuts>;
857 static ref COMMAND_SHORTCUT_FILTER_ID: CommandMetaVarId<ShortcutFilter>;
858}
859
860#[doc(hidden)]
861#[macro_export]
862macro_rules! __shortcut {
863 (-> + $Key:tt) => {
864 $crate::shortcut::KeyGesture {
865 key: $crate::__shortcut!(@key $Key),
866 modifiers: $crate::shortcut::ModifiersState::empty(),
867 }
868 };
869
870 (-> $($MODIFIER:ident)|+ + $Key:tt) => {
871 $crate::shortcut::KeyGesture {
872 key: $crate::__shortcut!(@key $Key),
873 modifiers: $($crate::shortcut::ModifiersState::$MODIFIER)|+,
874 }
875 };
876
877 (=> $($STARTER_MODIFIER:ident)|* + $StarterKey:tt, $($COMPLEMENT_MODIFIER:ident)|* + $ComplementKey:tt) => {
878 $crate::shortcut::KeyChord {
879 starter: $crate::__shortcut!(-> $($STARTER_MODIFIER)|* + $StarterKey),
880 complement: $crate::__shortcut!(-> $($COMPLEMENT_MODIFIER)|* + $ComplementKey)
881 }
882 };
883
884 (@key $Key:ident) => { $crate::shortcut::GestureKey::Key($crate::shortcut::Key::$Key) };
885 (@key $key_char:literal) => { $crate::shortcut::GestureKey::Key($crate::shortcut::Key::Char($key_char)) };
886}
887
888#[macro_export]
937macro_rules! shortcut_macro {
938 (Super) => {
939 $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Super)
940 };
941 (Shift) => {
942 $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Shift)
943 };
944 (Ctrl) => {
945 $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Ctrl)
946 };
947 (Alt) => {
948 $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Alt)
949 };
950
951 ($Key:tt) => {
952 $crate::shortcut::Shortcut::Gesture($crate::__shortcut!(-> + $Key))
953 };
954 ($($MODIFIER:ident)|+ + $Key:tt) => {
955 $crate::shortcut::Shortcut::Gesture($crate::__shortcut!(-> $($MODIFIER)|+ + $Key))
956 };
957
958 ($StarterKey:tt, $ComplementKey:tt) => {
959 $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
960 + $StarterKey,
961 + $ComplementKey
962 ))
963 };
964
965 ($StarterKey:tt, $($COMPLEMENT_MODIFIER:ident)|+ + $ComplementKey:tt) => {
966 $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
967 + $StarterKey,
968 $(COMPLEMENT_MODIFIER)|* + $ComplementKey
969 ))
970 };
971
972 ($($STARTER_MODIFIER:ident)|+ + $StarterKey:tt, $ComplementKey:tt) => {
973 $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
974 $($STARTER_MODIFIER)|* + $StarterKey,
975 + $ComplementKey
976 ))
977 };
978
979 ($($STARTER_MODIFIER:ident)|+ + $StarterKey:tt, $($COMPLEMENT_MODIFIER:ident)|+ + $ComplementKey:tt) => {
980 $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
981 $($STARTER_MODIFIER)|* + $StarterKey,
982 $($COMPLEMENT_MODIFIER)|* + $ComplementKey
983 ))
984 };
985}
986#[doc(inline)]
987pub use crate::shortcut_macro as shortcut;