Fix keybind hints flickering in certain scenarios (#40927)
Closes #39172 This refactors when we resolve UI keybindings in an effort to reduce flickering whilst painting these: Previously, we would always resolve these upon creating the binding. This could lead to cases where the corresponding context was not yet available and no binding could be resolved, even if the binding was then available on the next presented frame. Following that, on the next rerender of whatever requested this keybinding, the keybind for that context would then be found, we would render that and then also win a layout shift in that process, as we went from nothing rendered to something rendered between these frames. With these changes, this now happens less often, because we only look for the keybinding once the context can actually be resolved in the window. | Before | After | | --- | --- | | https://github.com/user-attachments/assets/adebf8ac-217d-4c7f-ae5a-bab3aa0b0ee8 | https://github.com/user-attachments/assets/70a82b4b-488f-4a9f-94d7-b6d0a49aada9 | Also reduced cloning in the keymap editor in this process, since that requiered changing due to this anyway. Release Notes: - Fixed some cases where keybinds would appear with a slight delay, causing a flicker in the process
This commit is contained in:
@@ -834,9 +834,9 @@ impl ContextMenu {
|
||||
.disabled(true)
|
||||
.child(Label::new(label.clone()))
|
||||
.into_any_element(),
|
||||
ContextMenuItem::Entry(entry) => self
|
||||
.render_menu_entry(ix, entry, window, cx)
|
||||
.into_any_element(),
|
||||
ContextMenuItem::Entry(entry) => {
|
||||
self.render_menu_entry(ix, entry, cx).into_any_element()
|
||||
}
|
||||
ContextMenuItem::CustomEntry {
|
||||
entry_render,
|
||||
handler,
|
||||
@@ -883,7 +883,6 @@ impl ContextMenu {
|
||||
&self,
|
||||
ix: usize,
|
||||
entry: &ContextMenuEntry,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let ContextMenuEntry {
|
||||
@@ -980,18 +979,18 @@ impl ContextMenu {
|
||||
.justify_between()
|
||||
.child(label_element)
|
||||
.debug_selector(|| format!("MENU_ITEM-{}", label))
|
||||
.children(action.as_ref().and_then(|action| {
|
||||
self.action_context
|
||||
.children(action.as_ref().map(|action| {
|
||||
let binding = self
|
||||
.action_context
|
||||
.as_ref()
|
||||
.and_then(|focus| {
|
||||
KeyBinding::for_action_in(&**action, focus, window, cx)
|
||||
})
|
||||
.or_else(|| KeyBinding::for_action(&**action, window, cx))
|
||||
.map(|binding| {
|
||||
div().ml_4().child(binding.disabled(*disabled)).when(
|
||||
*disabled && documentation_aside.is_some(),
|
||||
|parent| parent.invisible(),
|
||||
)
|
||||
.map(|focus| KeyBinding::for_action_in(&**action, focus, cx))
|
||||
.unwrap_or_else(|| KeyBinding::for_action(&**action, cx));
|
||||
|
||||
div()
|
||||
.ml_4()
|
||||
.child(binding.disabled(*disabled))
|
||||
.when(*disabled && documentation_aside.is_some(), |parent| {
|
||||
parent.invisible()
|
||||
})
|
||||
}))
|
||||
.when(*disabled && documentation_aside.is_some(), |parent| {
|
||||
@@ -1016,7 +1015,7 @@ impl ContextMenu {
|
||||
let action_context = self.action_context.clone();
|
||||
let title = title.clone();
|
||||
let action = action.boxed_clone();
|
||||
move |window, cx| {
|
||||
move |_window, cx| {
|
||||
action_context
|
||||
.as_ref()
|
||||
.map(|focus| {
|
||||
@@ -1024,17 +1023,11 @@ impl ContextMenu {
|
||||
title.clone(),
|
||||
&*action,
|
||||
focus,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
Tooltip::for_action(
|
||||
title.clone(),
|
||||
&*action,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
Tooltip::for_action(title.clone(), &*action, cx)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::PlatformStyle;
|
||||
use crate::{Icon, IconName, IconSize, h_flex, prelude::*};
|
||||
use gpui::{
|
||||
@@ -5,23 +7,49 @@ use gpui::{
|
||||
Modifiers, Window, relative,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use settings::KeybindSource;
|
||||
|
||||
#[derive(Debug, IntoElement, Clone, RegisterComponent)]
|
||||
#[derive(Debug)]
|
||||
enum Source {
|
||||
Action {
|
||||
action: Box<dyn Action>,
|
||||
focus_handle: Option<FocusHandle>,
|
||||
},
|
||||
Keystrokes {
|
||||
/// A keybinding consists of a set of keystrokes,
|
||||
/// where each keystroke is a key and a set of modifier keys.
|
||||
/// More than one keystroke produces a chord.
|
||||
///
|
||||
/// This should always contain at least one keystroke.
|
||||
keystrokes: Rc<[KeybindingKeystroke]>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Clone for Source {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Source::Action {
|
||||
action,
|
||||
focus_handle,
|
||||
} => Source::Action {
|
||||
action: action.boxed_clone(),
|
||||
focus_handle: focus_handle.clone(),
|
||||
},
|
||||
Source::Keystrokes { keystrokes } => Source::Keystrokes {
|
||||
keystrokes: keystrokes.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, IntoElement, RegisterComponent)]
|
||||
pub struct KeyBinding {
|
||||
/// A keybinding consists of a set of keystrokes,
|
||||
/// where each keystroke is a key and a set of modifier keys.
|
||||
/// More than one keystroke produces a chord.
|
||||
///
|
||||
/// This should always contain at least one keystroke.
|
||||
pub keystrokes: Vec<KeybindingKeystroke>,
|
||||
|
||||
source: Source,
|
||||
size: Option<AbsoluteLength>,
|
||||
/// The [`PlatformStyle`] to use when displaying this keybinding.
|
||||
platform_style: PlatformStyle,
|
||||
size: Option<AbsoluteLength>,
|
||||
|
||||
/// Determines whether the keybinding is meant for vim mode.
|
||||
vim_mode: bool,
|
||||
|
||||
/// Indicates whether the keybinding is currently disabled.
|
||||
disabled: bool,
|
||||
}
|
||||
@@ -32,23 +60,13 @@ impl Global for VimStyle {}
|
||||
impl KeyBinding {
|
||||
/// Returns the highest precedence keybinding for an action. This is the last binding added to
|
||||
/// the keymap. User bindings are added after built-in bindings so that they take precedence.
|
||||
pub fn for_action(action: &dyn Action, window: &mut Window, cx: &App) -> Option<Self> {
|
||||
if let Some(focused) = window.focused(cx) {
|
||||
return Self::for_action_in(action, &focused, window, cx);
|
||||
}
|
||||
let key_binding = window.highest_precedence_binding_for_action(action)?;
|
||||
Some(Self::new_from_gpui(key_binding, cx))
|
||||
pub fn for_action(action: &dyn Action, cx: &App) -> Self {
|
||||
Self::new(action, None, cx)
|
||||
}
|
||||
|
||||
/// Like `for_action`, but lets you specify the context from which keybindings are matched.
|
||||
pub fn for_action_in(
|
||||
action: &dyn Action,
|
||||
focus: &FocusHandle,
|
||||
window: &Window,
|
||||
cx: &App,
|
||||
) -> Option<Self> {
|
||||
let key_binding = window.highest_precedence_binding_for_action_in(action, focus)?;
|
||||
Some(Self::new_from_gpui(key_binding, cx))
|
||||
pub fn for_action_in(action: &dyn Action, focus: &FocusHandle, cx: &App) -> Self {
|
||||
Self::new(action, Some(focus.clone()), cx)
|
||||
}
|
||||
|
||||
pub fn set_vim_mode(cx: &mut App, enabled: bool) {
|
||||
@@ -59,18 +77,27 @@ impl KeyBinding {
|
||||
cx.try_global::<VimStyle>().is_some_and(|g| g.0)
|
||||
}
|
||||
|
||||
pub fn new(keystrokes: Vec<KeybindingKeystroke>, cx: &App) -> Self {
|
||||
pub fn new(action: &dyn Action, focus_handle: Option<FocusHandle>, cx: &App) -> Self {
|
||||
Self {
|
||||
keystrokes,
|
||||
platform_style: PlatformStyle::platform(),
|
||||
source: Source::Action {
|
||||
action: action.boxed_clone(),
|
||||
focus_handle,
|
||||
},
|
||||
size: None,
|
||||
vim_mode: KeyBinding::is_vim_mode(cx),
|
||||
platform_style: PlatformStyle::platform(),
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_gpui(key_binding: gpui::KeyBinding, cx: &App) -> Self {
|
||||
Self::new(key_binding.keystrokes().to_vec(), cx)
|
||||
pub fn from_keystrokes(keystrokes: Rc<[KeybindingKeystroke]>, source: KeybindSource) -> Self {
|
||||
Self {
|
||||
source: Source::Keystrokes { keystrokes },
|
||||
size: None,
|
||||
vim_mode: source == KeybindSource::Vim,
|
||||
platform_style: PlatformStyle::platform(),
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the [`PlatformStyle`] for this [`KeyBinding`].
|
||||
@@ -91,11 +118,6 @@ impl KeyBinding {
|
||||
self.disabled = disabled;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn vim_mode(mut self, enabled: bool) -> Self {
|
||||
self.vim_mode = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn render_key(
|
||||
@@ -115,36 +137,54 @@ fn render_key(
|
||||
}
|
||||
|
||||
impl RenderOnce for KeyBinding {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let color = self.disabled.then_some(Color::Disabled);
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let render_keybinding = |keystrokes: &[KeybindingKeystroke]| {
|
||||
let color = self.disabled.then_some(Color::Disabled);
|
||||
|
||||
h_flex()
|
||||
.debug_selector(|| {
|
||||
format!(
|
||||
"KEY_BINDING-{}",
|
||||
self.keystrokes
|
||||
.iter()
|
||||
.map(|k| k.key().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
)
|
||||
})
|
||||
.gap(DynamicSpacing::Base04.rems(cx))
|
||||
.flex_none()
|
||||
.children(self.keystrokes.iter().map(|keystroke| {
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.py_0p5()
|
||||
.rounded_xs()
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.children(render_keybinding_keystroke(
|
||||
keystroke,
|
||||
color,
|
||||
self.size,
|
||||
self.platform_style,
|
||||
self.vim_mode,
|
||||
))
|
||||
}))
|
||||
h_flex()
|
||||
.debug_selector(|| {
|
||||
format!(
|
||||
"KEY_BINDING-{}",
|
||||
keystrokes
|
||||
.iter()
|
||||
.map(|k| k.key().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
)
|
||||
})
|
||||
.gap(DynamicSpacing::Base04.rems(cx))
|
||||
.flex_none()
|
||||
.children(keystrokes.iter().map(|keystroke| {
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.py_0p5()
|
||||
.rounded_xs()
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.children(render_keybinding_keystroke(
|
||||
keystroke,
|
||||
color,
|
||||
self.size,
|
||||
PlatformStyle::platform(),
|
||||
self.vim_mode,
|
||||
))
|
||||
}))
|
||||
.into_any_element()
|
||||
};
|
||||
|
||||
match self.source {
|
||||
Source::Action {
|
||||
action,
|
||||
focus_handle,
|
||||
} => focus_handle
|
||||
.or_else(|| window.focused(cx))
|
||||
.and_then(|focus| {
|
||||
window.highest_precedence_binding_for_action_in(action.as_ref(), &focus)
|
||||
})
|
||||
.or_else(|| window.highest_precedence_binding_for_action(action.as_ref()))
|
||||
.map(|binding| render_keybinding(binding.keystrokes())),
|
||||
Source::Keystrokes { keystrokes } => Some(render_keybinding(keystrokes.as_ref())),
|
||||
}
|
||||
.unwrap_or_else(|| gpui::Empty.into_any_element())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,79 +557,79 @@ impl Component for KeyBinding {
|
||||
)
|
||||
}
|
||||
|
||||
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
|
||||
Some(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.children(vec![
|
||||
example_group_with_title(
|
||||
"Basic Usage",
|
||||
vec![
|
||||
single_example(
|
||||
"Default",
|
||||
KeyBinding::new_from_gpui(
|
||||
gpui::KeyBinding::new("ctrl-s", gpui::NoAction, None),
|
||||
cx,
|
||||
)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Mac Style",
|
||||
KeyBinding::new_from_gpui(
|
||||
gpui::KeyBinding::new("cmd-s", gpui::NoAction, None),
|
||||
cx,
|
||||
)
|
||||
.platform_style(PlatformStyle::Mac)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Windows Style",
|
||||
KeyBinding::new_from_gpui(
|
||||
gpui::KeyBinding::new("ctrl-s", gpui::NoAction, None),
|
||||
cx,
|
||||
)
|
||||
.platform_style(PlatformStyle::Windows)
|
||||
.into_any_element(),
|
||||
),
|
||||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"Vim Mode",
|
||||
vec![single_example(
|
||||
"Vim Mode Enabled",
|
||||
KeyBinding::new_from_gpui(
|
||||
gpui::KeyBinding::new("dd", gpui::NoAction, None),
|
||||
cx,
|
||||
)
|
||||
.vim_mode(true)
|
||||
.into_any_element(),
|
||||
)],
|
||||
),
|
||||
example_group_with_title(
|
||||
"Complex Bindings",
|
||||
vec![
|
||||
single_example(
|
||||
"Multiple Keys",
|
||||
KeyBinding::new_from_gpui(
|
||||
gpui::KeyBinding::new("ctrl-k ctrl-b", gpui::NoAction, None),
|
||||
cx,
|
||||
)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"With Shift",
|
||||
KeyBinding::new_from_gpui(
|
||||
gpui::KeyBinding::new("shift-cmd-p", gpui::NoAction, None),
|
||||
cx,
|
||||
)
|
||||
.into_any_element(),
|
||||
),
|
||||
],
|
||||
),
|
||||
])
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
// fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
|
||||
// Some(
|
||||
// v_flex()
|
||||
// .gap_6()
|
||||
// .children(vec![
|
||||
// example_group_with_title(
|
||||
// "Basic Usage",
|
||||
// vec![
|
||||
// single_example(
|
||||
// "Default",
|
||||
// KeyBinding::new_from_gpui(
|
||||
// gpui::KeyBinding::new("ctrl-s", gpui::NoAction, None),
|
||||
// cx,
|
||||
// )
|
||||
// .into_any_element(),
|
||||
// ),
|
||||
// single_example(
|
||||
// "Mac Style",
|
||||
// KeyBinding::new_from_gpui(
|
||||
// gpui::KeyBinding::new("cmd-s", gpui::NoAction, None),
|
||||
// cx,
|
||||
// )
|
||||
// .platform_style(PlatformStyle::Mac)
|
||||
// .into_any_element(),
|
||||
// ),
|
||||
// single_example(
|
||||
// "Windows Style",
|
||||
// KeyBinding::new_from_gpui(
|
||||
// gpui::KeyBinding::new("ctrl-s", gpui::NoAction, None),
|
||||
// cx,
|
||||
// )
|
||||
// .platform_style(PlatformStyle::Windows)
|
||||
// .into_any_element(),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// example_group_with_title(
|
||||
// "Vim Mode",
|
||||
// vec![single_example(
|
||||
// "Vim Mode Enabled",
|
||||
// KeyBinding::new_from_gpui(
|
||||
// gpui::KeyBinding::new("dd", gpui::NoAction, None),
|
||||
// cx,
|
||||
// )
|
||||
// .vim_mode(true)
|
||||
// .into_any_element(),
|
||||
// )],
|
||||
// ),
|
||||
// example_group_with_title(
|
||||
// "Complex Bindings",
|
||||
// vec![
|
||||
// single_example(
|
||||
// "Multiple Keys",
|
||||
// KeyBinding::new_from_gpui(
|
||||
// gpui::KeyBinding::new("ctrl-k ctrl-b", gpui::NoAction, None),
|
||||
// cx,
|
||||
// )
|
||||
// .into_any_element(),
|
||||
// ),
|
||||
// single_example(
|
||||
// "With Shift",
|
||||
// KeyBinding::new_from_gpui(
|
||||
// gpui::KeyBinding::new("shift-cmd-p", gpui::NoAction, None),
|
||||
// cx,
|
||||
// )
|
||||
// .into_any_element(),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ])
|
||||
// .into_any_element(),
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -14,10 +14,11 @@ use theme::Appearance;
|
||||
/// use gpui::{App, Hsla, KeybindingKeystroke, Keystroke};
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::{KeyBinding, KeybindingHint};
|
||||
/// use settings::KeybindSource;
|
||||
///
|
||||
/// # fn example(cx: &App) {
|
||||
/// let hint = KeybindingHint::new(
|
||||
/// KeyBinding::new(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-s").unwrap())], cx),
|
||||
/// KeyBinding::from_keystrokes(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-s").unwrap())].into(), KeybindSource::Base),
|
||||
/// Hsla::black()
|
||||
/// )
|
||||
/// .prefix("Save:")
|
||||
@@ -45,10 +46,11 @@ impl KeybindingHint {
|
||||
/// use gpui::{App, Hsla, KeybindingKeystroke, Keystroke};
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::{KeyBinding, KeybindingHint};
|
||||
/// use settings::KeybindSource;
|
||||
///
|
||||
/// # fn example(cx: &App) {
|
||||
/// let hint = KeybindingHint::new(
|
||||
/// KeyBinding::new(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-c").unwrap())], cx),
|
||||
/// KeyBinding::from_keystrokes(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-c").unwrap())].into(), KeybindSource::Base),
|
||||
/// Hsla::black()
|
||||
/// );
|
||||
/// # }
|
||||
@@ -74,11 +76,12 @@ impl KeybindingHint {
|
||||
/// use gpui::{App, Hsla, KeybindingKeystroke, Keystroke};
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::{KeyBinding, KeybindingHint};
|
||||
/// use settings::KeybindSource;
|
||||
///
|
||||
/// # fn example(cx: &App) {
|
||||
/// let hint = KeybindingHint::with_prefix(
|
||||
/// "Copy:",
|
||||
/// KeyBinding::new(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-c").unwrap())], cx),
|
||||
/// KeyBinding::from_keystrokes(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-c").unwrap())].into(), KeybindSource::Base),
|
||||
/// Hsla::black()
|
||||
/// );
|
||||
/// # }
|
||||
@@ -108,10 +111,11 @@ impl KeybindingHint {
|
||||
/// use gpui::{App, Hsla, KeybindingKeystroke, Keystroke};
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::{KeyBinding, KeybindingHint};
|
||||
/// use settings::KeybindSource;
|
||||
///
|
||||
/// # fn example(cx: &App) {
|
||||
/// let hint = KeybindingHint::with_suffix(
|
||||
/// KeyBinding::new(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-v").unwrap())], cx),
|
||||
/// KeyBinding::from_keystrokes(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-v").unwrap())].into(), KeybindSource::Base),
|
||||
/// "Paste",
|
||||
/// Hsla::black()
|
||||
/// );
|
||||
@@ -141,10 +145,11 @@ impl KeybindingHint {
|
||||
/// use gpui::{App, Hsla, KeybindingKeystroke, Keystroke};
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::{KeyBinding, KeybindingHint};
|
||||
/// use settings::KeybindSource;
|
||||
///
|
||||
/// # fn example(cx: &App) {
|
||||
/// let hint = KeybindingHint::new(
|
||||
/// KeyBinding::new(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-x").unwrap())], cx),
|
||||
/// KeyBinding::from_keystrokes(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-x").unwrap())].into(), KeybindSource::Base),
|
||||
/// Hsla::black()
|
||||
/// )
|
||||
/// .prefix("Cut:");
|
||||
@@ -165,10 +170,11 @@ impl KeybindingHint {
|
||||
/// use gpui::{App, Hsla, KeybindingKeystroke, Keystroke};
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::{KeyBinding, KeybindingHint};
|
||||
/// use settings::KeybindSource;
|
||||
///
|
||||
/// # fn example(cx: &App) {
|
||||
/// let hint = KeybindingHint::new(
|
||||
/// KeyBinding::new(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-f").unwrap())], cx),
|
||||
/// KeyBinding::from_keystrokes(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-f").unwrap())].into(), KeybindSource::Base),
|
||||
/// Hsla::black()
|
||||
/// )
|
||||
/// .suffix("Find");
|
||||
@@ -189,10 +195,11 @@ impl KeybindingHint {
|
||||
/// use gpui::{App, Hsla, KeybindingKeystroke, Keystroke};
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::{KeyBinding, KeybindingHint};
|
||||
/// use settings::KeybindSource;
|
||||
///
|
||||
/// # fn example(cx: &App) {
|
||||
/// let hint = KeybindingHint::new(
|
||||
/// KeyBinding::new(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-z").unwrap())], cx),
|
||||
/// KeyBinding::from_keystrokes(vec![KeybindingKeystroke::from_keystroke(Keystroke::parse("ctrl-z").unwrap())].into(), KeybindSource::Base),
|
||||
/// Hsla::black()
|
||||
/// )
|
||||
/// .size(Pixels::from(16.0));
|
||||
@@ -265,10 +272,8 @@ impl Component for KeybindingHint {
|
||||
Some("Displays a keyboard shortcut hint with optional prefix and suffix text")
|
||||
}
|
||||
|
||||
fn preview(window: &mut Window, cx: &mut App) -> Option<AnyElement> {
|
||||
let enter_fallback = gpui::KeyBinding::new("enter", menu::Confirm, None);
|
||||
let enter = KeyBinding::for_action(&menu::Confirm, window, cx)
|
||||
.unwrap_or(KeyBinding::new_from_gpui(enter_fallback, cx));
|
||||
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
|
||||
let enter = KeyBinding::for_action(&menu::Confirm, cx);
|
||||
|
||||
let bg_color = cx.theme().colors().surface_background;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use gpui::NoAction;
|
||||
use gpui::Render;
|
||||
use itertools::Itertools;
|
||||
use settings::KeybindSource;
|
||||
use story::Story;
|
||||
|
||||
use crate::{KeyBinding, prelude::*};
|
||||
@@ -15,19 +16,36 @@ impl Render for KeybindingStory {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let all_modifier_permutations = ["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
|
||||
|
||||
const SOURCE: KeybindSource = KeybindSource::Base;
|
||||
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<KeyBinding>(cx))
|
||||
.child(Story::label("Single Key", cx))
|
||||
.child(KeyBinding::new_from_gpui(binding("Z"), cx))
|
||||
.child(KeyBinding::from_keystrokes(
|
||||
binding("Z").keystrokes().into(),
|
||||
SOURCE,
|
||||
))
|
||||
.child(Story::label("Single Key with Modifier", cx))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.child(KeyBinding::new_from_gpui(binding("ctrl-c"), cx))
|
||||
.child(KeyBinding::new_from_gpui(binding("alt-c"), cx))
|
||||
.child(KeyBinding::new_from_gpui(binding("cmd-c"), cx))
|
||||
.child(KeyBinding::new_from_gpui(binding("shift-c"), cx)),
|
||||
.child(KeyBinding::from_keystrokes(
|
||||
binding("ctrl-c").keystrokes().into(),
|
||||
SOURCE,
|
||||
))
|
||||
.child(KeyBinding::from_keystrokes(
|
||||
binding("alt-c").keystrokes().into(),
|
||||
SOURCE,
|
||||
))
|
||||
.child(KeyBinding::from_keystrokes(
|
||||
binding("cmd-c").keystrokes().into(),
|
||||
SOURCE,
|
||||
))
|
||||
.child(KeyBinding::from_keystrokes(
|
||||
binding("shift-c").keystrokes().into(),
|
||||
SOURCE,
|
||||
)),
|
||||
)
|
||||
.child(Story::label("Single Key with Modifier (Permuted)", cx))
|
||||
.child(
|
||||
@@ -41,58 +59,77 @@ impl Render for KeybindingStory {
|
||||
.gap_4()
|
||||
.py_3()
|
||||
.children(chunk.map(|permutation| {
|
||||
KeyBinding::new_from_gpui(
|
||||
binding(&(permutation.join("-") + "-x")),
|
||||
cx,
|
||||
KeyBinding::from_keystrokes(
|
||||
binding(&(permutation.join("-") + "-x"))
|
||||
.keystrokes()
|
||||
.into(),
|
||||
SOURCE,
|
||||
)
|
||||
}))
|
||||
}),
|
||||
),
|
||||
)
|
||||
.child(Story::label("Single Key with All Modifiers", cx))
|
||||
.child(KeyBinding::new_from_gpui(
|
||||
binding("ctrl-alt-cmd-shift-z"),
|
||||
cx,
|
||||
.child(KeyBinding::from_keystrokes(
|
||||
binding("ctrl-alt-cmd-shift-z").keystrokes().into(),
|
||||
SOURCE,
|
||||
))
|
||||
.child(Story::label("Chord", cx))
|
||||
.child(KeyBinding::new_from_gpui(binding("a z"), cx))
|
||||
.child(KeyBinding::from_keystrokes(
|
||||
binding("a z").keystrokes().into(),
|
||||
SOURCE,
|
||||
))
|
||||
.child(Story::label("Chord with Modifier", cx))
|
||||
.child(KeyBinding::new_from_gpui(binding("ctrl-a shift-z"), cx))
|
||||
.child(KeyBinding::new_from_gpui(binding("fn-s"), cx))
|
||||
.child(KeyBinding::from_keystrokes(
|
||||
binding("ctrl-a shift-z").keystrokes().into(),
|
||||
SOURCE,
|
||||
))
|
||||
.child(KeyBinding::from_keystrokes(
|
||||
binding("fn-s").keystrokes().into(),
|
||||
SOURCE,
|
||||
))
|
||||
.child(Story::label("Single Key with All Modifiers (Linux)", cx))
|
||||
.child(
|
||||
KeyBinding::new_from_gpui(binding("ctrl-alt-cmd-shift-z"), cx)
|
||||
.platform_style(PlatformStyle::Linux),
|
||||
KeyBinding::from_keystrokes(
|
||||
binding("ctrl-alt-cmd-shift-z").keystrokes().into(),
|
||||
SOURCE,
|
||||
)
|
||||
.platform_style(PlatformStyle::Linux),
|
||||
)
|
||||
.child(Story::label("Chord (Linux)", cx))
|
||||
.child(
|
||||
KeyBinding::new_from_gpui(binding("a z"), cx).platform_style(PlatformStyle::Linux),
|
||||
KeyBinding::from_keystrokes(binding("a z").keystrokes().into(), SOURCE)
|
||||
.platform_style(PlatformStyle::Linux),
|
||||
)
|
||||
.child(Story::label("Chord with Modifier (Linux)", cx))
|
||||
.child(
|
||||
KeyBinding::new_from_gpui(binding("ctrl-a shift-z"), cx)
|
||||
KeyBinding::from_keystrokes(binding("ctrl-a shift-z").keystrokes().into(), SOURCE)
|
||||
.platform_style(PlatformStyle::Linux),
|
||||
)
|
||||
.child(
|
||||
KeyBinding::new_from_gpui(binding("fn-s"), cx).platform_style(PlatformStyle::Linux),
|
||||
KeyBinding::from_keystrokes(binding("fn-s").keystrokes().into(), SOURCE)
|
||||
.platform_style(PlatformStyle::Linux),
|
||||
)
|
||||
.child(Story::label("Single Key with All Modifiers (Windows)", cx))
|
||||
.child(
|
||||
KeyBinding::new_from_gpui(binding("ctrl-alt-cmd-shift-z"), cx)
|
||||
.platform_style(PlatformStyle::Windows),
|
||||
KeyBinding::from_keystrokes(
|
||||
binding("ctrl-alt-cmd-shift-z").keystrokes().into(),
|
||||
SOURCE,
|
||||
)
|
||||
.platform_style(PlatformStyle::Windows),
|
||||
)
|
||||
.child(Story::label("Chord (Windows)", cx))
|
||||
.child(
|
||||
KeyBinding::new_from_gpui(binding("a z"), cx)
|
||||
KeyBinding::from_keystrokes(binding("a z").keystrokes().into(), SOURCE)
|
||||
.platform_style(PlatformStyle::Windows),
|
||||
)
|
||||
.child(Story::label("Chord with Modifier (Windows)", cx))
|
||||
.child(
|
||||
KeyBinding::new_from_gpui(binding("ctrl-a shift-z"), cx)
|
||||
KeyBinding::from_keystrokes(binding("ctrl-a shift-z").keystrokes().into(), SOURCE)
|
||||
.platform_style(PlatformStyle::Windows),
|
||||
)
|
||||
.child(
|
||||
KeyBinding::new_from_gpui(binding("fn-s"), cx)
|
||||
KeyBinding::from_keystrokes(binding("fn-s").keystrokes().into(), SOURCE)
|
||||
.platform_style(PlatformStyle::Windows),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,11 +64,11 @@ impl Tooltip {
|
||||
) -> impl Fn(&mut Window, &mut App) -> AnyView + use<T> {
|
||||
let title = title.into();
|
||||
let action = action.boxed_clone();
|
||||
move |window, cx| {
|
||||
move |_, cx| {
|
||||
cx.new(|cx| Self {
|
||||
title: Title::Str(title.clone()),
|
||||
meta: None,
|
||||
key_binding: KeyBinding::for_action(action.as_ref(), window, cx),
|
||||
key_binding: Some(KeyBinding::for_action(action.as_ref(), cx)),
|
||||
})
|
||||
.into()
|
||||
}
|
||||
@@ -82,11 +82,15 @@ impl Tooltip {
|
||||
let title = title.into();
|
||||
let action = action.boxed_clone();
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
move |_, cx| {
|
||||
cx.new(|cx| Self {
|
||||
title: Title::Str(title.clone()),
|
||||
meta: None,
|
||||
key_binding: KeyBinding::for_action_in(action.as_ref(), &focus_handle, window, cx),
|
||||
key_binding: Some(KeyBinding::for_action_in(
|
||||
action.as_ref(),
|
||||
&focus_handle,
|
||||
cx,
|
||||
)),
|
||||
})
|
||||
.into()
|
||||
}
|
||||
@@ -95,13 +99,12 @@ impl Tooltip {
|
||||
pub fn for_action(
|
||||
title: impl Into<SharedString>,
|
||||
action: &dyn Action,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| Self {
|
||||
title: Title::Str(title.into()),
|
||||
meta: None,
|
||||
key_binding: KeyBinding::for_action(action, window, cx),
|
||||
key_binding: Some(KeyBinding::for_action(action, cx)),
|
||||
})
|
||||
.into()
|
||||
}
|
||||
@@ -110,13 +113,12 @@ impl Tooltip {
|
||||
title: impl Into<SharedString>,
|
||||
action: &dyn Action,
|
||||
focus_handle: &FocusHandle,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| Self {
|
||||
title: title.into().into(),
|
||||
meta: None,
|
||||
key_binding: KeyBinding::for_action_in(action, focus_handle, window, cx),
|
||||
key_binding: Some(KeyBinding::for_action_in(action, focus_handle, cx)),
|
||||
})
|
||||
.into()
|
||||
}
|
||||
@@ -125,13 +127,12 @@ impl Tooltip {
|
||||
title: impl Into<SharedString>,
|
||||
action: Option<&dyn Action>,
|
||||
meta: impl Into<SharedString>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| Self {
|
||||
title: title.into().into(),
|
||||
meta: Some(meta.into()),
|
||||
key_binding: action.and_then(|action| KeyBinding::for_action(action, window, cx)),
|
||||
key_binding: action.map(|action| KeyBinding::for_action(action, cx)),
|
||||
})
|
||||
.into()
|
||||
}
|
||||
@@ -141,14 +142,12 @@ impl Tooltip {
|
||||
action: Option<&dyn Action>,
|
||||
meta: impl Into<SharedString>,
|
||||
focus_handle: &FocusHandle,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| Self {
|
||||
title: title.into().into(),
|
||||
meta: Some(meta.into()),
|
||||
key_binding: action
|
||||
.and_then(|action| KeyBinding::for_action_in(action, focus_handle, window, cx)),
|
||||
key_binding: action.map(|action| KeyBinding::for_action_in(action, focus_handle, cx)),
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user