@@ -1,6 +1,30 @@
use crate::{h_flex, prelude::*, Icon, IconName, IconSize};
use gpui::{relative, rems, Action, FocusHandle, IntoElement, Keystroke};
+/// The way a [`KeyBinding`] should be displayed.
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
+pub enum KeyBindingDisplay {
+ /// Display in macOS style.
+ Mac,
+ /// Display in Linux style.
+ Linux,
+ /// Display in Windows style.
+ Windows,
+}
+
+impl KeyBindingDisplay {
+ /// Returns the [`KeyBindingDisplay`] for the current platform.
+ pub const fn platform() -> Self {
+ if cfg!(target_os = "linux") {
+ KeyBindingDisplay::Linux
+ } else if cfg!(target_os = "windows") {
+ KeyBindingDisplay::Windows
+ } else {
+ KeyBindingDisplay::Mac
+ }
+ }
+}
+
#[derive(IntoElement, Clone)]
pub struct KeyBinding {
/// A keybinding consists of a key and a set of modifier keys.
@@ -8,41 +32,9 @@ pub struct KeyBinding {
///
/// This should always contain at least one element.
key_binding: gpui::KeyBinding,
-}
-
-impl RenderOnce for KeyBinding {
- fn render(self, cx: &mut WindowContext) -> impl IntoElement {
- h_flex()
- .flex_none()
- .gap_2()
- .children(self.key_binding.keystrokes().iter().map(|keystroke| {
- let key_icon = Self::icon_for_key(keystroke);
- h_flex()
- .flex_none()
- .gap_0p5()
- .p_0p5()
- .rounded_sm()
- .text_color(cx.theme().colors().text_muted)
- .when(keystroke.modifiers.function, |el| el.child(Key::new("fn")))
- .when(keystroke.modifiers.control, |el| {
- el.child(KeyIcon::new(IconName::Control))
- })
- .when(keystroke.modifiers.alt, |el| {
- el.child(KeyIcon::new(IconName::Option))
- })
- .when(keystroke.modifiers.command, |el| {
- el.child(KeyIcon::new(IconName::Command))
- })
- .when(keystroke.modifiers.shift, |el| {
- el.child(KeyIcon::new(IconName::Shift))
- })
- .when_some(key_icon, |el, icon| el.child(KeyIcon::new(icon)))
- .when(key_icon.is_none(), |el| {
- el.child(Key::new(keystroke.key.to_uppercase().clone()))
- })
- }))
- }
+ /// How keybindings should be displayed.
+ display: KeyBindingDisplay,
}
impl KeyBinding {
@@ -82,7 +74,67 @@ impl KeyBinding {
}
pub fn new(key_binding: gpui::KeyBinding) -> Self {
- Self { key_binding }
+ Self {
+ key_binding,
+ display: KeyBindingDisplay::platform(),
+ }
+ }
+
+ /// Sets how this [`KeyBinding`] should be displayed.
+ pub fn display(mut self, display: KeyBindingDisplay) -> Self {
+ self.display = display;
+ self
+ }
+}
+
+impl RenderOnce for KeyBinding {
+ fn render(self, cx: &mut WindowContext) -> impl IntoElement {
+ h_flex()
+ .flex_none()
+ .gap_2()
+ .children(self.key_binding.keystrokes().iter().map(|keystroke| {
+ let key_icon = Self::icon_for_key(keystroke);
+
+ h_flex()
+ .flex_none()
+ .gap_0p5()
+ .p_0p5()
+ .rounded_sm()
+ .text_color(cx.theme().colors().text_muted)
+ .when(keystroke.modifiers.function, |el| match self.display {
+ KeyBindingDisplay::Mac => el.child(Key::new("fn")),
+ KeyBindingDisplay::Linux | KeyBindingDisplay::Windows => {
+ el.child(Key::new("Fn"))
+ }
+ })
+ .when(keystroke.modifiers.control, |el| match self.display {
+ KeyBindingDisplay::Mac => el.child(KeyIcon::new(IconName::Control)),
+ KeyBindingDisplay::Linux | KeyBindingDisplay::Windows => {
+ el.child(Key::new("Ctrl"))
+ }
+ })
+ .when(keystroke.modifiers.alt, |el| match self.display {
+ KeyBindingDisplay::Mac => el.child(KeyIcon::new(IconName::Option)),
+ KeyBindingDisplay::Linux | KeyBindingDisplay::Windows => {
+ el.child(Key::new("Alt"))
+ }
+ })
+ .when(keystroke.modifiers.command, |el| match self.display {
+ KeyBindingDisplay::Mac => el.child(KeyIcon::new(IconName::Command)),
+ KeyBindingDisplay::Linux => el.child(Key::new("Super")),
+ KeyBindingDisplay::Windows => el.child(Key::new("Win")),
+ })
+ .when(keystroke.modifiers.shift, |el| match self.display {
+ KeyBindingDisplay::Mac => el.child(KeyIcon::new(IconName::Option)),
+ KeyBindingDisplay::Linux | KeyBindingDisplay::Windows => {
+ el.child(Key::new("Shift"))
+ }
+ })
+ .map(|el| match key_icon {
+ Some(icon) => el.child(KeyIcon::new(icon)),
+ None => el.child(Key::new(keystroke.key.to_uppercase())),
+ })
+ }))
}
}
@@ -1,10 +1,9 @@
use gpui::NoAction;
use gpui::Render;
use itertools::Itertools;
-use story::Story;
+use story::{Story, StoryContainer};
-use crate::prelude::*;
-use crate::KeyBinding;
+use crate::{prelude::*, KeyBinding, KeyBindingDisplay};
pub struct KeybindingStory;
@@ -16,23 +15,28 @@ impl Render for KeybindingStory {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
let all_modifier_permutations = ["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
- Story::container()
- .child(Story::title_for::<KeyBinding>())
- .child(Story::label("Single Key"))
- .child(KeyBinding::new(binding("Z")))
- .child(Story::label("Single Key with Modifier"))
- .child(
- div()
- .flex()
- .gap_3()
- .child(KeyBinding::new(binding("ctrl-c")))
- .child(KeyBinding::new(binding("alt-c")))
- .child(KeyBinding::new(binding("cmd-c")))
- .child(KeyBinding::new(binding("shift-c"))),
- )
- .child(Story::label("Single Key with Modifier (Permuted)"))
- .child(
- div().flex().flex_col().children(
+ StoryContainer::new(
+ "KeyBinding",
+ "crates/ui/src/components/stories/keybinding.rs",
+ )
+ .child(Story::label("Single Key"))
+ .child(KeyBinding::new(binding("Z")))
+ .child(Story::label("Single Key with Modifier"))
+ .child(
+ div()
+ .flex()
+ .gap_3()
+ .child(KeyBinding::new(binding("ctrl-c")))
+ .child(KeyBinding::new(binding("alt-c")))
+ .child(KeyBinding::new(binding("cmd-c")))
+ .child(KeyBinding::new(binding("shift-c"))),
+ )
+ .child(Story::label("Single Key with Modifier (Permuted)"))
+ .child(
+ div()
+ .flex()
+ .flex_col()
+ .children(
all_modifier_permutations
.chunks(4)
.into_iter()
@@ -46,13 +50,27 @@ impl Render for KeybindingStory {
}))
}),
),
- )
- .child(Story::label("Single Key with All Modifiers"))
- .child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")))
- .child(Story::label("Chord"))
- .child(KeyBinding::new(binding("a z")))
- .child(Story::label("Chord with Modifier"))
- .child(KeyBinding::new(binding("ctrl-a shift-z")))
- .child(KeyBinding::new(binding("fn-s")))
+ )
+ .child(Story::label("Single Key with All Modifiers"))
+ .child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")))
+ .child(Story::label("Chord"))
+ .child(KeyBinding::new(binding("a z")))
+ .child(Story::label("Chord with Modifier"))
+ .child(KeyBinding::new(binding("ctrl-a shift-z")))
+ .child(KeyBinding::new(binding("fn-s")))
+ .child(Story::label("Single Key with All Modifiers (Linux)"))
+ .child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")).display(KeyBindingDisplay::Linux))
+ .child(Story::label("Chord (Linux)"))
+ .child(KeyBinding::new(binding("a z")).display(KeyBindingDisplay::Linux))
+ .child(Story::label("Chord with Modifier (Linux)"))
+ .child(KeyBinding::new(binding("ctrl-a shift-z")).display(KeyBindingDisplay::Linux))
+ .child(KeyBinding::new(binding("fn-s")).display(KeyBindingDisplay::Linux))
+ .child(Story::label("Single Key with All Modifiers (Windows)"))
+ .child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")).display(KeyBindingDisplay::Windows))
+ .child(Story::label("Chord (Windows)"))
+ .child(KeyBinding::new(binding("a z")).display(KeyBindingDisplay::Windows))
+ .child(Story::label("Chord with Modifier (Windows)"))
+ .child(KeyBinding::new(binding("ctrl-a shift-z")).display(KeyBindingDisplay::Windows))
+ .child(KeyBinding::new(binding("fn-s")).display(KeyBindingDisplay::Windows))
}
}