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