zeta: Improve UX for simultaneous LSP and prediction completions (#24024)
Release Notes: - N/A --------- Co-authored-by: Michael Sloan <michael@zed.dev> Co-authored-by: Danilo <danilo@zed.dev> Co-authored-by: Richard <richard@zed.dev>
This commit is contained in:
parent
b6e680ea3d
commit
4c29e1ff07
16 changed files with 1196 additions and 762 deletions
|
@ -1,7 +1,9 @@
|
|||
#![allow(missing_docs)]
|
||||
use crate::PlatformStyle;
|
||||
use crate::{h_flex, prelude::*, Icon, IconName, IconSize};
|
||||
use gpui::{relative, Action, App, FocusHandle, IntoElement, Keystroke, Window};
|
||||
use gpui::{
|
||||
relative, Action, AnyElement, App, FocusHandle, IntoElement, Keystroke, Modifiers, Window,
|
||||
};
|
||||
|
||||
#[derive(Debug, IntoElement, Clone)]
|
||||
pub struct KeyBinding {
|
||||
|
@ -41,30 +43,6 @@ impl KeyBinding {
|
|||
Some(Self::new(key_binding))
|
||||
}
|
||||
|
||||
fn icon_for_key(&self, keystroke: &Keystroke) -> Option<IconName> {
|
||||
match keystroke.key.as_str() {
|
||||
"left" => Some(IconName::ArrowLeft),
|
||||
"right" => Some(IconName::ArrowRight),
|
||||
"up" => Some(IconName::ArrowUp),
|
||||
"down" => Some(IconName::ArrowDown),
|
||||
"backspace" => Some(IconName::Backspace),
|
||||
"delete" => Some(IconName::Delete),
|
||||
"return" => Some(IconName::Return),
|
||||
"enter" => Some(IconName::Return),
|
||||
"tab" => Some(IconName::Tab),
|
||||
"space" => Some(IconName::Space),
|
||||
"escape" => Some(IconName::Escape),
|
||||
"pagedown" => Some(IconName::PageDown),
|
||||
"pageup" => Some(IconName::PageUp),
|
||||
"shift" if self.platform_style == PlatformStyle::Mac => Some(IconName::Shift),
|
||||
"control" if self.platform_style == PlatformStyle::Mac => Some(IconName::Control),
|
||||
"platform" if self.platform_style == PlatformStyle::Mac => Some(IconName::Command),
|
||||
"function" if self.platform_style == PlatformStyle::Mac => Some(IconName::Control),
|
||||
"alt" if self.platform_style == PlatformStyle::Mac => Some(IconName::Option),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(key_binding: gpui::KeyBinding) -> Self {
|
||||
Self {
|
||||
key_binding,
|
||||
|
@ -96,63 +74,148 @@ impl RenderOnce for KeyBinding {
|
|||
.gap(DynamicSpacing::Base04.rems(cx))
|
||||
.flex_none()
|
||||
.children(self.key_binding.keystrokes().iter().map(|keystroke| {
|
||||
let key_icon = self.icon_for_key(keystroke);
|
||||
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.py_0p5()
|
||||
.rounded_sm()
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.when(keystroke.modifiers.function, |el| {
|
||||
match self.platform_style {
|
||||
PlatformStyle::Mac => el.child(Key::new("fn")),
|
||||
PlatformStyle::Linux | PlatformStyle::Windows => {
|
||||
el.child(Key::new("Fn")).child(Key::new("+"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.when(keystroke.modifiers.control, |el| {
|
||||
match self.platform_style {
|
||||
PlatformStyle::Mac => el.child(KeyIcon::new(IconName::Control)),
|
||||
PlatformStyle::Linux | PlatformStyle::Windows => {
|
||||
el.child(Key::new("Ctrl")).child(Key::new("+"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.when(keystroke.modifiers.alt, |el| match self.platform_style {
|
||||
PlatformStyle::Mac => el.child(KeyIcon::new(IconName::Option)),
|
||||
PlatformStyle::Linux | PlatformStyle::Windows => {
|
||||
el.child(Key::new("Alt")).child(Key::new("+"))
|
||||
}
|
||||
})
|
||||
.when(keystroke.modifiers.platform, |el| {
|
||||
match self.platform_style {
|
||||
PlatformStyle::Mac => el.child(KeyIcon::new(IconName::Command)),
|
||||
PlatformStyle::Linux => {
|
||||
el.child(Key::new("Super")).child(Key::new("+"))
|
||||
}
|
||||
PlatformStyle::Windows => {
|
||||
el.child(Key::new("Win")).child(Key::new("+"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.when(keystroke.modifiers.shift, |el| match self.platform_style {
|
||||
PlatformStyle::Mac => el.child(KeyIcon::new(IconName::Shift)),
|
||||
PlatformStyle::Linux | PlatformStyle::Windows => {
|
||||
el.child(Key::new("Shift")).child(Key::new("+"))
|
||||
}
|
||||
})
|
||||
.map(|el| match key_icon {
|
||||
Some(icon) => el.child(KeyIcon::new(icon)),
|
||||
None => el.child(Key::new(keystroke.key.to_uppercase())),
|
||||
})
|
||||
.children(render_modifiers(
|
||||
&keystroke.modifiers,
|
||||
self.platform_style,
|
||||
None,
|
||||
))
|
||||
.map(|el| el.child(render_key(&keystroke, self.platform_style, None)))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_key(
|
||||
keystroke: &Keystroke,
|
||||
platform_style: PlatformStyle,
|
||||
color: Option<Color>,
|
||||
) -> AnyElement {
|
||||
let key_icon = icon_for_key(keystroke, platform_style);
|
||||
match key_icon {
|
||||
Some(icon) => KeyIcon::new(icon, color).into_any_element(),
|
||||
None => Key::new(
|
||||
if keystroke.key.len() > 1 {
|
||||
keystroke.key.clone()
|
||||
} else {
|
||||
keystroke.key.to_uppercase()
|
||||
},
|
||||
color,
|
||||
)
|
||||
.into_any_element(),
|
||||
}
|
||||
}
|
||||
|
||||
fn icon_for_key(keystroke: &Keystroke, platform_style: PlatformStyle) -> Option<IconName> {
|
||||
match keystroke.key.as_str() {
|
||||
"left" => Some(IconName::ArrowLeft),
|
||||
"right" => Some(IconName::ArrowRight),
|
||||
"up" => Some(IconName::ArrowUp),
|
||||
"down" => Some(IconName::ArrowDown),
|
||||
"backspace" => Some(IconName::Backspace),
|
||||
"delete" => Some(IconName::Delete),
|
||||
"return" => Some(IconName::Return),
|
||||
"enter" => Some(IconName::Return),
|
||||
// "tab" => Some(IconName::Tab),
|
||||
"space" => Some(IconName::Space),
|
||||
"escape" => Some(IconName::Escape),
|
||||
"pagedown" => Some(IconName::PageDown),
|
||||
"pageup" => Some(IconName::PageUp),
|
||||
"shift" if platform_style == PlatformStyle::Mac => Some(IconName::Shift),
|
||||
"control" if platform_style == PlatformStyle::Mac => Some(IconName::Control),
|
||||
"platform" if platform_style == PlatformStyle::Mac => Some(IconName::Command),
|
||||
"function" if platform_style == PlatformStyle::Mac => Some(IconName::Control),
|
||||
"alt" if platform_style == PlatformStyle::Mac => Some(IconName::Option),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_modifiers(
|
||||
modifiers: &Modifiers,
|
||||
platform_style: PlatformStyle,
|
||||
color: Option<Color>,
|
||||
) -> impl Iterator<Item = AnyElement> {
|
||||
enum KeyOrIcon {
|
||||
Key(&'static str),
|
||||
Icon(IconName),
|
||||
}
|
||||
|
||||
struct Modifier {
|
||||
enabled: bool,
|
||||
mac: KeyOrIcon,
|
||||
linux: KeyOrIcon,
|
||||
windows: KeyOrIcon,
|
||||
}
|
||||
|
||||
let table = {
|
||||
use KeyOrIcon::*;
|
||||
|
||||
[
|
||||
Modifier {
|
||||
enabled: modifiers.function,
|
||||
mac: Icon(IconName::Control),
|
||||
linux: Key("Fn"),
|
||||
windows: Key("Fn"),
|
||||
},
|
||||
Modifier {
|
||||
enabled: modifiers.control,
|
||||
mac: Icon(IconName::Control),
|
||||
linux: Key("Ctrl"),
|
||||
windows: Key("Ctrl"),
|
||||
},
|
||||
Modifier {
|
||||
enabled: modifiers.alt,
|
||||
mac: Icon(IconName::Option),
|
||||
linux: Key("Alt"),
|
||||
windows: Key("Alt"),
|
||||
},
|
||||
Modifier {
|
||||
enabled: modifiers.platform,
|
||||
mac: Icon(IconName::Command),
|
||||
linux: Key("Super"),
|
||||
windows: Key("Win"),
|
||||
},
|
||||
Modifier {
|
||||
enabled: modifiers.shift,
|
||||
mac: Icon(IconName::Shift),
|
||||
linux: Key("Shift"),
|
||||
windows: Key("Shift"),
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
table
|
||||
.into_iter()
|
||||
.flat_map(move |modifier| {
|
||||
if modifier.enabled {
|
||||
match platform_style {
|
||||
PlatformStyle::Mac => Some(modifier.mac),
|
||||
PlatformStyle::Linux => Some(modifier.linux)
|
||||
.into_iter()
|
||||
.chain(Some(KeyOrIcon::Key("+")))
|
||||
.next(),
|
||||
PlatformStyle::Windows => Some(modifier.windows)
|
||||
.into_iter()
|
||||
.chain(Some(KeyOrIcon::Key("+")))
|
||||
.next(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(move |key_or_icon| match key_or_icon {
|
||||
KeyOrIcon::Key(key) => Key::new(key, color).into_any_element(),
|
||||
KeyOrIcon::Icon(icon) => KeyIcon::new(icon, color).into_any_element(),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Key {
|
||||
key: SharedString,
|
||||
color: Option<Color>,
|
||||
}
|
||||
|
||||
impl RenderOnce for Key {
|
||||
|
@ -174,33 +237,37 @@ impl RenderOnce for Key {
|
|||
.h(rems_from_px(14.))
|
||||
.text_ui(cx)
|
||||
.line_height(relative(1.))
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.text_color(self.color.unwrap_or(Color::Muted).color(cx))
|
||||
.child(self.key.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn new(key: impl Into<SharedString>) -> Self {
|
||||
Self { key: key.into() }
|
||||
pub fn new(key: impl Into<SharedString>, color: Option<Color>) -> Self {
|
||||
Self {
|
||||
key: key.into(),
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct KeyIcon {
|
||||
icon: IconName,
|
||||
color: Option<Color>,
|
||||
}
|
||||
|
||||
impl RenderOnce for KeyIcon {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
Icon::new(self.icon)
|
||||
.size(IconSize::XSmall)
|
||||
.color(Color::Muted)
|
||||
.color(self.color.unwrap_or(Color::Muted))
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyIcon {
|
||||
pub fn new(icon: IconName) -> Self {
|
||||
Self { icon }
|
||||
pub fn new(icon: IconName, color: Option<Color>) -> Self {
|
||||
Self { icon, color }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue