diff --git a/crates/gpui/src/interactive.rs b/crates/gpui/src/interactive.rs index 218ae5fcdf..57e3f4ac02 100644 --- a/crates/gpui/src/interactive.rs +++ b/crates/gpui/src/interactive.rs @@ -534,6 +534,23 @@ impl InputEvent for FileDropEvent { } impl MouseEvent for FileDropEvent {} +/// A swipe direction on macOS. +#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] +#[allow(missing_docs)] +pub enum SwipeDirection { + Left, + Right, + Up, + Down, +} +/// Recognizing touch pad gesture, such as swipe. +/// For now only swipe is supported. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum TouchPadGestureEvent { + /// A swipe gesture was performed. + Swipe(SwipeDirection), +} + /// An enum corresponding to all kinds of platform input events. #[derive(Clone, Debug)] pub enum PlatformInput { @@ -555,6 +572,8 @@ pub enum PlatformInput { ScrollWheel(ScrollWheelEvent), /// Files were dragged and dropped onto the window. FileDrop(FileDropEvent), + /// Touch pad gesture was performed. + TouchPad(TouchPadGestureEvent), } impl PlatformInput { @@ -569,6 +588,7 @@ impl PlatformInput { PlatformInput::MouseExited(event) => Some(event), PlatformInput::ScrollWheel(event) => Some(event), PlatformInput::FileDrop(event) => Some(event), + PlatformInput::TouchPad(_) => None, } } @@ -583,6 +603,13 @@ impl PlatformInput { PlatformInput::MouseExited(_) => None, PlatformInput::ScrollWheel(_) => None, PlatformInput::FileDrop(_) => None, + PlatformInput::TouchPad(_) => None, + } + } + pub(crate) fn touchpad_event(&self) -> Option<&TouchPadGestureEvent> { + match self { + PlatformInput::TouchPad(event) => Some(event), + _ => None, } } } diff --git a/crates/gpui/src/platform/mac/events.rs b/crates/gpui/src/platform/mac/events.rs index 938db4b762..92cbcb2067 100644 --- a/crates/gpui/src/platform/mac/events.rs +++ b/crates/gpui/src/platform/mac/events.rs @@ -1,7 +1,7 @@ use crate::{ Capslock, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, - MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, - PlatformInput, ScrollDelta, ScrollWheelEvent, TouchPhase, + MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, + Pixels, PlatformInput, ScrollDelta, ScrollWheelEvent, SwipeDirection, TouchPadGestureEvent, TouchPhase, platform::mac::{ LMGetKbdType, NSStringExt, TISCopyCurrentKeyboardLayoutInputSource, TISGetInputSourceProperty, UCKeyTranslate, kTISPropertyUnicodeKeyLayoutData, @@ -186,33 +186,14 @@ impl PlatformInput { }) }) } - // Some mice (like Logitech MX Master) send navigation buttons as swipe events - NSEventType::NSEventTypeSwipe => { - let navigation_direction = match native_event.phase() { - NSEventPhase::NSEventPhaseEnded => match native_event.deltaX() { - x if x > 0.0 => Some(NavigationDirection::Back), - x if x < 0.0 => Some(NavigationDirection::Forward), - _ => return None, - }, - _ => return None, - }; - - match navigation_direction { - Some(direction) => window_height.map(|window_height| { - Self::MouseDown(MouseDownEvent { - button: MouseButton::Navigate(direction), - position: point( - px(native_event.locationInWindow().x as f32), - window_height - px(native_event.locationInWindow().y as f32), - ), - modifiers: read_modifiers(native_event), - click_count: 1, - first_mouse: false, - }) - }), - _ => None, - } + NSEventType::NSEventTypeSwipe => match (native_event.deltaX() as i32, native_event.deltaY() as i32) { + (1, 0) => Some(SwipeDirection::Left), + (-1, 0) => Some(SwipeDirection::Right), + (0, 1) => Some(SwipeDirection::Up), + (0, -1) => Some(SwipeDirection::Down), + _ => None, } + .map(|direction| Self::TouchPad(TouchPadGestureEvent::Swipe(direction))), NSEventType::NSScrollWheel => window_height.map(|window_height| { let phase = match native_event.phase() { NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => { diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 0791dcc621..576a5c4b33 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -12,8 +12,8 @@ use crate::{ PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS, ScaledPixels, Scene, Shadow, SharedString, Size, - StrikethroughStyle, Style, SubscriberSet, Subscription, TabHandles, TaffyLayoutEngine, Task, - TextStyle, TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle, + StrikethroughStyle, Style, SubscriberSet, Subscription, SwipeDirection, TabHandles, TaffyLayoutEngine, Task, TextStyle, + TextStyleRefinement, TouchPadGestureEvent, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, point, prelude::*, px, rems, size, transparent_black, @@ -3554,12 +3554,15 @@ impl Window { } }, PlatformInput::KeyDown(_) | PlatformInput::KeyUp(_) => event, + PlatformInput::TouchPad(TouchPadGestureEvent::Swipe(_)) => event, }; if let Some(any_mouse_event) = event.mouse_event() { self.dispatch_mouse_event(any_mouse_event, cx); } else if let Some(any_key_event) = event.keyboard_event() { self.dispatch_key_event(any_key_event, cx); + } else if let Some(TouchPadGestureEvent::Swipe(direction)) = event.touchpad_event() { + self.dispatch_swipe_event(direction, cx); } DispatchEventResult { @@ -3621,6 +3624,26 @@ impl Window { } } + fn dispatch_swipe_event(&mut self, direction: &SwipeDirection, cx: &mut App) { + // Eventually we want to read from a config what to do, while forward/back is default. + let action_name = match direction { + SwipeDirection::Right => Some("pane::GoForward"), + SwipeDirection::Left => Some("pane::GoBack"), + _ => None, + }; + + if let Ok(_) = action_name + .ok_or_else(|| crate::ActionBuildError::NotFound { + name: format!("swipe::{:?}", action_name), + }) + .and_then(|action_name| cx.build_action(action_name, None)) + .map(|action| { + self.dispatch_action(action, cx); + direction + }) + {} + } + fn dispatch_key_event(&mut self, event: &dyn Any, cx: &mut App) { if self.invalidator.is_dirty() { self.draw(cx).clear();