keybinding.rs

  1use crate::{h_stack, prelude::*, Icon, IconElement, IconSize};
  2use gpui::{relative, rems, Action, Div, FocusHandle, IntoElement, Keystroke};
  3
  4#[derive(IntoElement, Clone)]
  5pub struct KeyBinding {
  6    /// A keybinding consists of a key and a set of modifier keys.
  7    /// More then one keybinding produces a chord.
  8    ///
  9    /// This should always contain at least one element.
 10    key_binding: gpui::KeyBinding,
 11}
 12
 13impl RenderOnce for KeyBinding {
 14    type Output = Div;
 15
 16    fn render(self, cx: &mut WindowContext) -> Self::Output {
 17        h_stack()
 18            .flex_none()
 19            .gap_2()
 20            .children(self.key_binding.keystrokes().iter().map(|keystroke| {
 21                let key_icon = Self::icon_for_key(&keystroke);
 22
 23                h_stack()
 24                    .flex_none()
 25                    .gap_0p5()
 26                    .bg(cx.theme().colors().element_background)
 27                    .p_0p5()
 28                    .rounded_sm()
 29                    .when(keystroke.modifiers.function, |el| el.child(Key::new("fn")))
 30                    .when(keystroke.modifiers.control, |el| {
 31                        el.child(KeyIcon::new(Icon::Control))
 32                    })
 33                    .when(keystroke.modifiers.alt, |el| {
 34                        el.child(KeyIcon::new(Icon::Option))
 35                    })
 36                    .when(keystroke.modifiers.command, |el| {
 37                        el.child(KeyIcon::new(Icon::Command))
 38                    })
 39                    .when(keystroke.modifiers.shift, |el| {
 40                        el.child(KeyIcon::new(Icon::Shift))
 41                    })
 42                    .when_some(key_icon, |el, icon| el.child(KeyIcon::new(icon)))
 43                    .when(key_icon.is_none(), |el| {
 44                        el.child(Key::new(keystroke.key.to_uppercase().clone()))
 45                    })
 46            }))
 47    }
 48}
 49
 50impl KeyBinding {
 51    pub fn for_action(action: &dyn Action, cx: &mut WindowContext) -> Option<Self> {
 52        let key_binding = cx.bindings_for_action(action).last().cloned()?;
 53        Some(Self::new(key_binding))
 54    }
 55
 56    // like for_action(), but lets you specify the context from which keybindings
 57    // are matched.
 58    pub fn for_action_in(
 59        action: &dyn Action,
 60        focus: &FocusHandle,
 61        cx: &mut WindowContext,
 62    ) -> Option<Self> {
 63        let key_binding = cx.bindings_for_action_in(action, focus).last().cloned()?;
 64        Some(Self::new(key_binding))
 65    }
 66
 67    fn icon_for_key(keystroke: &Keystroke) -> Option<Icon> {
 68        match keystroke.key.as_str() {
 69            "backspace" => Some(Icon::Backspace),
 70            "delete" => Some(Icon::Delete),
 71            "down" => Some(Icon::ArrowDown),
 72            "enter" => Some(Icon::Return),
 73            "escape" => Some(Icon::Escape),
 74            "left" => Some(Icon::ArrowLeft),
 75            "pagedown" => Some(Icon::PageDown),
 76            "pageup" => Some(Icon::PageUp),
 77            "return" => Some(Icon::Return),
 78            "right" => Some(Icon::ArrowRight),
 79            "space" => Some(Icon::Space),
 80            "tab" => Some(Icon::Tab),
 81            "up" => Some(Icon::ArrowUp),
 82            _ => None,
 83        }
 84    }
 85
 86    pub fn new(key_binding: gpui::KeyBinding) -> Self {
 87        Self { key_binding }
 88    }
 89}
 90
 91#[derive(IntoElement)]
 92pub struct Key {
 93    key: SharedString,
 94}
 95
 96impl RenderOnce for Key {
 97    type Output = Div;
 98
 99    fn render(self, cx: &mut WindowContext) -> Self::Output {
100        let single_char = self.key.len() == 1;
101
102        div()
103            .py_0()
104            .map(|this| {
105                if single_char {
106                    this.w(rems(14. / 16.)).flex().flex_none().justify_center()
107                } else {
108                    this.px_0p5()
109                }
110            })
111            .h(rems(14. / 16.))
112            .text_ui()
113            .line_height(relative(1.))
114            .text_color(cx.theme().colors().text)
115            .child(self.key.clone())
116    }
117}
118
119impl Key {
120    pub fn new(key: impl Into<SharedString>) -> Self {
121        Self { key: key.into() }
122    }
123}
124
125#[derive(IntoElement)]
126pub struct KeyIcon {
127    icon: Icon,
128}
129
130impl RenderOnce for KeyIcon {
131    type Output = Div;
132
133    fn render(self, _cx: &mut WindowContext) -> Self::Output {
134        div()
135            .w(rems(14. / 16.))
136            .child(IconElement::new(self.icon).size(IconSize::Small))
137    }
138}
139
140impl KeyIcon {
141    pub fn new(icon: Icon) -> Self {
142        Self { icon }
143    }
144}