zng_app/
shortcut.rs

1//! Key combination types.
2//!
3//! This is declared on this crate mostly to support shortcuts in commands, shortcut events
4//! are implemented in the input crate.
5
6use 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/// A keyboard key used in a gesture.
19///
20/// Gesture keys are case-insensitive, [`Key::Char`] is matched as case-insensitive.
21///
22/// Note that not all keys work well as gesture keys, you can use `try_into` to filter [`Key`] or [`KeyCode`] values
23/// that do not work.
24///
25/// [`Key::Char`]: zng_view_api::keyboard::Key::Char
26/// [`Key`]: zng_view_api::keyboard::Key
27/// [`KeyCode`]: zng_view_api::keyboard::KeyCode
28#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
29pub enum GestureKey {
30    /// Gesture key identified by the semantic key.
31    Key(Key),
32    /// Gesture key identified by the physical key.
33    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}
91/// Accepts only keys that are not [`is_modifier`] and not [`is_composition`].
92///
93/// [`is_modifier`]: Key::is_modifier
94/// [`is_composition`]: Key::is_composition
95impl 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}
106/// Accepts only keys that are not [`is_modifier`] and not [`is_composition`].
107///
108/// [`is_modifier`]: KeyCode::is_modifier
109/// [`is_composition`]: KeyCode::is_composition
110impl 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/// A keyboard combination.
148#[derive(Clone, serde::Serialize, serde::Deserialize)]
149pub struct KeyGesture {
150    /// The key modifiers.
151    ///
152    /// Equality of key gestures matches the [`ambit`] modifiers, so a `L_CTRL` is equal to a `R_CTRL` in a key gesture,
153    /// the actual bit flag is preserved in the state and can be extracted from the shortcut.
154    ///
155    /// [`ambit`]: ModifiersState::ambit
156    pub modifiers: ModifiersState,
157    /// The key.
158    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    /// New from modifiers and key.
174    pub fn new(modifiers: ModifiersState, key: GestureKey) -> Self {
175        KeyGesture { modifiers, key }
176    }
177
178    /// New key gesture without modifiers.
179    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/// A modifier key press and release without any other key press in between.
218#[derive(Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
219pub enum ModifierGesture {
220    /// Any of the Windows/Apple keys.
221    Super,
222    /// Any of the CTRL keys.
223    Ctrl,
224    /// Any of the SHIFT keys.
225    Shift,
226    /// Any of the ALT keys.
227    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    /// Left modifier key.
273    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    /// To modifiers state.
282    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/// A sequence of two keyboard combinations.
293#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
294pub struct KeyChord {
295    /// The first key gesture.
296    pub starter: KeyGesture,
297
298    /// The second key gesture.
299    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/// Keyboard gesture or chord associated with a command.
320///
321/// See the [`shortcut!`] macro for declaring a shortcut.
322#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
323pub enum Shortcut {
324    /// Key-press plus modifiers.
325    Gesture(KeyGesture),
326    /// Sequence of two key gestures.
327    Chord(KeyChord),
328    /// Modifier press and release.
329    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    /// Modifiers state required by shortcut.
355    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/// Multiple shortcuts.
418#[derive(Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
419pub struct Shortcuts(pub Vec<Shortcut>);
420impl Shortcuts {
421    /// New default (empty).
422    pub const fn new() -> Self {
423        Self(vec![])
424    }
425
426    /// Try to generate shortcuts that produce the `character`.
427    ///
428    /// Returns at least one shortcut or error the char back if it cannot
429    /// be generated by a single shortcut.
430    ///
431    /// Note chords are not generated. Caps lock is assumed to be off.
432    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    /// If the `shortcut` is present in the shortcuts.
444    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    /// See [`from_char`](Self::from_char).
452    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/// Shortcut, gesture parsing error.
505#[derive(Debug, Clone, PartialEq, Eq)]
506pub struct ParseError {
507    /// Error message, usually in the pattern "`{invalid-input}` is not a {shortcut/modifier}".
508    pub error: String,
509}
510impl ParseError {
511    /// New from any error message.
512    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    /// Represents the current state of the keyboard modifiers.
525    ///
526    /// Each flag represents a modifier and is set if this modifier is active.
527    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
528    #[serde(transparent)]
529    pub struct ModifiersState: u8 {
530        /// The left "shift" key.
531        const L_SHIFT = 0b0000_0001;
532        /// The right "shift" key.
533        const R_SHIFT = 0b0000_0010;
534        /// Any "shift" key.
535        const SHIFT   = 0b0000_0011;
536
537        /// The left "control" key.
538        const L_CTRL = 0b0000_0100;
539        /// The right "control" key.
540        const R_CTRL = 0b0000_1000;
541        /// Any "control" key.
542        const CTRL   = 0b0000_1100;
543
544        /// The left "alt" key.
545        const L_ALT = 0b0001_0000;
546        /// The right "alt" key.
547        const R_ALT = 0b0010_0000;
548        /// Any "alt" key.
549        const ALT   = 0b0011_0000;
550
551        /// The left "logo" key.
552        const L_LOGO = 0b0100_0000;
553        /// The right "logo" key.
554        const R_LOGO = 0b1000_0000;
555        /// Any "logo" key.
556        ///
557        /// This is the "windows" key on PC and "command" key on Mac.
558        const LOGO   = 0b1100_0000;
559    }
560}
561impl ModifiersState {
562    /// Returns `true` if any shift key is pressed.
563    pub fn has_shift(self) -> bool {
564        self.intersects(Self::SHIFT)
565    }
566    /// Returns `true` if any control key is pressed.
567    pub fn has_ctrl(self) -> bool {
568        self.intersects(Self::CTRL)
569    }
570    /// Returns `true` if any alt key is pressed.
571    pub fn has_alt(self) -> bool {
572        self.intersects(Self::ALT)
573    }
574    /// Returns `true` if any logo key is pressed.
575    pub fn has_super(self) -> bool {
576        self.intersects(Self::LOGO)
577    }
578
579    /// Returns `true` if only any flag in `part` is pressed.
580    pub fn is_only(self, part: ModifiersState) -> bool {
581        !self.is_empty() && (self - part).is_empty()
582    }
583
584    /// Returns `true` if only any shift key is pressed.
585    pub fn is_only_shift(self) -> bool {
586        self.is_only(ModifiersState::SHIFT)
587    }
588    /// Returns `true` if only any control key is pressed.
589    pub fn is_only_ctrl(self) -> bool {
590        self.is_only(ModifiersState::CTRL)
591    }
592    /// Returns `true` if only any alt key is pressed.
593    pub fn is_only_alt(self) -> bool {
594        self.is_only(ModifiersState::ALT)
595    }
596    /// Returns `true` if only any logo key is pressed.
597    pub fn is_only_logo(self) -> bool {
598        self.is_only(ModifiersState::LOGO)
599    }
600
601    /// Removes `part` and returns if it was removed.
602    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    /// Removes `SHIFT` and returns if it was removed.
611    pub fn take_shift(&mut self) -> bool {
612        self.take(ModifiersState::SHIFT)
613    }
614
615    /// Removes `CTRL` and returns if it was removed.
616    pub fn take_ctrl(&mut self) -> bool {
617        self.take(ModifiersState::CTRL)
618    }
619
620    /// Removes `ALT` and returns if it was removed.
621    pub fn take_alt(&mut self) -> bool {
622        self.take(ModifiersState::ALT)
623    }
624
625    /// Removes `LOGO` and returns if it was removed.
626    pub fn take_logo(&mut self) -> bool {
627        self.take(ModifiersState::LOGO)
628    }
629
630    /// Returns modifiers that set both left and right flags if any side is set in `self`.
631    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    /// Returns only the alt flags in `self`.
649    pub fn into_alt(self) -> Self {
650        self & Self::ALT
651    }
652
653    /// Returns only the control flags in `self`.
654    pub fn into_ctrl(self) -> Self {
655        self & Self::CTRL
656    }
657
658    /// Returns only the shift flags in `self`.
659    pub fn into_shift(self) -> Self {
660        self & Self::SHIFT
661    }
662
663    /// Returns only the logo flags in `self`.
664    pub fn into_logo(self) -> Self {
665        self & Self::LOGO
666    }
667
668    /// Modifier from `code`, returns empty if the key is not a modifier.
669    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    /// Modifier from `key`, returns empty if the key is not a modifier.
684    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    /// All key codes that when pressed form the modifiers state.
696    ///
697    /// In case of multiple keys the order is `LOGO`, `CTRL`, `SHIFT`, `ALT`.
698    ///
699    /// In case both left and right keys are flagged for a modifier, the left key is used.
700    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    /// All keys that when pressed form the modifiers state.
731    ///
732    /// In case of multiple keys the order is `LOGO`, `CTRL`, `SHIFT`, `ALT`.
733    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
758/// Adds the [`shortcut`] metadata.
759///
760/// If a command has a shortcut the `GestureManager` will invoke the command when the shortcut is pressed
761/// the command is enabled, if the command target is a widget it will also be focused. See the `GESTURES`
762/// service documentation for details on how shortcuts are resolved.
763///
764/// [`shortcut`]: CommandShortcutExt::shortcut
765pub trait CommandShortcutExt {
766    /// Gets a read-write variable that is zero-or-more shortcuts that invoke the command.
767    fn shortcut(self) -> CommandMetaVar<Shortcuts>;
768
769    /// Gets a read-only variable that is the display text for the first shortcut.
770    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    /// Gets a read-write variable that sets a filter for when the [`shortcut`] is valid.
780    ///
781    /// [`shortcut`]: CommandShortcutExt::shortcut
782    fn shortcut_filter(self) -> CommandMetaVar<ShortcutFilter>;
783
784    /// Sets the initial shortcuts.
785    fn init_shortcut(self, shortcut: impl Into<Shortcuts>) -> Self;
786
787    /// Sets the initial shortcut filters.
788    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    /// Conditions that must be met for the shortcut to apply.
812    #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
813    #[serde(transparent)]
814    pub struct ShortcutFilter: u8 {
815        /// Shortcut only applies if the scope is enabled.
816        const ENABLED = 0b001;
817        /// Shortcut only applies if the scope is in the focused path.
818        const FOCUSED = 0b010;
819        /// Shortcut only applies if the command is enabled.
820        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///<span data-del-macro-root></span> Creates a [`Shortcut`].
858///
859/// This macro input can be:
860///
861/// * A single [`ModifierGesture`] variant defines a [`Shortcut::Modifier`].
862/// * A single [`Key`] variant defines a [`Shortcut::Gesture`] without modifiers.
863/// * A single [`char`] literal that translates to a [`Key::Char`].
864/// * [`ModifiersState`] followed by `+` followed by a `Key` or `char` defines a gesture with modifiers. Modifier
865///   combinations must be joined by `|`.
866/// * A gesture followed by `,` followed by another gesture defines a [`Shortcut::Chord`].
867///
868/// Note that not all shortcuts can be declared with this macro, in particular there is no support for [`Key::Str`]
869/// and [`KeyCode`], these shortcuts must be declared manually. Also note that some keys are not recommended in shortcuts,
870/// in particular [`Key::is_modifier`] and [`Key::is_composition`] keys will not work right.
871///
872///
873/// # Examples
874///
875/// ```
876/// use zng_app::shortcut::{Shortcut, shortcut};
877///
878/// fn single_key() -> Shortcut {
879///     shortcut!(Enter)
880/// }
881///
882/// fn modified_key() -> Shortcut {
883///     shortcut!(CTRL+'C')
884/// }
885///
886/// fn multi_modified_key() -> Shortcut {
887///     shortcut!(CTRL|SHIFT+'C')
888/// }
889///
890/// fn chord() -> Shortcut {
891///     shortcut!(CTRL+'E', 'A')
892/// }
893///
894/// fn modifier_release() -> Shortcut {
895///     shortcut!(Alt)
896/// }
897/// ```
898///
899/// [`Key`]: zng_view_api::keyboard::Key
900/// [`Key::Char`]: zng_view_api::keyboard::Key::Char
901/// [`Key::Str`]: zng_view_api::keyboard::Key::Str
902/// [`KeyCode`]: zng_view_api::keyboard::KeyCode
903/// [`Key::is_modifier`]: zng_view_api::keyboard::Key::is_modifier
904/// [`Key::is_composition`]: zng_view_api::keyboard::Key::is_composition
905#[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;