ui: Extend KeyBinding
with support for displaying keybindings for other platforms (#9192)
This PR extends the `KeyBinding` component with support for displaying keybindings for platforms other than macOS. <img width="824" alt="Screenshot 2024-03-11 at 2 41 59 PM" src="https://github.com/zed-industries/zed/assets/1486634/7108b17d-dfc3-42ee-9bfd-c58b334d7374"> Release Notes: - N/A
This commit is contained in:
parent
dfcc143ead
commit
f2aa183512
2 changed files with 133 additions and 63 deletions
|
@ -1,6 +1,30 @@
|
||||||
use crate::{h_flex, prelude::*, Icon, IconName, IconSize};
|
use crate::{h_flex, prelude::*, Icon, IconName, IconSize};
|
||||||
use gpui::{relative, rems, Action, FocusHandle, IntoElement, Keystroke};
|
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)]
|
#[derive(IntoElement, Clone)]
|
||||||
pub struct KeyBinding {
|
pub struct KeyBinding {
|
||||||
/// A keybinding consists of a key and a set of modifier keys.
|
/// 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.
|
/// This should always contain at least one element.
|
||||||
key_binding: gpui::KeyBinding,
|
key_binding: gpui::KeyBinding,
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for KeyBinding {
|
/// How keybindings should be displayed.
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
display: KeyBindingDisplay,
|
||||||
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()))
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyBinding {
|
impl KeyBinding {
|
||||||
|
@ -82,7 +74,67 @@ impl KeyBinding {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(key_binding: gpui::KeyBinding) -> Self {
|
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::NoAction;
|
||||||
use gpui::Render;
|
use gpui::Render;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use story::Story;
|
use story::{Story, StoryContainer};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::{prelude::*, KeyBinding, KeyBindingDisplay};
|
||||||
use crate::KeyBinding;
|
|
||||||
|
|
||||||
pub struct KeybindingStory;
|
pub struct KeybindingStory;
|
||||||
|
|
||||||
|
@ -16,23 +15,28 @@ impl Render for KeybindingStory {
|
||||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let all_modifier_permutations = ["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
|
let all_modifier_permutations = ["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
|
||||||
|
|
||||||
Story::container()
|
StoryContainer::new(
|
||||||
.child(Story::title_for::<KeyBinding>())
|
"KeyBinding",
|
||||||
.child(Story::label("Single Key"))
|
"crates/ui/src/components/stories/keybinding.rs",
|
||||||
.child(KeyBinding::new(binding("Z")))
|
)
|
||||||
.child(Story::label("Single Key with Modifier"))
|
.child(Story::label("Single Key"))
|
||||||
.child(
|
.child(KeyBinding::new(binding("Z")))
|
||||||
div()
|
.child(Story::label("Single Key with Modifier"))
|
||||||
.flex()
|
.child(
|
||||||
.gap_3()
|
div()
|
||||||
.child(KeyBinding::new(binding("ctrl-c")))
|
.flex()
|
||||||
.child(KeyBinding::new(binding("alt-c")))
|
.gap_3()
|
||||||
.child(KeyBinding::new(binding("cmd-c")))
|
.child(KeyBinding::new(binding("ctrl-c")))
|
||||||
.child(KeyBinding::new(binding("shift-c"))),
|
.child(KeyBinding::new(binding("alt-c")))
|
||||||
)
|
.child(KeyBinding::new(binding("cmd-c")))
|
||||||
.child(Story::label("Single Key with Modifier (Permuted)"))
|
.child(KeyBinding::new(binding("shift-c"))),
|
||||||
.child(
|
)
|
||||||
div().flex().flex_col().children(
|
.child(Story::label("Single Key with Modifier (Permuted)"))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.children(
|
||||||
all_modifier_permutations
|
all_modifier_permutations
|
||||||
.chunks(4)
|
.chunks(4)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -46,13 +50,27 @@ impl Render for KeybindingStory {
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(Story::label("Single Key with All Modifiers"))
|
.child(Story::label("Single Key with All Modifiers"))
|
||||||
.child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")))
|
.child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")))
|
||||||
.child(Story::label("Chord"))
|
.child(Story::label("Chord"))
|
||||||
.child(KeyBinding::new(binding("a z")))
|
.child(KeyBinding::new(binding("a z")))
|
||||||
.child(Story::label("Chord with Modifier"))
|
.child(Story::label("Chord with Modifier"))
|
||||||
.child(KeyBinding::new(binding("ctrl-a shift-z")))
|
.child(KeyBinding::new(binding("ctrl-a shift-z")))
|
||||||
.child(KeyBinding::new(binding("fn-s")))
|
.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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue