Skip to content

Commit 0812adc

Browse files
jpedrickmadsmtm
authored andcommitted
Add UIGestureRecognizerDelegate and PanGestureRecogniser (#3597)
- Allow all gestures simultaneously recognized. - Add PanGestureRecogniser with min/max number of touches. - Fix sending delta values relative to Update instead to match macOS. - Fix rotation gesture units from iOS to be in degrees instead of radians. Co-authored-by: Mads Marquart <mads@marquart.dk>
1 parent cd6ec19 commit 0812adc

File tree

8 files changed

+289
-31
lines changed

8 files changed

+289
-31
lines changed

examples/window.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ impl Application {
159159
window.recognize_doubletap_gesture(true);
160160
window.recognize_pinch_gesture(true);
161161
window.recognize_rotation_gesture(true);
162+
window.recognize_pan_gesture(true, 2, 2);
162163
}
163164

164165
let window_state = WindowState::new(self, window)?;
@@ -428,6 +429,11 @@ impl ApplicationHandler<UserEvent> for Application {
428429
info!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
429430
}
430431
},
432+
WindowEvent::PanGesture { delta, phase, .. } => {
433+
window.panned.x += delta.x;
434+
window.panned.y += delta.y;
435+
info!("Panned ({delta:?})) (now: {:?}), {phase:?}", window.panned);
436+
},
431437
WindowEvent::DoubleTapGesture { .. } => {
432438
info!("Smart zoom");
433439
},
@@ -502,6 +508,8 @@ struct WindowState {
502508
zoom: f64,
503509
/// The amount of rotation of the window.
504510
rotated: f32,
511+
/// The amount of pan of the window.
512+
panned: PhysicalPosition<f32>,
505513

506514
#[cfg(macos_platform)]
507515
option_as_alt: OptionAsAlt,
@@ -547,6 +555,7 @@ impl WindowState {
547555
modifiers: Default::default(),
548556
occluded: Default::default(),
549557
rotated: Default::default(),
558+
panned: Default::default(),
550559
zoom: Default::default(),
551560
};
552561

src/changelog/unreleased.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ changelog entry.
5959
- Add `CustomCursor` which could be set via `Window::set_cursor`, implemented on
6060
Windows, macOS, X11, Wayland, and Web.
6161
- On Web, add to toggle calling `Event.preventDefault()` on `Window`.
62-
- On iOS, add `PinchGesture`, `DoubleTapGesture`, and `RotationGesture`
62+
- On iOS, add `PinchGesture`, `DoubleTapGesture`, `PanGesture` and `RotationGesture`.
63+
- on iOS, use `UIGestureRecognizerDelegate` for fine grained control of gesture recognizers.
6364
- On macOS, add services menu.
6465
- On Windows, add `with_title_text_color`, and `with_corner_preference` on
6566
`WindowAttributesExtWindows`.

src/event.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,19 @@ pub enum WindowEvent {
293293
phase: TouchPhase,
294294
},
295295

296+
/// N-finger pan gesture
297+
///
298+
/// ## Platform-specific
299+
///
300+
/// - Only available on **iOS**.
301+
/// - On iOS, not recognized by default. It must be enabled when needed.
302+
PanGesture {
303+
device_id: DeviceId,
304+
/// Change in pixels of pan gesture from last update.
305+
delta: PhysicalPosition<f32>,
306+
phase: TouchPhase,
307+
},
308+
296309
/// Double tap gesture.
297310
///
298311
/// On a Mac, smart magnification is triggered by a double tap with two fingers
@@ -322,7 +335,12 @@ pub enum WindowEvent {
322335
///
323336
/// - Only available on **macOS** and **iOS**.
324337
/// - On iOS, not recognized by default. It must be enabled when needed.
325-
RotationGesture { device_id: DeviceId, delta: f32, phase: TouchPhase },
338+
RotationGesture {
339+
device_id: DeviceId,
340+
/// change in rotation in degrees
341+
delta: f32,
342+
phase: TouchPhase,
343+
},
326344

327345
/// Touchpad pressure event.
328346
///
@@ -993,6 +1011,7 @@ impl PartialEq for InnerSizeWriter {
9931011

9941012
#[cfg(test)]
9951013
mod tests {
1014+
use crate::dpi::PhysicalPosition;
9961015
use crate::event;
9971016
use std::collections::{BTreeSet, HashSet};
9981017

@@ -1055,6 +1074,11 @@ mod tests {
10551074
delta: 0.0,
10561075
phase: event::TouchPhase::Started,
10571076
});
1077+
with_window_event(PanGesture {
1078+
device_id: did,
1079+
delta: PhysicalPosition::<f32>::new(0.0, 0.0),
1080+
phase: event::TouchPhase::Started,
1081+
});
10581082
with_window_event(TouchpadPressure { device_id: did, pressure: 0.0, stage: 0 });
10591083
with_window_event(AxisMotion { device_id: did, axis: 0, value: 0.0 });
10601084
with_window_event(Touch(event::Touch {

src/platform/ios.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,21 @@ pub trait WindowExtIOS {
155155
/// The default is to not recognize gestures.
156156
fn recognize_pinch_gesture(&self, should_recognize: bool);
157157

158+
/// Sets whether the [`Window`] should recognize pan gestures.
159+
///
160+
/// The default is to not recognize gestures.
161+
/// Installs [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer) onto view
162+
///
163+
/// Set the minimum number of touches required: [`minimumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-minimumnumberoftouches)
164+
///
165+
/// Set the maximum number of touches recognized: [`maximumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-maximumnumberoftouches)
166+
fn recognize_pan_gesture(
167+
&self,
168+
should_recognize: bool,
169+
minimum_number_of_touches: u8,
170+
maximum_number_of_touches: u8,
171+
);
172+
158173
/// Sets whether the [`Window`] should recognize double tap gestures.
159174
///
160175
/// The default is to not recognize gestures.
@@ -204,6 +219,22 @@ impl WindowExtIOS for Window {
204219
self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
205220
}
206221

222+
#[inline]
223+
fn recognize_pan_gesture(
224+
&self,
225+
should_recognize: bool,
226+
minimum_number_of_touches: u8,
227+
maximum_number_of_touches: u8,
228+
) {
229+
self.window.maybe_queue_on_main(move |w| {
230+
w.recognize_pan_gesture(
231+
should_recognize,
232+
minimum_number_of_touches,
233+
maximum_number_of_touches,
234+
)
235+
});
236+
}
237+
207238
#[inline]
208239
fn recognize_doubletap_gesture(&self, should_recognize: bool) {
209240
self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));

src/platform_impl/ios/uikit/gesture_recognizer.rs

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
use objc2::encode::{Encode, Encoding};
2-
use objc2::{extern_class, extern_methods, mutability, ClassType};
3-
use objc2_foundation::{CGFloat, NSInteger, NSObject, NSUInteger};
2+
use objc2::rc::Id;
3+
use objc2::runtime::ProtocolObject;
4+
use objc2::{extern_class, extern_methods, extern_protocol, mutability, ClassType, ProtocolType};
5+
use objc2_foundation::{CGFloat, CGPoint, NSInteger, NSObject, NSObjectProtocol, NSUInteger};
6+
7+
use super::UIView;
48

5-
// https://developer.apple.com/documentation/uikit/uigesturerecognizer
69
extern_class!(
10+
/// [`UIGestureRecognizer`](https://developer.apple.com/documentation/uikit/uigesturerecognizer)
711
#[derive(Debug, PartialEq, Eq, Hash)]
812
pub(crate) struct UIGestureRecognizer;
913

@@ -17,14 +21,22 @@ extern_methods!(
1721
unsafe impl UIGestureRecognizer {
1822
#[method(state)]
1923
pub fn state(&self) -> UIGestureRecognizerState;
24+
25+
/// [`delegate`](https://developer.apple.com/documentation/uikit/uigesturerecognizer/1624207-delegate?language=objc)
26+
/// @property(nullable, nonatomic, weak) id<UIGestureRecognizerDelegate> delegate;
27+
#[method(setDelegate:)]
28+
pub fn setDelegate(&self, delegate: &ProtocolObject<dyn UIGestureRecognizerDelegate>);
29+
30+
#[method_id(delegate)]
31+
pub fn delegate(&self) -> Id<ProtocolObject<dyn UIGestureRecognizerDelegate>>;
2032
}
2133
);
2234

2335
unsafe impl Encode for UIGestureRecognizer {
2436
const ENCODING: Encoding = Encoding::Object;
2537
}
2638

27-
// https://developer.apple.com/documentation/uikit/uigesturerecognizer/state
39+
// [`UIGestureRecognizerState`](https://developer.apple.com/documentation/uikit/uigesturerecognizer/state)
2840
#[repr(transparent)]
2941
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3042
pub struct UIGestureRecognizerState(NSInteger);
@@ -43,7 +55,7 @@ impl UIGestureRecognizerState {
4355
pub const Failed: Self = Self(5);
4456
}
4557

46-
// https://developer.apple.com/documentation/uikit/uipinchgesturerecognizer
58+
// [`UIPinchGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipinchgesturerecognizer)
4759
extern_class!(
4860
#[derive(Debug, PartialEq, Eq, Hash)]
4961
pub(crate) struct UIPinchGestureRecognizer;
@@ -68,8 +80,8 @@ unsafe impl Encode for UIPinchGestureRecognizer {
6880
const ENCODING: Encoding = Encoding::Object;
6981
}
7082

71-
// https://developer.apple.com/documentation/uikit/uirotationgesturerecognizer
7283
extern_class!(
84+
/// [`UIRotationGestureRecognizer`](https://developer.apple.com/documentation/uikit/uirotationgesturerecognizer)
7385
#[derive(Debug, PartialEq, Eq, Hash)]
7486
pub(crate) struct UIRotationGestureRecognizer;
7587

@@ -93,8 +105,8 @@ unsafe impl Encode for UIRotationGestureRecognizer {
93105
const ENCODING: Encoding = Encoding::Object;
94106
}
95107

96-
// https://developer.apple.com/documentation/uikit/uitapgesturerecognizer
97108
extern_class!(
109+
/// [`UITapGestureRecognizer`](https://developer.apple.com/documentation/uikit/uitapgesturerecognizer)
98110
#[derive(Debug, PartialEq, Eq, Hash)]
99111
pub(crate) struct UITapGestureRecognizer;
100112

@@ -117,3 +129,48 @@ extern_methods!(
117129
unsafe impl Encode for UITapGestureRecognizer {
118130
const ENCODING: Encoding = Encoding::Object;
119131
}
132+
133+
extern_class!(
134+
/// [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer)
135+
#[derive(Debug, PartialEq, Eq, Hash)]
136+
pub(crate) struct UIPanGestureRecognizer;
137+
138+
unsafe impl ClassType for UIPanGestureRecognizer {
139+
type Super = UIGestureRecognizer;
140+
type Mutability = mutability::InteriorMutable;
141+
}
142+
);
143+
144+
extern_methods!(
145+
unsafe impl UIPanGestureRecognizer {
146+
#[method(translationInView:)]
147+
pub fn translationInView(&self, view: &UIView) -> CGPoint;
148+
149+
#[method(setTranslation:inView:)]
150+
pub fn setTranslationInView(&self, translation: CGPoint, view: &UIView);
151+
152+
#[method(velocityInView:)]
153+
pub fn velocityInView(&self, view: &UIView) -> CGPoint;
154+
155+
#[method(setMinimumNumberOfTouches:)]
156+
pub fn setMinimumNumberOfTouches(&self, minimum_number_of_touches: NSUInteger);
157+
158+
#[method(minimumNumberOfTouches)]
159+
pub fn minimumNumberOfTouches(&self) -> NSUInteger;
160+
161+
#[method(setMaximumNumberOfTouches:)]
162+
pub fn setMaximumNumberOfTouches(&self, maximum_number_of_touches: NSUInteger);
163+
164+
#[method(maximumNumberOfTouches)]
165+
pub fn maximumNumberOfTouches(&self) -> NSUInteger;
166+
}
167+
);
168+
169+
extern_protocol!(
170+
/// (@protocol UIGestureRecognizerDelegate)[https://developer.apple.com/documentation/uikit/uigesturerecognizerdelegate?language=objc]
171+
pub(crate) unsafe trait UIGestureRecognizerDelegate: NSObjectProtocol {}
172+
173+
unsafe impl ProtocolType for dyn UIGestureRecognizerDelegate {
174+
const NAME: &'static str = "UIGestureRecognizerDelegate";
175+
}
176+
);

src/platform_impl/ios/uikit/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ pub(crate) use self::device::{UIDevice, UIUserInterfaceIdiom};
2727
pub(crate) use self::event::UIEvent;
2828
pub(crate) use self::geometry::UIRectEdge;
2929
pub(crate) use self::gesture_recognizer::{
30-
UIGestureRecognizer, UIGestureRecognizerState, UIPinchGestureRecognizer,
31-
UIRotationGestureRecognizer, UITapGestureRecognizer,
30+
UIGestureRecognizer, UIGestureRecognizerDelegate, UIGestureRecognizerState,
31+
UIPanGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer,
32+
UITapGestureRecognizer,
3233
};
3334
pub(crate) use self::responder::UIResponder;
3435
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};

0 commit comments

Comments
 (0)