Refactor KeybindingKeystroke (#37065)

This pull request refactors the `KeybindingKeystroke` struct and related
code to improve platform abstraction. The changes centralize
platform-specific logic within `KeybindingKeystroke` and update its
usage throughout the codebase, making the API more consistent and less
error-prone.



Release Notes:

- N/A
This commit is contained in:
张小白
2025-08-28 08:40:43 +00:00
committed by GitHub
parent 73b38c8306
commit ff03dda90a
11 changed files with 183 additions and 123 deletions
+15 -35
View File
@@ -14,9 +14,9 @@ use gpui::{
Action, AppContext as _, AsyncApp, Axis, ClickEvent, Context, DismissEvent, Entity,
EventEmitter, FocusHandle, Focusable, Global, IsZero,
KeyBindingContextPredicate::{And, Descendant, Equal, Identifier, Not, NotEqual, Or},
KeyContext, KeybindingKeystroke, Keystroke, MouseButton, PlatformKeyboardMapper, Point,
ScrollStrategy, ScrollWheelEvent, Stateful, StyledText, Subscription, Task,
TextStyleRefinement, WeakEntity, actions, anchored, deferred, div,
KeyContext, KeybindingKeystroke, MouseButton, PlatformKeyboardMapper, Point, ScrollStrategy,
ScrollWheelEvent, Stateful, StyledText, Subscription, Task, TextStyleRefinement, WeakEntity,
actions, anchored, deferred, div,
};
use language::{Language, LanguageConfig, ToOffset as _};
use notifications::status_toast::{StatusToast, ToastIcon};
@@ -420,7 +420,7 @@ fn keystrokes_match_exactly(
) -> bool {
keystrokes1.len() == keystrokes2.len()
&& keystrokes1.iter().zip(keystrokes2).all(|(k1, k2)| {
k1.inner.key == k2.inner.key && k1.inner.modifiers == k2.inner.modifiers
k1.inner().key == k2.inner().key && k1.inner().modifiers == k2.inner().modifiers
})
}
@@ -532,7 +532,7 @@ impl KeymapEditor {
let keystroke_query = keystroke_query
.into_iter()
.map(|keystroke| keystroke.inner.unparse())
.map(|keystroke| keystroke.inner().unparse())
.collect::<Vec<String>>()
.join(" ");
@@ -606,13 +606,13 @@ impl KeymapEditor {
let query = &keystroke_query[query_cursor];
let keystroke = &keystrokes[keystroke_cursor];
let matches = query
.inner
.inner()
.modifiers
.is_subset_of(&keystroke.inner.modifiers)
&& ((query.inner.key.is_empty()
|| query.inner.key == keystroke.inner.key)
&& query.inner.key_char.as_ref().is_none_or(
|q_kc| q_kc == &keystroke.inner.key,
.is_subset_of(&keystroke.inner().modifiers)
&& ((query.inner().key.is_empty()
|| query.inner().key == keystroke.inner().key)
&& query.inner().key_char.as_ref().is_none_or(
|q_kc| q_kc == &keystroke.inner().key,
));
if matches {
found_count += 1;
@@ -2256,12 +2256,10 @@ impl KeybindingEditorModal {
let fs = self.fs.clone();
let tab_size = cx.global::<settings::SettingsStore>().json_tab_size();
let new_keystrokes = self
.validate_keystrokes(cx)
.map_err(InputError::error)?
.into_iter()
.map(remove_key_char)
.collect::<Vec<_>>();
let mut new_keystrokes = self.validate_keystrokes(cx).map_err(InputError::error)?;
new_keystrokes
.iter_mut()
.for_each(|ks| ks.remove_key_char());
let new_context = self.validate_context(cx).map_err(InputError::error)?;
let new_action_args = self
@@ -2454,24 +2452,6 @@ impl KeybindingEditorModal {
}
}
fn remove_key_char(
KeybindingKeystroke {
inner,
display_modifiers,
display_key,
}: KeybindingKeystroke,
) -> KeybindingKeystroke {
KeybindingKeystroke {
inner: Keystroke {
modifiers: inner.modifiers,
key: inner.key,
key_char: None,
},
display_modifiers,
display_key,
}
}
impl Render for KeybindingEditorModal {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme().colors();
@@ -116,7 +116,7 @@ impl KeystrokeInput {
&& self
.keystrokes
.last()
.is_some_and(|last| last.display_key.is_empty())
.is_some_and(|last| last.key().is_empty())
{
return &self.keystrokes[..self.keystrokes.len() - 1];
}
@@ -124,15 +124,11 @@ impl KeystrokeInput {
}
fn dummy(modifiers: Modifiers) -> KeybindingKeystroke {
KeybindingKeystroke {
inner: Keystroke {
modifiers,
key: "".to_string(),
key_char: None,
},
display_modifiers: modifiers,
display_key: "".to_string(),
}
KeybindingKeystroke::from_keystroke(Keystroke {
modifiers,
key: "".to_string(),
key_char: None,
})
}
fn keystrokes_changed(&self, cx: &mut Context<Self>) {
@@ -258,7 +254,7 @@ impl KeystrokeInput {
self.keystrokes_changed(cx);
if let Some(last) = self.keystrokes.last_mut()
&& last.display_key.is_empty()
&& last.key().is_empty()
&& keystrokes_len <= Self::KEYSTROKE_COUNT_MAX
{
if !self.search && !event.modifiers.modified() {
@@ -267,15 +263,14 @@ impl KeystrokeInput {
}
if self.search {
if self.previous_modifiers.modified() {
last.display_modifiers |= event.modifiers;
last.inner.modifiers |= event.modifiers;
let modifiers = *last.modifiers() | event.modifiers;
last.set_modifiers(modifiers);
} else {
self.keystrokes.push(Self::dummy(event.modifiers));
}
self.previous_modifiers |= event.modifiers;
} else {
last.display_modifiers = event.modifiers;
last.inner.modifiers = event.modifiers;
last.set_modifiers(event.modifiers);
return;
}
} else if keystrokes_len < Self::KEYSTROKE_COUNT_MAX {
@@ -303,10 +298,13 @@ impl KeystrokeInput {
return;
}
let keystroke =
KeybindingKeystroke::new(keystroke.clone(), false, cx.keyboard_mapper().as_ref());
let keystroke = KeybindingKeystroke::new_with_mapper(
keystroke.clone(),
false,
cx.keyboard_mapper().as_ref(),
);
if let Some(last) = self.keystrokes.last()
&& last.display_key.is_empty()
&& last.key().is_empty()
&& (!self.search || self.previous_modifiers.modified())
{
self.keystrokes.pop();
@@ -825,7 +823,7 @@ mod tests {
input
.keystrokes
.iter()
.map(|keystroke| keystroke.inner.clone())
.map(|keystroke| keystroke.inner().clone())
.collect()
});
Self::expect_keystrokes_equal(&actual, expected);
@@ -1094,7 +1092,7 @@ mod tests {
}
fn keystrokes_str(ks: &[KeybindingKeystroke]) -> String {
ks.iter().map(|ks| ks.inner.unparse()).join(" ")
ks.iter().map(|ks| ks.inner().unparse()).join(" ")
}
}
}