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<State = 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<State = 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}