From d61696ff3ad492c3631f2999c8defb422933b3e0 Mon Sep 17 00:00:00 2001 From: Kulratan Thapar Date: Wed, 18 Feb 2026 10:05:03 +0000 Subject: [PATCH 1/7] Double-Tap --- .../input_mapper/input_mapper_message.rs | 2 + .../input_mapper_message_handler.rs | 3 +- .../messages/input_mapper/input_mappings.rs | 9 ++- .../input_mapper/utility_types/macros.rs | 4 +- .../input_mapper/utility_types/misc.rs | 2 + .../input_preprocessor_message_handler.rs | 62 +++++++++++++++++++ 6 files changed, 78 insertions(+), 4 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mapper_message.rs b/editor/src/messages/input_mapper/input_mapper_message.rs index bfcda26c73..2f9a3d2ac2 100644 --- a/editor/src/messages/input_mapper/input_mapper_message.rs +++ b/editor/src/messages/input_mapper/input_mapper_message.rs @@ -16,6 +16,8 @@ pub enum InputMapperMessage { KeyUpNoRepeat(Key), #[child] DoubleClick(MouseButton), + #[child] + DoubleTap(Key), // Messages PointerMove, diff --git a/editor/src/messages/input_mapper/input_mapper_message_handler.rs b/editor/src/messages/input_mapper/input_mapper_message_handler.rs index d38845c6d9..50744c5820 100644 --- a/editor/src/messages/input_mapper/input_mapper_message_handler.rs +++ b/editor/src/messages/input_mapper/input_mapper_message_handler.rs @@ -40,6 +40,7 @@ impl InputMapperMessageHandler { .chain(self.mapping.key_up_no_repeat.iter()) .chain(self.mapping.key_down_no_repeat.iter()) .chain(self.mapping.double_click.iter()) + .chain(self.mapping.double_tap.iter()) .chain(std::iter::once(&self.mapping.wheel_scroll)) .chain(std::iter::once(&self.mapping.pointer_move)); let all_mapping_entries = all_key_mapping_entries.flat_map(|entry| entry.0.iter()); @@ -68,7 +69,7 @@ impl InputMapperMessageHandler { // Append the key button for the entry use InputMapperMessage as IMM; match entry.input { - IMM::KeyDown(key) | IMM::KeyUp(key) | IMM::KeyDownNoRepeat(key) | IMM::KeyUpNoRepeat(key) => keys.push(key), + IMM::KeyDown(key) | IMM::KeyUp(key) | IMM::KeyDownNoRepeat(key) | IMM::KeyUpNoRepeat(key) | IMM::DoubleTap(key) => keys.push(key), _ => (), } diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 644b8d5a7b..135b92773e 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -337,7 +337,7 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { entry!(KeyDown(Tab); action_dispatch=ToolMessage::ToggleSelectVsPath), // // DocumentMessage - entry!(KeyDown(Space); modifiers=[Control], action_dispatch=DocumentMessage::GraphViewOverlayToggle), + entry!(DoubleTap(Space); action_dispatch=DocumentMessage::GraphViewOverlayToggle), entry!(KeyDownNoRepeat(Escape); action_dispatch=DocumentMessage::Escape), entry!(KeyDown(Delete); action_dispatch=DocumentMessage::DeleteSelectedLayers), entry!(KeyDown(Backspace); action_dispatch=DocumentMessage::DeleteSelectedLayers), @@ -463,7 +463,7 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { entry!(KeyDown(Space); modifiers=[Shift], action_dispatch=AnimationMessage::ToggleLivePreview), entry!(KeyDown(Home); modifiers=[Shift], action_dispatch=AnimationMessage::RestartAnimation), ]; - let (mut key_up, mut key_down, mut key_up_no_repeat, mut key_down_no_repeat, mut double_click, mut wheel_scroll, mut pointer_move, mut pointer_shake) = mappings; + let (mut key_up, mut key_down, mut key_up_no_repeat, mut key_down_no_repeat, mut double_click, mut double_tap, mut wheel_scroll, mut pointer_move, mut pointer_shake) = mappings; let sort = |list: &mut KeyMappingEntries| list.0.sort_by(|a, b| b.modifiers.count_ones().cmp(&a.modifiers.count_ones())); // Sort the sublists of `key_up`, `key_down`, `key_up_no_repeat`, and `key_down_no_repeat` @@ -476,6 +476,10 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { for sublist in &mut double_click { sort(sublist) } + // Sort the sublists of `double_tap` + for sublist in &mut double_tap { + sort(sublist) + } // Sort `wheel_scroll` sort(&mut wheel_scroll); // Sort `pointer_move` @@ -489,6 +493,7 @@ pub fn input_mappings(zoom_with_scroll: bool) -> Mapping { key_up_no_repeat, key_down_no_repeat, double_click, + double_tap, wheel_scroll, pointer_move, pointer_shake, diff --git a/editor/src/messages/input_mapper/utility_types/macros.rs b/editor/src/messages/input_mapper/utility_types/macros.rs index 9dae86c9a5..c7a16e2772 100644 --- a/editor/src/messages/input_mapper/utility_types/macros.rs +++ b/editor/src/messages/input_mapper/utility_types/macros.rs @@ -119,6 +119,7 @@ macro_rules! mapping { let mut key_up_no_repeat = KeyMappingEntries::key_array(); let mut key_down_no_repeat = KeyMappingEntries::key_array(); let mut double_click = KeyMappingEntries::mouse_buttons_arrays(); + let mut double_tap = KeyMappingEntries::key_array(); let mut wheel_scroll = KeyMappingEntries::new(); let mut pointer_move = KeyMappingEntries::new(); let mut pointer_shake = KeyMappingEntries::new(); @@ -138,6 +139,7 @@ macro_rules! mapping { InputMapperMessage::KeyDownNoRepeat(key) => &mut key_down_no_repeat[key as usize], InputMapperMessage::KeyUpNoRepeat(key) => &mut key_up_no_repeat[key as usize], InputMapperMessage::DoubleClick(key) => &mut double_click[key as usize], + InputMapperMessage::DoubleTap(key) => &mut double_tap[key as usize], InputMapperMessage::WheelScroll => &mut wheel_scroll, InputMapperMessage::PointerMove => &mut pointer_move, InputMapperMessage::PointerShake => &mut pointer_shake, @@ -148,7 +150,7 @@ macro_rules! mapping { } )* - (key_up, key_down, key_up_no_repeat, key_down_no_repeat, double_click, wheel_scroll, pointer_move, pointer_shake) + (key_up, key_down, key_up_no_repeat, key_down_no_repeat, double_click, double_tap, wheel_scroll, pointer_move, pointer_shake) }}; } diff --git a/editor/src/messages/input_mapper/utility_types/misc.rs b/editor/src/messages/input_mapper/utility_types/misc.rs index 2470e7a719..98d103b323 100644 --- a/editor/src/messages/input_mapper/utility_types/misc.rs +++ b/editor/src/messages/input_mapper/utility_types/misc.rs @@ -12,6 +12,7 @@ pub struct Mapping { pub key_up_no_repeat: [KeyMappingEntries; NUMBER_OF_KEYS], pub key_down_no_repeat: [KeyMappingEntries; NUMBER_OF_KEYS], pub double_click: [KeyMappingEntries; NUMBER_OF_MOUSE_BUTTONS], + pub double_tap: [KeyMappingEntries; NUMBER_OF_KEYS], pub wheel_scroll: KeyMappingEntries, pub pointer_move: KeyMappingEntries, pub pointer_shake: KeyMappingEntries, @@ -36,6 +37,7 @@ impl Mapping { InputMapperMessage::KeyDownNoRepeat(key) => &self.key_down_no_repeat[*key as usize], InputMapperMessage::KeyUpNoRepeat(key) => &self.key_up_no_repeat[*key as usize], InputMapperMessage::DoubleClick(key) => &self.double_click[*key as usize], + InputMapperMessage::DoubleTap(key) => &self.double_tap[*key as usize], InputMapperMessage::WheelScroll => &self.wheel_scroll, InputMapperMessage::PointerMove => &self.pointer_move, InputMapperMessage::PointerShake => &self.pointer_shake, diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index e9f5847b7b..b390659cda 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -1,4 +1,5 @@ use crate::application::Editor; +use crate::consts::DOUBLE_CLICK_MILLISECONDS; use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeyStates, ModifierKeys}; use crate::messages::input_mapper::utility_types::input_mouse::{MouseButton, MouseKeys, MouseState}; use crate::messages::input_mapper::utility_types::misc::FrameTimeInfo; @@ -16,6 +17,7 @@ pub struct InputPreprocessorMessageHandler { pub time: u64, pub keyboard: KeyStates, pub mouse: MouseState, + pub last_key_down: Option<(Key, u64)>, // (Key, timestamp) } #[message_handler_data] @@ -44,7 +46,18 @@ impl<'a> MessageHandler { self.update_states_of_modifier_keys(modifier_keys, responses); self.keyboard.set(key as usize); + if !key_repeat { + if let Some((last_key, last_time)) = self.last_key_down { + if last_key == key && self.time.saturating_sub(last_time) < DOUBLE_CLICK_MILLISECONDS { + responses.add(InputMapperMessage::DoubleTap(key)); + self.last_key_down = None; + } else { + self.last_key_down = Some((key, self.time)); + } + } else { + self.last_key_down = Some((key, self.time)); + } responses.add(InputMapperMessage::KeyDownNoRepeat(key)); } responses.add(InputMapperMessage::KeyDown(key)); @@ -185,6 +198,7 @@ impl InputPreprocessorMessageHandler { #[cfg(test)] mod test { + use crate::consts::DOUBLE_CLICK_MILLISECONDS; use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys}; use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta}; use crate::messages::prelude::*; @@ -292,4 +306,52 @@ mod test { assert!(responses.contains(&InputMapperMessage::KeyDown(Key::Control).into())); assert!(responses.contains(&InputMapperMessage::KeyDown(Key::Control).into())); } + + fn key_down(input_preprocessor: &mut InputPreprocessorMessageHandler, key: Key, responses: &mut VecDeque) { + input_preprocessor.process_message( + InputPreprocessorMessage::KeyDown { + key, + key_repeat: false, + modifier_keys: ModifierKeys::empty(), + }, + responses, + InputPreprocessorMessageContext { + viewport: &ViewportMessageHandler::default(), + }, + ); + } + + #[test] + fn process_double_tap_within_threshold() { + let mut input_preprocessor = InputPreprocessorMessageHandler::default(); + let mut responses = VecDeque::new(); + + // First tap at time 0 + key_down(&mut input_preprocessor, Key::Space, &mut responses); + responses.clear(); + + // Second tap within threshold + input_preprocessor.time = 50; + key_down(&mut input_preprocessor, Key::Space, &mut responses); + + assert!(responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); + assert!(input_preprocessor.last_key_down.is_none()); + } + + #[test] + fn process_double_tap_outside_threshold() { + let mut input_preprocessor = InputPreprocessorMessageHandler::default(); + let mut responses = VecDeque::new(); + + // First tap at time 0 + key_down(&mut input_preprocessor, Key::Space, &mut responses); + responses.clear(); + + // Second tap outside threshold + input_preprocessor.time = DOUBLE_CLICK_MILLISECONDS + 1; + key_down(&mut input_preprocessor, Key::Space, &mut responses); + + assert!(!responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); + assert_eq!(input_preprocessor.last_key_down, Some((Key::Space, DOUBLE_CLICK_MILLISECONDS + 1))); + } } From ca5fc2812cd55b1e99f7cb6d566eefffb2d407c4 Mon Sep 17 00:00:00 2001 From: Kulratan Thapar Date: Wed, 18 Feb 2026 10:19:37 +0000 Subject: [PATCH 2/7] improvement --- .../input_preprocessor_message_handler.rs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index b390659cda..c036ff7a29 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -48,19 +48,23 @@ impl<'a> MessageHandler { self.update_states_of_modifier_keys(modifier_keys, responses); @@ -335,6 +339,8 @@ mod test { key_down(&mut input_preprocessor, Key::Space, &mut responses); assert!(responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); + assert!(!responses.contains(&InputMapperMessage::KeyDown(Key::Space).into())); + assert!(!responses.contains(&InputMapperMessage::KeyDownNoRepeat(Key::Space).into())); assert!(input_preprocessor.last_key_down.is_none()); } From 99ec5da06799b22ed71b7aae6cf3574c3c7a2c7f Mon Sep 17 00:00:00 2001 From: Kulratan Thapar Date: Wed, 18 Feb 2026 10:28:52 +0000 Subject: [PATCH 3/7] Fixed --- .../input_preprocessor_message_handler.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index c036ff7a29..3a3d0e861f 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -59,12 +59,11 @@ impl<'a> MessageHandler { self.update_states_of_modifier_keys(modifier_keys, responses); @@ -339,8 +338,8 @@ mod test { key_down(&mut input_preprocessor, Key::Space, &mut responses); assert!(responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); - assert!(!responses.contains(&InputMapperMessage::KeyDown(Key::Space).into())); - assert!(!responses.contains(&InputMapperMessage::KeyDownNoRepeat(Key::Space).into())); + assert!(responses.contains(&InputMapperMessage::KeyDown(Key::Space).into())); + assert!(responses.contains(&InputMapperMessage::KeyDownNoRepeat(Key::Space).into())); assert!(input_preprocessor.last_key_down.is_none()); } From f2af3e51bae97feefc683b07e07dc999d0b8e41a Mon Sep 17 00:00:00 2001 From: Kulratan Thapar Date: Wed, 18 Feb 2026 11:36:47 +0000 Subject: [PATCH 4/7] Test --- .../input_preprocessor/input_preprocessor_message_handler.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index 3a3d0e861f..192f6a702a 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -357,6 +357,8 @@ mod test { key_down(&mut input_preprocessor, Key::Space, &mut responses); assert!(!responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); + assert!(responses.contains(&InputMapperMessage::KeyDown(Key::Space).into())); + assert!(responses.contains(&InputMapperMessage::KeyDownNoRepeat(Key::Space).into())); assert_eq!(input_preprocessor.last_key_down, Some((Key::Space, DOUBLE_CLICK_MILLISECONDS + 1))); } } From cfca75151a6d6f396aee7f388c57af4806b76cf7 Mon Sep 17 00:00:00 2001 From: Kulratan Thapar Date: Sun, 22 Feb 2026 06:41:34 +0000 Subject: [PATCH 5/7] Update --- .../input_preprocessor_message_handler.rs | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index 192f6a702a..c7dece889a 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -18,6 +18,7 @@ pub struct InputPreprocessorMessageHandler { pub keyboard: KeyStates, pub mouse: MouseState, pub last_key_down: Option<(Key, u64)>, // (Key, timestamp) + pub double_tap_key: Option, } #[message_handler_data] @@ -48,14 +49,12 @@ impl<'a> MessageHandler MessageHandler { @@ -324,6 +327,20 @@ mod test { ); } + fn key_up(input_preprocessor: &mut InputPreprocessorMessageHandler, key: Key, responses: &mut VecDeque) { + input_preprocessor.process_message( + InputPreprocessorMessage::KeyUp { + key, + key_repeat: false, + modifier_keys: ModifierKeys::empty(), + }, + responses, + InputPreprocessorMessageContext { + viewport: &ViewportMessageHandler::default(), + }, + ); + } + #[test] fn process_double_tap_within_threshold() { let mut input_preprocessor = InputPreprocessorMessageHandler::default(); @@ -331,16 +348,25 @@ mod test { // First tap at time 0 key_down(&mut input_preprocessor, Key::Space, &mut responses); + key_up(&mut input_preprocessor, Key::Space, &mut responses); responses.clear(); // Second tap within threshold input_preprocessor.time = 50; key_down(&mut input_preprocessor, Key::Space, &mut responses); - assert!(responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); + assert!(!responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); assert!(responses.contains(&InputMapperMessage::KeyDown(Key::Space).into())); assert!(responses.contains(&InputMapperMessage::KeyDownNoRepeat(Key::Space).into())); assert!(input_preprocessor.last_key_down.is_none()); + assert_eq!(input_preprocessor.double_tap_key, Some(Key::Space)); + + responses.clear(); + key_up(&mut input_preprocessor, Key::Space, &mut responses); + + assert!(responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); + assert!(input_preprocessor.last_key_down.is_none()); + assert!(input_preprocessor.double_tap_key.is_none()); } #[test] @@ -350,6 +376,7 @@ mod test { // First tap at time 0 key_down(&mut input_preprocessor, Key::Space, &mut responses); + key_up(&mut input_preprocessor, Key::Space, &mut responses); responses.clear(); // Second tap outside threshold @@ -360,5 +387,11 @@ mod test { assert!(responses.contains(&InputMapperMessage::KeyDown(Key::Space).into())); assert!(responses.contains(&InputMapperMessage::KeyDownNoRepeat(Key::Space).into())); assert_eq!(input_preprocessor.last_key_down, Some((Key::Space, DOUBLE_CLICK_MILLISECONDS + 1))); + assert!(input_preprocessor.double_tap_key.is_none()); + + responses.clear(); + key_up(&mut input_preprocessor, Key::Space, &mut responses); + + assert!(!responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); } } From 300914734eb1981bce24a5b5eb56dc51eaa1b262 Mon Sep 17 00:00:00 2001 From: Kulratan Thapar Date: Sun, 22 Feb 2026 07:59:08 +0000 Subject: [PATCH 6/7] Fix --- .../input_preprocessor_message_handler.rs | 73 +++++++++++++++++-- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index c7dece889a..64319b9455 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -18,7 +18,7 @@ pub struct InputPreprocessorMessageHandler { pub keyboard: KeyStates, pub mouse: MouseState, pub last_key_down: Option<(Key, u64)>, // (Key, timestamp) - pub double_tap_key: Option, + pub double_tap_key: Option<(Key, u64)>, } #[message_handler_data] @@ -28,6 +28,7 @@ impl<'a> MessageHandler { + self.clear_double_tap_state(); self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); @@ -54,10 +55,11 @@ impl<'a> MessageHandler MessageHandler { + self.clear_double_tap_state(); self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); @@ -96,6 +101,7 @@ impl<'a> MessageHandler { + self.clear_double_tap_state(); self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); @@ -117,6 +123,7 @@ impl<'a> MessageHandler { + self.clear_double_tap_state(); self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); @@ -135,6 +142,11 @@ impl<'a> MessageHandler) { let click_mappings = [ (MouseKeys::LEFT, Key::MouseLeft), @@ -359,7 +371,7 @@ mod test { assert!(responses.contains(&InputMapperMessage::KeyDown(Key::Space).into())); assert!(responses.contains(&InputMapperMessage::KeyDownNoRepeat(Key::Space).into())); assert!(input_preprocessor.last_key_down.is_none()); - assert_eq!(input_preprocessor.double_tap_key, Some(Key::Space)); + assert_eq!(input_preprocessor.double_tap_key, Some((Key::Space, 50))); responses.clear(); key_up(&mut input_preprocessor, Key::Space, &mut responses); @@ -394,4 +406,55 @@ mod test { assert!(!responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); } + + #[test] + fn process_double_tap_held_too_long() { + let mut input_preprocessor = InputPreprocessorMessageHandler::default(); + let mut responses = VecDeque::new(); + + key_down(&mut input_preprocessor, Key::Space, &mut responses); + key_up(&mut input_preprocessor, Key::Space, &mut responses); + responses.clear(); + + input_preprocessor.time = 50; + key_down(&mut input_preprocessor, Key::Space, &mut responses); + responses.clear(); + + // Release after the threshold + input_preprocessor.time = 50 + DOUBLE_CLICK_MILLISECONDS + 1; + key_up(&mut input_preprocessor, Key::Space, &mut responses); + + assert!(!responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); + } + + #[test] + fn process_double_tap_interrupted_by_pointer() { + let mut input_preprocessor = InputPreprocessorMessageHandler::default(); + let mut responses = VecDeque::new(); + + key_down(&mut input_preprocessor, Key::Space, &mut responses); + key_up(&mut input_preprocessor, Key::Space, &mut responses); + responses.clear(); + + input_preprocessor.time = 50; + key_down(&mut input_preprocessor, Key::Space, &mut responses); + responses.clear(); + + // PointerDown happens! + input_preprocessor.process_message( + InputPreprocessorMessage::PointerDown { + editor_mouse_state: EditorMouseState::default(), + modifier_keys: ModifierKeys::empty(), + }, + &mut responses, + InputPreprocessorMessageContext { + viewport: &ViewportMessageHandler::default(), + }, + ); + responses.clear(); + + key_up(&mut input_preprocessor, Key::Space, &mut responses); + + assert!(!responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); + } } From e4a6b4617f90006945807004d1aba74d9cb6bef2 Mon Sep 17 00:00:00 2001 From: Kulratan Thapar Date: Sun, 22 Feb 2026 08:27:35 +0000 Subject: [PATCH 7/7] Fix --- .../input_preprocessor_message_handler.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index 64319b9455..7820fa72f0 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -90,6 +90,7 @@ impl<'a> MessageHandler { + self.clear_double_tap_state(); self.update_states_of_modifier_keys(modifier_keys, responses); let mouse_state = editor_mouse_state.to_mouse_state(viewport); @@ -457,4 +458,35 @@ mod test { assert!(!responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); } + + #[test] + fn process_double_tap_interrupted_by_mouse_movement() { + let mut input_preprocessor = InputPreprocessorMessageHandler::default(); + let mut responses = VecDeque::new(); + + key_down(&mut input_preprocessor, Key::Space, &mut responses); + key_up(&mut input_preprocessor, Key::Space, &mut responses); + responses.clear(); + + input_preprocessor.time = 50; + key_down(&mut input_preprocessor, Key::Space, &mut responses); + responses.clear(); + + // PointerMove happens! + input_preprocessor.process_message( + InputPreprocessorMessage::PointerMove { + editor_mouse_state: EditorMouseState::default(), + modifier_keys: ModifierKeys::empty(), + }, + &mut responses, + InputPreprocessorMessageContext { + viewport: &ViewportMessageHandler::default(), + }, + ); + responses.clear(); + + key_up(&mut input_preprocessor, Key::Space, &mut responses); + + assert!(!responses.contains(&InputMapperMessage::DoubleTap(Key::Space).into())); + } }