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, _view: &mut S, 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().to_string()))
 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: SharedString,
 62}
 63
 64impl<S: 'static + Send + Sync> Key<S> {
 65    pub fn new(key: impl Into<SharedString>) -> Self {
 66        Self {
 67            state_type: PhantomData,
 68            key: key.into(),
 69        }
 70    }
 71
 72    fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 73        let theme = theme(cx);
 74
 75        div()
 76            .px_2()
 77            .py_0()
 78            .rounded_md()
 79            .text_sm()
 80            .text_color(theme.lowest.on.default.foreground)
 81            .bg(theme.lowest.on.default.background)
 82            .child(self.key.clone())
 83    }
 84}
 85
 86// NOTE: The order the modifier keys appear in this enum impacts the order in
 87// which they are rendered in the UI.
 88#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
 89pub enum ModifierKey {
 90    Control,
 91    Alt,
 92    Command,
 93    Shift,
 94}
 95
 96impl ModifierKey {
 97    /// Returns the glyph for the [`ModifierKey`].
 98    pub fn glyph(&self) -> char {
 99        match self {
100            Self::Control => '^',
101            Self::Alt => '⌥',
102            Self::Command => '⌘',
103            Self::Shift => '⇧',
104        }
105    }
106}
107
108#[derive(Clone)]
109pub struct ModifierKeys(HashSet<ModifierKey>);
110
111impl ModifierKeys {
112    pub fn new() -> Self {
113        Self(HashSet::new())
114    }
115
116    pub fn all() -> Self {
117        Self(HashSet::from_iter(ModifierKey::iter()))
118    }
119
120    pub fn add(mut self, modifier: ModifierKey) -> Self {
121        self.0.insert(modifier);
122        self
123    }
124
125    pub fn control(mut self, control: bool) -> Self {
126        if control {
127            self.0.insert(ModifierKey::Control);
128        } else {
129            self.0.remove(&ModifierKey::Control);
130        }
131
132        self
133    }
134
135    pub fn alt(mut self, alt: bool) -> Self {
136        if alt {
137            self.0.insert(ModifierKey::Alt);
138        } else {
139            self.0.remove(&ModifierKey::Alt);
140        }
141
142        self
143    }
144
145    pub fn command(mut self, command: bool) -> Self {
146        if command {
147            self.0.insert(ModifierKey::Command);
148        } else {
149            self.0.remove(&ModifierKey::Command);
150        }
151
152        self
153    }
154
155    pub fn shift(mut self, shift: bool) -> Self {
156        if shift {
157            self.0.insert(ModifierKey::Shift);
158        } else {
159            self.0.remove(&ModifierKey::Shift);
160        }
161
162        self
163    }
164}
165
166#[cfg(feature = "stories")]
167pub use stories::*;
168
169#[cfg(feature = "stories")]
170mod stories {
171    use itertools::Itertools;
172
173    use crate::Story;
174
175    use super::*;
176
177    #[derive(Element)]
178    pub struct KeybindingStory<S: 'static + Send + Sync + Clone> {
179        state_type: PhantomData<S>,
180    }
181
182    impl<S: 'static + Send + Sync + Clone> KeybindingStory<S> {
183        pub fn new() -> Self {
184            Self {
185                state_type: PhantomData,
186            }
187        }
188
189        fn render(
190            &mut self,
191            _view: &mut S,
192            cx: &mut ViewContext<S>,
193        ) -> impl Element<ViewState = S> {
194            let all_modifier_permutations = ModifierKey::iter().permutations(2);
195
196            Story::container(cx)
197                .child(Story::title_for::<_, Keybinding<S>>(cx))
198                .child(Story::label(cx, "Single Key"))
199                .child(Keybinding::new("Z".to_string(), ModifierKeys::new()))
200                .child(Story::label(cx, "Single Key with Modifier"))
201                .child(
202                    div()
203                        .flex()
204                        .gap_3()
205                        .children(ModifierKey::iter().map(|modifier| {
206                            Keybinding::new("C".to_string(), ModifierKeys::new().add(modifier))
207                        })),
208                )
209                .child(Story::label(cx, "Single Key with Modifier (Permuted)"))
210                .child(
211                    div().flex().flex_col().children(
212                        all_modifier_permutations
213                            .chunks(4)
214                            .into_iter()
215                            .map(|chunk| {
216                                div()
217                                    .flex()
218                                    .gap_4()
219                                    .py_3()
220                                    .children(chunk.map(|permutation| {
221                                        let mut modifiers = ModifierKeys::new();
222
223                                        for modifier in permutation {
224                                            modifiers = modifiers.add(modifier);
225                                        }
226
227                                        Keybinding::new("X".to_string(), modifiers)
228                                    }))
229                            }),
230                    ),
231                )
232                .child(Story::label(cx, "Single Key with All Modifiers"))
233                .child(Keybinding::new("Z".to_string(), ModifierKeys::all()))
234                .child(Story::label(cx, "Chord"))
235                .child(Keybinding::new_chord(
236                    ("A".to_string(), ModifierKeys::new()),
237                    ("Z".to_string(), ModifierKeys::new()),
238                ))
239                .child(Story::label(cx, "Chord with Modifier"))
240                .child(Keybinding::new_chord(
241                    ("A".to_string(), ModifierKeys::new().control(true)),
242                    ("Z".to_string(), ModifierKeys::new().shift(true)),
243                ))
244        }
245    }
246}