keybinding.rs

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