1use std::fmt;
7
8use bitflags::bitflags;
9use zng_txt::{ToTxt, Txt};
10use zng_unique_id::static_id;
11use zng_var::{BoxedVar, 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),
84 Key::Str(s) => write!(f, "{}", s),
85 k => write!(f, "{k:?}"),
86 },
87 GestureKey::Code(c) => write!(f, "{:?}", c),
88 }
89 }
90}
91impl TryFrom<Key> for GestureKey {
96 type Error = Key;
97
98 fn try_from(key: Key) -> Result<Self, Self::Error> {
99 if key.is_modifier() || key.is_composition() || key == Key::Unidentified {
100 Err(key)
101 } else {
102 Ok(Self::Key(key))
103 }
104 }
105}
106impl TryFrom<KeyCode> for GestureKey {
111 type Error = KeyCode;
112
113 fn try_from(key: KeyCode) -> Result<Self, Self::Error> {
114 if key.is_modifier() || key.is_composition() || key.is_unidentified() {
115 Err(key)
116 } else {
117 Ok(Self::Code(key))
118 }
119 }
120}
121impl std::str::FromStr for GestureKey {
122 type Err = ParseError;
123
124 fn from_str(s: &str) -> Result<Self, Self::Err> {
125 match Key::from_str(s) {
126 Key::Str(s) => match KeyCode::from_str(&s) {
127 Ok(k) => {
128 let key = k
129 .try_into()
130 .map_err(|e| ParseError::new(format!("key `{e:?}` cannot be used in gestures")))?;
131
132 Ok(key)
133 }
134 Err(_) => Ok(Self::Key(Key::Str(s))),
135 },
136 k => {
137 let key = k
138 .try_into()
139 .map_err(|e| ParseError::new(format!("key `{e:?}` cannot be used in gestures")))?;
140
141 Ok(key)
142 }
143 }
144 }
145}
146
147#[derive(Clone, serde::Serialize, serde::Deserialize)]
149pub struct KeyGesture {
150 pub modifiers: ModifiersState,
157 pub key: GestureKey,
159}
160impl PartialEq for KeyGesture {
161 fn eq(&self, other: &Self) -> bool {
162 self.modifiers.ambit() == other.modifiers.ambit() && self.key == other.key
163 }
164}
165impl Eq for KeyGesture {}
166impl std::hash::Hash for KeyGesture {
167 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
168 self.modifiers.ambit().hash(state);
169 self.key.hash(state);
170 }
171}
172impl KeyGesture {
173 pub fn new(modifiers: ModifiersState, key: GestureKey) -> Self {
175 KeyGesture { modifiers, key }
176 }
177
178 pub fn new_key(key: GestureKey) -> Self {
180 KeyGesture {
181 modifiers: ModifiersState::empty(),
182 key,
183 }
184 }
185}
186impl fmt::Debug for KeyGesture {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 if f.alternate() {
189 f.debug_struct("KeyGesture")
190 .field("modifiers", &self.modifiers)
191 .field("key", &self.key)
192 .finish()
193 } else {
194 write!(f, "{self}")
195 }
196 }
197}
198impl fmt::Display for KeyGesture {
199 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200 if self.modifiers.has_super() {
201 write!(f, "Super+")?
202 }
203 if self.modifiers.has_ctrl() {
204 write!(f, "Ctrl+")?
205 }
206 if self.modifiers.has_shift() {
207 write!(f, "Shift+")?
208 }
209 if self.modifiers.has_alt() {
210 write!(f, "Alt+")?
211 }
212
213 write!(f, "{}", self.key)
214 }
215}
216
217#[derive(Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
219pub enum ModifierGesture {
220 Super,
222 Ctrl,
224 Shift,
226 Alt,
228}
229impl fmt::Debug for ModifierGesture {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 if f.alternate() {
232 write!(f, "ModifierGesture::")?;
233 }
234 write!(f, "{self}")
235 }
236}
237impl fmt::Display for ModifierGesture {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 match self {
240 ModifierGesture::Super => write!(f, "Super"),
241 ModifierGesture::Ctrl => write!(f, "Ctrl"),
242 ModifierGesture::Shift => write!(f, "Shift"),
243 ModifierGesture::Alt => write!(f, "Alt"),
244 }
245 }
246}
247impl<'a> TryFrom<&'a Key> for ModifierGesture {
248 type Error = &'a Key;
249 fn try_from(value: &'a Key) -> Result<Self, Self::Error> {
250 match value {
251 Key::Alt | Key::AltGraph => Ok(ModifierGesture::Alt),
252 Key::Ctrl => Ok(ModifierGesture::Ctrl),
253 Key::Shift => Ok(ModifierGesture::Shift),
254 Key::Super => Ok(ModifierGesture::Super),
255 key => Err(key),
256 }
257 }
258}
259impl TryFrom<KeyCode> for ModifierGesture {
260 type Error = KeyCode;
261 fn try_from(value: KeyCode) -> Result<Self, Self::Error> {
262 match value {
263 KeyCode::AltLeft | KeyCode::AltRight => Ok(ModifierGesture::Alt),
264 KeyCode::CtrlLeft | KeyCode::CtrlRight => Ok(ModifierGesture::Ctrl),
265 KeyCode::ShiftLeft | KeyCode::ShiftRight => Ok(ModifierGesture::Shift),
266 KeyCode::SuperLeft | KeyCode::SuperRight => Ok(ModifierGesture::Super),
267 key => Err(key),
268 }
269 }
270}
271impl ModifierGesture {
272 pub fn left_key(&self) -> (KeyCode, Key) {
274 match self {
275 ModifierGesture::Super => (KeyCode::SuperLeft, Key::Super),
276 ModifierGesture::Ctrl => (KeyCode::CtrlLeft, Key::Ctrl),
277 ModifierGesture::Shift => (KeyCode::ShiftLeft, Key::Shift),
278 ModifierGesture::Alt => (KeyCode::AltLeft, Key::Alt),
279 }
280 }
281 pub fn modifiers_state(&self) -> ModifiersState {
283 match self {
284 ModifierGesture::Super => ModifiersState::LOGO,
285 ModifierGesture::Ctrl => ModifiersState::CTRL,
286 ModifierGesture::Shift => ModifiersState::SHIFT,
287 ModifierGesture::Alt => ModifiersState::ALT,
288 }
289 }
290}
291
292#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
294pub struct KeyChord {
295 pub starter: KeyGesture,
297
298 pub complement: KeyGesture,
300}
301impl fmt::Debug for KeyChord {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 if f.alternate() {
304 f.debug_struct("KeyChord")
305 .field("starter", &self.starter)
306 .field("complement", &self.complement)
307 .finish()
308 } else {
309 write!(f, "{self}")
310 }
311 }
312}
313impl fmt::Display for KeyChord {
314 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
315 write!(f, "{} {}", self.starter, self.complement)
316 }
317}
318
319#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
323pub enum Shortcut {
324 Gesture(KeyGesture),
326 Chord(KeyChord),
328 Modifier(ModifierGesture),
330}
331impl fmt::Debug for Shortcut {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 if f.alternate() {
334 match self {
335 Shortcut::Gesture(g) => f.debug_tuple("Shortcut::Gesture").field(g).finish(),
336 Shortcut::Chord(c) => f.debug_tuple("Shortcut::Chord").field(c).finish(),
337 Shortcut::Modifier(m) => f.debug_tuple("Shortcut::Modifier").field(m).finish(),
338 }
339 } else {
340 write!(f, "{self}")
341 }
342 }
343}
344impl fmt::Display for Shortcut {
345 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346 match self {
347 Shortcut::Gesture(g) => fmt::Display::fmt(g, f),
348 Shortcut::Chord(c) => fmt::Display::fmt(c, f),
349 Shortcut::Modifier(m) => fmt::Display::fmt(m, f),
350 }
351 }
352}
353impl Shortcut {
354 pub fn modifiers_state(&self) -> ModifiersState {
356 match self {
357 Shortcut::Gesture(g) => g.modifiers,
358 Shortcut::Chord(c) => c.complement.modifiers,
359 Shortcut::Modifier(m) => m.modifiers_state(),
360 }
361 }
362}
363impl_from_and_into_var! {
364 fn from(shortcut: Shortcut) -> Shortcuts {
365 Shortcuts(vec![shortcut])
366 }
367
368 fn from(key_gesture: KeyGesture) -> Shortcut {
369 Shortcut::Gesture(key_gesture)
370 }
371
372 fn from(key_chord: KeyChord) -> Shortcut {
373 Shortcut::Chord(key_chord)
374 }
375
376 fn from(modifier: ModifierGesture) -> Shortcut {
377 Shortcut::Modifier(modifier)
378 }
379
380 fn from(gesture_key: GestureKey) -> Shortcut {
381 KeyGesture::new_key(gesture_key).into()
382 }
383
384 fn from(gesture_key: GestureKey) -> Shortcuts {
385 Shortcuts(vec![gesture_key.into()])
386 }
387
388 fn from(key_gesture: KeyGesture) -> Shortcuts {
389 Shortcuts(vec![key_gesture.into()])
390 }
391
392 fn from(key_chord: KeyChord) -> Shortcuts {
393 Shortcuts(vec![key_chord.into()])
394 }
395
396 fn from(modifier: ModifierGesture) -> Shortcuts {
397 Shortcuts(vec![modifier.into()])
398 }
399
400 fn from(shortcuts: Vec<Shortcut>) -> Shortcuts {
401 Shortcuts(shortcuts)
402 }
403}
404impl<const N: usize> From<[Shortcut; N]> for Shortcuts {
405 fn from(a: [Shortcut; N]) -> Self {
406 Shortcuts(a.into())
407 }
408}
409impl<const N: usize> crate::var::IntoVar<Shortcuts> for [Shortcut; N] {
410 type Var = crate::var::LocalVar<Shortcuts>;
411
412 fn into_var(self) -> Self::Var {
413 crate::var::LocalVar(self.into())
414 }
415}
416
417#[derive(Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
419pub struct Shortcuts(pub Vec<Shortcut>);
420impl Shortcuts {
421 pub const fn new() -> Self {
423 Self(vec![])
424 }
425
426 pub fn from_char(character: char) -> Result<Self, char> {
433 if character.is_control() {
434 Err(character)
435 } else {
436 Ok(Self(vec![Shortcut::Gesture(KeyGesture {
437 modifiers: ModifiersState::empty(),
438 key: GestureKey::Key(Key::Char(character)),
439 })]))
440 }
441 }
442
443 pub fn contains(&self, shortcut: &Shortcut) -> bool {
445 self.0.contains(shortcut)
446 }
447}
448impl TryFrom<char> for Shortcuts {
449 type Error = char;
450
451 fn try_from(value: char) -> Result<Self, Self::Error> {
453 Shortcuts::from_char(value)
454 }
455}
456impl fmt::Debug for Shortcuts {
457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458 if f.alternate() {
459 f.debug_tuple("Shortcuts").field(&self.0).finish()
460 } else {
461 write!(f, "[")?;
462 if !self.0.is_empty() {
463 if let Shortcut::Chord(c) = &self.0[0] {
464 write!(f, "({c:?})")?;
465 } else {
466 write!(f, "{:?}", self.0[0])?;
467 }
468 for shortcut in &self.0[1..] {
469 if let Shortcut::Chord(c) = shortcut {
470 write!(f, ", ({c:?})")?;
471 } else {
472 write!(f, ", {shortcut:?}")?;
473 }
474 }
475 }
476 write!(f, "]")
477 }
478 }
479}
480impl fmt::Display for Shortcuts {
481 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
482 if !self.0.is_empty() {
483 write!(f, "{}", self.0[0])?;
484 for shortcut in &self.0[1..] {
485 write!(f, " | {shortcut}")?;
486 }
487 }
488 Ok(())
489 }
490}
491impl std::ops::Deref for Shortcuts {
492 type Target = Vec<Shortcut>;
493
494 fn deref(&self) -> &Self::Target {
495 &self.0
496 }
497}
498impl std::ops::DerefMut for Shortcuts {
499 fn deref_mut(&mut self) -> &mut Self::Target {
500 &mut self.0
501 }
502}
503
504#[derive(Debug, Clone, PartialEq, Eq)]
506pub struct ParseError {
507 pub error: String,
509}
510impl ParseError {
511 pub fn new(error: impl ToString) -> Self {
513 ParseError { error: error.to_string() }
514 }
515}
516impl fmt::Display for ParseError {
517 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
518 self.error.fmt(f)
519 }
520}
521impl std::error::Error for ParseError {}
522
523bitflags! {
524 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
528 #[serde(transparent)]
529 pub struct ModifiersState: u8 {
530 const L_SHIFT = 0b0000_0001;
532 const R_SHIFT = 0b0000_0010;
534 const SHIFT = 0b0000_0011;
536
537 const L_CTRL = 0b0000_0100;
539 const R_CTRL = 0b0000_1000;
541 const CTRL = 0b0000_1100;
543
544 const L_ALT = 0b0001_0000;
546 const R_ALT = 0b0010_0000;
548 const ALT = 0b0011_0000;
550
551 const L_LOGO = 0b0100_0000;
553 const R_LOGO = 0b1000_0000;
555 const LOGO = 0b1100_0000;
559 }
560}
561impl ModifiersState {
562 pub fn has_shift(self) -> bool {
564 self.intersects(Self::SHIFT)
565 }
566 pub fn has_ctrl(self) -> bool {
568 self.intersects(Self::CTRL)
569 }
570 pub fn has_alt(self) -> bool {
572 self.intersects(Self::ALT)
573 }
574 pub fn has_super(self) -> bool {
576 self.intersects(Self::LOGO)
577 }
578
579 pub fn is_only(self, part: ModifiersState) -> bool {
581 !self.is_empty() && (self - part).is_empty()
582 }
583
584 pub fn is_only_shift(self) -> bool {
586 self.is_only(ModifiersState::SHIFT)
587 }
588 pub fn is_only_ctrl(self) -> bool {
590 self.is_only(ModifiersState::CTRL)
591 }
592 pub fn is_only_alt(self) -> bool {
594 self.is_only(ModifiersState::ALT)
595 }
596 pub fn is_only_logo(self) -> bool {
598 self.is_only(ModifiersState::LOGO)
599 }
600
601 pub fn take(&mut self, part: ModifiersState) -> bool {
603 let r = self.intersects(part);
604 if r {
605 self.remove(part);
606 }
607 r
608 }
609
610 pub fn take_shift(&mut self) -> bool {
612 self.take(ModifiersState::SHIFT)
613 }
614
615 pub fn take_ctrl(&mut self) -> bool {
617 self.take(ModifiersState::CTRL)
618 }
619
620 pub fn take_alt(&mut self) -> bool {
622 self.take(ModifiersState::ALT)
623 }
624
625 pub fn take_logo(&mut self) -> bool {
627 self.take(ModifiersState::LOGO)
628 }
629
630 pub fn ambit(self) -> Self {
632 let mut r = Self::empty();
633 if self.has_alt() {
634 r |= Self::ALT;
635 }
636 if self.has_ctrl() {
637 r |= Self::CTRL;
638 }
639 if self.has_shift() {
640 r |= Self::SHIFT;
641 }
642 if self.has_super() {
643 r |= Self::LOGO;
644 }
645 r
646 }
647
648 pub fn into_alt(self) -> Self {
650 self & Self::ALT
651 }
652
653 pub fn into_ctrl(self) -> Self {
655 self & Self::CTRL
656 }
657
658 pub fn into_shift(self) -> Self {
660 self & Self::SHIFT
661 }
662
663 pub fn into_logo(self) -> Self {
665 self & Self::LOGO
666 }
667
668 pub fn from_code(code: KeyCode) -> ModifiersState {
670 match code {
671 KeyCode::AltLeft => Self::L_ALT,
672 KeyCode::AltRight => Self::R_ALT,
673 KeyCode::CtrlLeft => Self::L_CTRL,
674 KeyCode::CtrlRight => Self::R_CTRL,
675 KeyCode::ShiftLeft => Self::L_SHIFT,
676 KeyCode::ShiftRight => Self::R_SHIFT,
677 KeyCode::SuperLeft => Self::L_LOGO,
678 KeyCode::SuperRight => Self::R_LOGO,
679 _ => Self::empty(),
680 }
681 }
682
683 pub fn from_key(key: Key) -> ModifiersState {
685 match key {
686 Key::Alt => Self::L_ALT,
687 Key::AltGraph => Self::R_ALT,
688 Key::Shift => Self::SHIFT,
689 Key::Ctrl => Self::CTRL,
690 Key::Super => Self::LOGO,
691 _ => Self::empty(),
692 }
693 }
694
695 pub fn codes(self) -> Vec<KeyCode> {
701 let mut r = vec![];
702
703 if self.contains(Self::L_LOGO) {
704 r.push(KeyCode::SuperLeft);
705 } else if self.contains(Self::R_LOGO) {
706 r.push(KeyCode::SuperRight);
707 }
708
709 if self.contains(Self::L_CTRL) {
710 r.push(KeyCode::CtrlLeft);
711 } else if self.contains(Self::R_CTRL) {
712 r.push(KeyCode::CtrlRight);
713 }
714
715 if self.contains(Self::L_SHIFT) {
716 r.push(KeyCode::ShiftLeft);
717 } else if self.contains(Self::R_SHIFT) {
718 r.push(KeyCode::ShiftRight);
719 }
720
721 if self.contains(Self::L_ALT) {
722 r.push(KeyCode::AltLeft);
723 } else if self.contains(Self::R_ALT) {
724 r.push(KeyCode::AltRight);
725 }
726
727 r
728 }
729
730 pub fn keys(self) -> Vec<Key> {
734 let mut r = vec![];
735
736 if self.intersects(Self::LOGO) {
737 r.push(Key::Super);
738 }
739
740 if self.intersects(Self::CTRL) {
741 r.push(Key::Ctrl);
742 }
743
744 if self.intersects(Self::SHIFT) {
745 r.push(Key::Shift);
746 }
747
748 if self.contains(Self::R_ALT) {
749 r.push(Key::AltGraph);
750 } else if self.contains(Self::R_ALT) {
751 r.push(Key::Alt);
752 }
753
754 r
755 }
756}
757
758pub trait CommandShortcutExt {
766 fn shortcut(self) -> CommandMetaVar<Shortcuts>;
768
769 fn shortcut_txt(self) -> BoxedVar<Txt>
771 where
772 Self: Sized,
773 {
774 self.shortcut()
775 .map(|c| if c.is_empty() { Txt::from("") } else { c[0].to_txt() })
776 .boxed()
777 }
778
779 fn shortcut_filter(self) -> CommandMetaVar<ShortcutFilter>;
783
784 fn init_shortcut(self, shortcut: impl Into<Shortcuts>) -> Self;
786
787 fn init_shortcut_filter(self, filter: impl Into<ShortcutFilter>) -> Self;
789}
790impl CommandShortcutExt for Command {
791 fn shortcut(self) -> CommandMetaVar<Shortcuts> {
792 self.with_meta(|m| m.get_var_or_default(*COMMAND_SHORTCUT_ID))
793 }
794
795 fn shortcut_filter(self) -> CommandMetaVar<ShortcutFilter> {
796 self.with_meta(|m| m.get_var_or_default(*COMMAND_SHORTCUT_FILTER_ID))
797 }
798
799 fn init_shortcut(self, shortcut: impl Into<Shortcuts>) -> Self {
800 self.with_meta(|m| m.init_var(*COMMAND_SHORTCUT_ID, shortcut.into()));
801 self
802 }
803
804 fn init_shortcut_filter(self, filter: impl Into<ShortcutFilter>) -> Self {
805 self.with_meta(|m| m.init_var(*COMMAND_SHORTCUT_FILTER_ID, filter.into()));
806 self
807 }
808}
809
810bitflags! {
811 #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
813 #[serde(transparent)]
814 pub struct ShortcutFilter: u8 {
815 const ENABLED = 0b001;
817 const FOCUSED = 0b010;
819 const CMD_ENABLED = 0b100;
821 }
822}
823
824static_id! {
825 static ref COMMAND_SHORTCUT_ID: CommandMetaVarId<Shortcuts>;
826 static ref COMMAND_SHORTCUT_FILTER_ID: CommandMetaVarId<ShortcutFilter>;
827}
828
829#[doc(hidden)]
830#[macro_export]
831macro_rules! __shortcut {
832 (-> + $Key:tt) => {
833 $crate::shortcut::KeyGesture {
834 key: $crate::__shortcut!(@key $Key),
835 modifiers: $crate::shortcut::ModifiersState::empty(),
836 }
837 };
838
839 (-> $($MODIFIER:ident)|+ + $Key:tt) => {
840 $crate::shortcut::KeyGesture {
841 key: $crate::__shortcut!(@key $Key),
842 modifiers: $($crate::shortcut::ModifiersState::$MODIFIER)|+,
843 }
844 };
845
846 (=> $($STARTER_MODIFIER:ident)|* + $StarterKey:tt, $($COMPLEMENT_MODIFIER:ident)|* + $ComplementKey:tt) => {
847 $crate::shortcut::KeyChord {
848 starter: $crate::__shortcut!(-> $($STARTER_MODIFIER)|* + $StarterKey),
849 complement: $crate::__shortcut!(-> $($COMPLEMENT_MODIFIER)|* + $ComplementKey)
850 }
851 };
852
853 (@key $Key:ident) => { $crate::shortcut::GestureKey::Key($crate::shortcut::Key::$Key) };
854 (@key $key_char:literal) => { $crate::shortcut::GestureKey::Key($crate::shortcut::Key::Char($key_char)) };
855}
856
857#[macro_export]
906macro_rules! shortcut_macro {
907 (Super) => {
908 $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Super)
909 };
910 (Shift) => {
911 $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Shift)
912 };
913 (Ctrl) => {
914 $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Ctrl)
915 };
916 (Alt) => {
917 $crate::shortcut::Shortcut::Modifier($crate::shortcut::ModifierGesture::Alt)
918 };
919
920 ($Key:tt) => {
921 $crate::shortcut::Shortcut::Gesture($crate::__shortcut!(-> + $Key))
922 };
923 ($($MODIFIER:ident)|+ + $Key:tt) => {
924 $crate::shortcut::Shortcut::Gesture($crate::__shortcut!(-> $($MODIFIER)|+ + $Key))
925 };
926
927 ($StarterKey:tt, $ComplementKey:tt) => {
928 $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
929 + $StarterKey,
930 + $ComplementKey
931 ))
932 };
933
934 ($StarterKey:tt, $($COMPLEMENT_MODIFIER:ident)|+ + $ComplementKey:tt) => {
935 $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
936 + $StarterKey,
937 $(COMPLEMENT_MODIFIER)|* + $ComplementKey
938 ))
939 };
940
941 ($($STARTER_MODIFIER:ident)|+ + $StarterKey:tt, $ComplementKey:tt) => {
942 $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
943 $($STARTER_MODIFIER)|* + $StarterKey,
944 + $ComplementKey
945 ))
946 };
947
948 ($($STARTER_MODIFIER:ident)|+ + $StarterKey:tt, $($COMPLEMENT_MODIFIER:ident)|+ + $ComplementKey:tt) => {
949 $crate::shortcut::Shortcut::Chord($crate::__shortcut!(=>
950 $($STARTER_MODIFIER)|* + $StarterKey,
951 $($COMPLEMENT_MODIFIER)|* + $ComplementKey
952 ))
953 };
954}
955#[doc(inline)]
956pub use crate::shortcut_macro as shortcut;