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