keybinding.rs

  1use std::collections::HashSet;
  2use std::marker::PhantomData;
  3
  4use strum::{EnumIter, IntoEnumIterator};
  5
  6use crate::prelude::*;
  7use crate::theme;
  8
  9#[derive(Element, Clone)]
 10pub struct Keybinding<S: 'static + Send + Sync + Clone> {
 11    state_type: PhantomData<S>,
 12
 13    /// A keybinding consists of a key and a set of modifier keys.
 14    /// More then one keybinding produces a chord.
 15    ///
 16    /// This should always contain at least one element.
 17    keybinding: Vec<(String, ModifierKeys)>,
 18}
 19
 20impl<S: 'static + Send + Sync + Clone> Keybinding<S> {
 21    pub fn new(key: String, modifiers: ModifierKeys) -> Self {
 22        Self {
 23            state_type: PhantomData,
 24            keybinding: vec![(key, modifiers)],
 25        }
 26    }
 27
 28    pub fn new_chord(
 29        first_note: (String, ModifierKeys),
 30        second_note: (String, ModifierKeys),
 31    ) -> Self {
 32        Self {
 33            state_type: PhantomData,
 34            keybinding: vec![first_note, second_note],
 35        }
 36    }
 37
 38    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 39        div()
 40            .flex()
 41            .gap_2()
 42            .children(self.keybinding.iter().map(|(key, modifiers)| {
 43                div()
 44                    .flex()
 45                    .gap_1()
 46                    .children(ModifierKey::iter().filter_map(|modifier| {
 47                        if modifiers.0.contains(&modifier) {
 48                            Some(Key::new(modifier.glyph()))
 49                        } else {
 50                            None
 51                        }
 52                    }))
 53                    .child(Key::new(key.clone()))
 54            }))
 55    }
 56}
 57
 58#[derive(Element)]
 59pub struct Key<S: 'static + Send + Sync> {
 60    state_type: PhantomData<S>,
 61    key: String,
 62}
 63
 64impl<S: 'static + Send + Sync> Key<S> {
 65    pub fn new<K>(key: K) -> Self
 66    where
 67        K: Into<String>,
 68    {
 69        Self {
 70            state_type: PhantomData,
 71            key: key.into(),
 72        }
 73    }
 74
 75    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 76        let theme = theme(cx);
 77
 78        div()
 79            .px_2()
 80            .py_0()
 81            .rounded_md()
 82            .text_sm()
 83            .text_color(theme.lowest.on.default.foreground)
 84            .fill(theme.lowest.on.default.background)
 85            .child(self.key.clone())
 86    }
 87}
 88
 89// NOTE: The order the modifier keys appear in this enum impacts the order in
 90// which they are rendered in the UI.
 91#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
 92pub enum ModifierKey {
 93    Control,
 94    Alt,
 95    Command,
 96    Shift,
 97}
 98
 99impl ModifierKey {
100    /// Returns the glyph for the [`ModifierKey`].
101    pub fn glyph(&self) -> char {
102        match self {
103            Self::Control => '^',
104            Self::Alt => '⌥',
105            Self::Command => '⌘',
106            Self::Shift => '⇧',
107        }
108    }
109}
110
111#[derive(Clone)]
112pub struct ModifierKeys(HashSet<ModifierKey>);
113
114impl ModifierKeys {
115    pub fn new() -> Self {
116        Self(HashSet::new())
117    }
118
119    pub fn all() -> Self {
120        Self(HashSet::from_iter(ModifierKey::iter()))
121    }
122
123    pub fn add(mut self, modifier: ModifierKey) -> Self {
124        self.0.insert(modifier);
125        self
126    }
127
128    pub fn control(mut self, control: bool) -> Self {
129        if control {
130            self.0.insert(ModifierKey::Control);
131        } else {
132            self.0.remove(&ModifierKey::Control);
133        }
134
135        self
136    }
137
138    pub fn alt(mut self, alt: bool) -> Self {
139        if alt {
140            self.0.insert(ModifierKey::Alt);
141        } else {
142            self.0.remove(&ModifierKey::Alt);
143        }
144
145        self
146    }
147
148    pub fn command(mut self, command: bool) -> Self {
149        if command {
150            self.0.insert(ModifierKey::Command);
151        } else {
152            self.0.remove(&ModifierKey::Command);
153        }
154
155        self
156    }
157
158    pub fn shift(mut self, shift: bool) -> Self {
159        if shift {
160            self.0.insert(ModifierKey::Shift);
161        } else {
162            self.0.remove(&ModifierKey::Shift);
163        }
164
165        self
166    }
167}
168
169#[cfg(feature = "stories")]
170pub use stories::*;
171
172#[cfg(feature = "stories")]
173mod stories {
174    use itertools::Itertools;
175
176    use crate::Story;
177
178    use super::*;
179
180    #[derive(Element)]
181    pub struct KeybindingStory<S: 'static + Send + Sync + Clone> {
182        state_type: PhantomData<S>,
183    }
184
185    impl<S: 'static + Send + Sync + Clone> KeybindingStory<S> {
186        pub fn new() -> Self {
187            Self {
188                state_type: PhantomData,
189            }
190        }
191
192        fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
193            let all_modifier_permutations = ModifierKey::iter().permutations(2);
194
195            Story::container(cx)
196                .child(Story::title_for::<_, Keybinding<S>>(cx))
197                .child(Story::label(cx, "Single Key"))
198                .child(Keybinding::new("Z".to_string(), ModifierKeys::new()))
199                .child(Story::label(cx, "Single Key with Modifier"))
200                .child(
201                    div()
202                        .flex()
203                        .gap_3()
204                        .children(ModifierKey::iter().map(|modifier| {
205                            Keybinding::new("C".to_string(), ModifierKeys::new().add(modifier))
206                        })),
207                )
208                .child(Story::label(cx, "Single Key with Modifier (Permuted)"))
209                .child(
210                    div().flex().flex_col().children(
211                        all_modifier_permutations
212                            .chunks(4)
213                            .into_iter()
214                            .map(|chunk| {
215                                div()
216                                    .flex()
217                                    .gap_4()
218                                    .py_3()
219                                    .children(chunk.map(|permutation| {
220                                        let mut modifiers = ModifierKeys::new();
221
222                                        for modifier in permutation {
223                                            modifiers = modifiers.add(modifier);
224                                        }
225
226                                        Keybinding::new("X".to_string(), modifiers)
227                                    }))
228                            }),
229                    ),
230                )
231                .child(Story::label(cx, "Single Key with All Modifiers"))
232                .child(Keybinding::new("Z".to_string(), ModifierKeys::all()))
233                .child(Story::label(cx, "Chord"))
234                .child(Keybinding::new_chord(
235                    ("A".to_string(), ModifierKeys::new()),
236                    ("Z".to_string(), ModifierKeys::new()),
237                ))
238                .child(Story::label(cx, "Chord with Modifier"))
239                .child(Keybinding::new_chord(
240                    ("A".to_string(), ModifierKeys::new().control(true)),
241                    ("Z".to_string(), ModifierKeys::new().shift(true)),
242                ))
243        }
244    }
245}