gpui: Prevent multi-modifier gestures from dispatching standalone modifiers (#62036)
Resolves #61888. Multi-modifier gestures could leave GPUI’s standalone-modifier state armed after one modifier was released. For example, releasing `Alt` before `Shift` during an `Alt+Shift` gesture caused the final `Shift` release to be synthesized as a standalone shift keystroke. This diff invalidates standalone-modifier synthesis whenever multiple modifiers are active, while preserving genuine modifier-only key bindings. Release Notes: - Fixed Alt+Shift gestures potentially triggering standalone Shift key bindings.
This commit is contained in:
2 files changed
+35
-5
No files matched your search
@@ -835,7 +835,7 @@ mod test {
|
||||
|
||||
use crate::{
|
||||
self as gpui, AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement,
|
||||
KeyBinding, Keystroke, ParentElement, Render, TestAppContext, Window, div,
|
||||
KeyBinding, Keystroke, Modifiers, ParentElement, Render, TestAppContext, Window, div,
|
||||
};
|
||||
|
||||
struct TestView {
|
||||
@@ -902,4 +902,32 @@ mod test {
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_multi_modifier_gesture_does_not_dispatch_standalone_modifier_binding(
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
let (test_view, cx) = cx.add_window_view(|_, cx| TestView {
|
||||
saw_key_down: false,
|
||||
saw_action: false,
|
||||
focus_handle: cx.focus_handle(),
|
||||
});
|
||||
|
||||
cx.update(|_, cx| {
|
||||
cx.bind_keys(vec![KeyBinding::new("shift", TestAction, None)]);
|
||||
});
|
||||
test_view.update_in(cx, |test_view, window, cx| {
|
||||
window.focus(&test_view.focus_handle, cx);
|
||||
});
|
||||
|
||||
cx.simulate_modifiers_change(Modifiers::alt());
|
||||
cx.simulate_modifiers_change(Modifiers::alt() | Modifiers::shift());
|
||||
cx.simulate_modifiers_change(Modifiers::shift());
|
||||
cx.simulate_modifiers_change(Modifiers::none());
|
||||
assert!(!test_view.read_with(cx, |test_view, _| test_view.saw_action));
|
||||
|
||||
cx.simulate_modifiers_change(Modifiers::shift());
|
||||
cx.simulate_modifiers_change(Modifiers::none());
|
||||
assert!(test_view.read_with(cx, |test_view, _| test_view.saw_action));
|
||||
}
|
||||
}
|
||||
@@ -1149,7 +1149,7 @@ pub struct Window {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct ModifierState {
|
||||
modifiers: Modifiers,
|
||||
saw_keystroke: bool,
|
||||
saw_other_input: bool,
|
||||
}
|
||||
|
||||
/// Tracks input event timestamps to determine if input is arriving at a high rate.
|
||||
@@ -5167,7 +5167,7 @@ impl Window {
|
||||
if let Some(event) = event.downcast_ref::<ModifiersChangedEvent>() {
|
||||
if event.modifiers.number_of_modifiers() == 0
|
||||
&& self.pending_modifier.modifiers.number_of_modifiers() == 1
|
||||
&& !self.pending_modifier.saw_keystroke
|
||||
&& !self.pending_modifier.saw_other_input
|
||||
{
|
||||
let key = match self.pending_modifier.modifiers {
|
||||
modifiers if modifiers.shift => Some("shift"),
|
||||
@@ -5189,11 +5189,13 @@ impl Window {
|
||||
if self.pending_modifier.modifiers.number_of_modifiers() == 0
|
||||
&& event.modifiers.number_of_modifiers() == 1
|
||||
{
|
||||
self.pending_modifier.saw_keystroke = false
|
||||
self.pending_modifier.saw_other_input = false
|
||||
} else if event.modifiers.number_of_modifiers() > 1 {
|
||||
self.pending_modifier.saw_other_input = true
|
||||
}
|
||||
self.pending_modifier.modifiers = event.modifiers
|
||||
} else if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
|
||||
self.pending_modifier.saw_keystroke = true;
|
||||
self.pending_modifier.saw_other_input = true;
|
||||
keystroke = Some(key_down_event.keystroke.clone());
|
||||
if key_down_event.keystroke.key_char.is_some()
|
||||
&& matches!(
|
||||
|
||||
Reference in new issue
Block a user