| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 6 #error "This file requires ARC support." |
| 7 #endif |
| 8 |
| 9 #import "remoting/client/ios/client_gestures.h" |
| 10 |
| 11 @implementation ClientGestures |
| 12 |
| 13 - (id)initWithView:(UIView*)view { |
| 14 _inputScheme = HostInputSchemeTouch; |
| 15 |
| 16 _longPressRecognizer = [[UILongPressGestureRecognizer alloc] |
| 17 initWithTarget:self |
| 18 action:@selector(longPressGestureTriggered:)]; |
| 19 _longPressRecognizer.delegate = self; |
| 20 [view addGestureRecognizer:_longPressRecognizer]; |
| 21 |
| 22 _panRecognizer = [[UIPanGestureRecognizer alloc] |
| 23 initWithTarget:self |
| 24 action:@selector(panGestureTriggered:)]; |
| 25 _panRecognizer.minimumNumberOfTouches = 1; |
| 26 _panRecognizer.maximumNumberOfTouches = 2; |
| 27 _panRecognizer.delegate = self; |
| 28 [view addGestureRecognizer:_panRecognizer]; |
| 29 |
| 30 _threeFingerPanRecognizer = [[UIPanGestureRecognizer alloc] |
| 31 initWithTarget:self |
| 32 action:@selector(threeFingerPanGestureTriggered:)]; |
| 33 _threeFingerPanRecognizer.minimumNumberOfTouches = 3; |
| 34 _threeFingerPanRecognizer.maximumNumberOfTouches = 3; |
| 35 _threeFingerPanRecognizer.delegate = self; |
| 36 [view addGestureRecognizer:_threeFingerPanRecognizer]; |
| 37 |
| 38 _pinchRecognizer = [[UIPinchGestureRecognizer alloc] |
| 39 initWithTarget:self |
| 40 action:@selector(pinchGestureTriggered:)]; |
| 41 _pinchRecognizer.delegate = self; |
| 42 [view addGestureRecognizer:_pinchRecognizer]; |
| 43 |
| 44 _singleTapRecognizer = [[UITapGestureRecognizer alloc] |
| 45 initWithTarget:self |
| 46 action:@selector(tapGestureTriggered:)]; |
| 47 _singleTapRecognizer.delegate = self; |
| 48 [view addGestureRecognizer:_singleTapRecognizer]; |
| 49 |
| 50 _twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] |
| 51 initWithTarget:self |
| 52 action:@selector(twoFingerTapGestureTriggered:)]; |
| 53 _twoFingerTapRecognizer.numberOfTouchesRequired = 2; |
| 54 _twoFingerTapRecognizer.delegate = self; |
| 55 [view addGestureRecognizer:_twoFingerTapRecognizer]; |
| 56 |
| 57 _threeFingerTapRecognizer = [[UITapGestureRecognizer alloc] |
| 58 initWithTarget:self |
| 59 action:@selector(threeFingerTapGestureTriggered:)]; |
| 60 _threeFingerTapRecognizer.numberOfTouchesRequired = 3; |
| 61 _threeFingerPanRecognizer.delegate = self; |
| 62 [view addGestureRecognizer:_threeFingerTapRecognizer]; |
| 63 |
| 64 _inputScheme = HostInputSchemeTouch; |
| 65 |
| 66 [_singleTapRecognizer requireGestureRecognizerToFail:_twoFingerTapRecognizer]; |
| 67 [_twoFingerTapRecognizer |
| 68 requireGestureRecognizerToFail:_threeFingerTapRecognizer]; |
| 69 [_pinchRecognizer requireGestureRecognizerToFail:_singleTapRecognizer]; |
| 70 [_panRecognizer requireGestureRecognizerToFail:_singleTapRecognizer]; |
| 71 [_threeFingerPanRecognizer |
| 72 requireGestureRecognizerToFail:_threeFingerTapRecognizer]; |
| 73 |
| 74 _edgeGesture = [[UIScreenEdgePanGestureRecognizer alloc] |
| 75 initWithTarget:self |
| 76 action:@selector(edgeGestureTriggered:)]; |
| 77 //_edgeGesture.edges = UIRectEdgeLeft; |
| 78 _edgeGesture.delegate = self; |
| 79 [view addGestureRecognizer:_edgeGesture]; |
| 80 |
| 81 _swipeGesture = [[UISwipeGestureRecognizer alloc] |
| 82 initWithTarget:self |
| 83 action:@selector(swipeGestureTriggered:)]; |
| 84 _swipeGesture.numberOfTouchesRequired = 2; |
| 85 _swipeGesture.delegate = self; |
| 86 [view addGestureRecognizer:_swipeGesture]; |
| 87 |
| 88 return self; |
| 89 } |
| 90 |
| 91 // TODO(nicholss): The following several methods have been commented out. |
| 92 // Integreation with the original implementation will be done with the app |
| 93 // is able to run to the point of interaction and debugging can happen. |
| 94 // I would prefer to leave this here rather than deleting because it is close |
| 95 // to the implementation that will be used client gestures integration. |
| 96 // This is a new class that was derived from the original source which had |
| 97 // the implementation mixed in with the host controller and was a huge mess. |
| 98 |
| 99 // Resize the view of the desktop - Zoom in/out. This can occur during a Pan. |
| 100 - (IBAction)pinchGestureTriggered:(UIPinchGestureRecognizer*)sender { |
| 101 // LOG_TRACE(INFO) << "pinchGestureTriggered"; |
| 102 // if ([sender state] == UIGestureRecognizerStateChanged) { |
| 103 // [self applySceneChange:CGPointMake(0.0, 0.0) scaleBy:sender.scale]; |
| 104 // |
| 105 // sender.scale = 1.0; // reset scale so next iteration is a relative ratio |
| 106 // } |
| 107 } |
| 108 |
| 109 - (IBAction)tapGestureTriggered:(UITapGestureRecognizer*)sender { |
| 110 // LOG_TRACE(INFO) << "tapGestureTriggered"; |
| 111 // CGPoint touchPoint = [sender locationInView:self.view]; |
| 112 // if ([_scene containsTouchPoint:touchPoint]) { |
| 113 // if (_inputScheme == HostInputSchemeTouch) { |
| 114 // [_scene setMouseLocationFromLocationInView:touchPoint]; |
| 115 // _circle.expandedRadius = 11.0f; |
| 116 // [_circle doExpandingAnimationAtLocation:[_scene mouseLocationInView]]; |
| 117 // } |
| 118 // [Utility leftClickOn:_clientToHostProxy at:_scene.mousePosition]; |
| 119 // } |
| 120 } |
| 121 |
| 122 // Change position of scene. This can occur during a pinch or long press. |
| 123 // Or perform a Mouse Wheel Scroll. |
| 124 - (IBAction)panGestureTriggered:(UIPanGestureRecognizer*)sender { |
| 125 // LOG_TRACE(INFO) << "panGestureTriggered"; |
| 126 // CGPoint translation = [sender translationInView:self.view]; |
| 127 // // If we start with 2 touches, and the pinch gesture is not in progress |
| 128 // yet, |
| 129 // // then disable it, so mouse scrolling and zoom do not occur at the same |
| 130 // // time. |
| 131 // if ([sender numberOfTouches] == 2 && |
| 132 // [sender state] == UIGestureRecognizerStateBegan && |
| 133 // !(_pinchRecognizer.state == UIGestureRecognizerStateBegan || |
| 134 // _pinchRecognizer.state == UIGestureRecognizerStateChanged)) { |
| 135 // _pinchRecognizer.enabled = NO; |
| 136 // } |
| 137 // |
| 138 // if (!_pinchRecognizer.enabled) { |
| 139 // // Began with 2 touches, so this is a scroll event. |
| 140 // translation.x *= kMouseWheelSensitivity; |
| 141 // translation.y *= kMouseWheelSensitivity; |
| 142 // // [Utility mouseScroll:_clientToHostProxy |
| 143 // // at:_scene.mousePosition |
| 144 // // delta:webrtc::DesktopVector(translation.x, |
| 145 // translation.y)]; |
| 146 // } else { |
| 147 // // Did not begin with 2 touches, doing a pan event. |
| 148 // if ([sender state] == UIGestureRecognizerStateChanged) { |
| 149 // CGPoint translation = [sender translationInView:self.view]; |
| 150 // BOOL shouldApplyPanAndTapBounding = |
| 151 // _inputScheme == HostInputSchemeTouch && |
| 152 // [_longPressRecognizer state] != UIGestureRecognizerStateChanged; |
| 153 // |
| 154 // if (shouldApplyPanAndTapBounding) { |
| 155 // // Reverse the orientation on both axes. |
| 156 // translation = CGPointMake(-translation.x, -translation.y); |
| 157 // } |
| 158 // |
| 159 // // if (shouldApplyPanAndTapBounding) { |
| 160 // // // Stop the translation as soon as the view becomes anchored. |
| 161 // // if ([SceneView |
| 162 // // couldMouseMoveTowardAnchorWithTranslation:translation.x |
| 163 // // isAnchoredLow:_scene.anchored.left |
| 164 // // isAnchoredHigh:_scene.anchored |
| 165 // // .right]) { |
| 166 // // translation = CGPointMake(0, translation.y); |
| 167 // // } |
| 168 // // |
| 169 // // if ([SceneView |
| 170 // // couldMouseMoveTowardAnchorWithTranslation:translation.y |
| 171 // // isAnchoredLow:_scene.anchored.top |
| 172 // // isAnchoredHigh:_scene.anchored |
| 173 // // .bottom]) |
| 174 // { |
| 175 // // translation = CGPointMake(translation.x, 0); |
| 176 // // } |
| 177 // // } |
| 178 // |
| 179 // [self applySceneChange:translation scaleBy:1.0]; |
| 180 // |
| 181 // if (_inputScheme == HostInputSchemeTouch && |
| 182 // [_longPressRecognizer state] == UIGestureRecognizerStateChanged |
| 183 // && |
| 184 // [sender numberOfTouches] == 1) { |
| 185 // // [_scene |
| 186 // // setMouseLocationFromLocationInView:[sender |
| 187 // // locationInView:self.view]]; |
| 188 // |
| 189 // // [Utility moveMouse:_clientToHostProxy at:_scene.mousePosition]; |
| 190 // } |
| 191 // |
| 192 // } else if ([sender state] == UIGestureRecognizerStateEnded) { |
| 193 // // After user removes their fingers from the screen |
| 194 // // apply an acceleration effect. |
| 195 // CGPoint velocity = [sender velocityInView:self.view]; |
| 196 // |
| 197 // if (_inputScheme == HostInputSchemeTouch) { |
| 198 // // Reverse the orientation on both axes. |
| 199 // velocity = CGPointMake(-velocity.x, -velocity.y); |
| 200 // } |
| 201 // // [_scene setPanVelocity:velocity]; |
| 202 // } |
| 203 // } |
| 204 // |
| 205 // // Finished the event chain. |
| 206 // if (!([sender state] == UIGestureRecognizerStateBegan || |
| 207 // [sender state] == UIGestureRecognizerStateChanged)) { |
| 208 // _pinchRecognizer.enabled = YES; |
| 209 // } |
| 210 // |
| 211 // // Reset translation so next iteration is relative. Wait until a changed |
| 212 // // event in order to also capture the portion of the translation that |
| 213 // occurred |
| 214 // // between the Began and Changed States. |
| 215 // if ([sender state] == UIGestureRecognizerStateChanged) { |
| 216 // [sender setTranslation:CGPointZero inView:self.view]; |
| 217 // } |
| 218 } |
| 219 |
| 220 // Click-Drag mouse operation. This can occur during a Pan. |
| 221 - (IBAction)longPressGestureTriggered:(UILongPressGestureRecognizer*)sender { |
| 222 // LOG_TRACE(INFO) << "longPressGestureTriggered"; |
| 223 // if ([sender state] == UIGestureRecognizerStateBegan) { |
| 224 // if (_inputScheme == HostInputSchemeTouch) { |
| 225 // CGPoint touchPoint = [sender locationInView:self.view]; |
| 226 // [_scene setMouseLocationFromLocationInView:touchPoint]; |
| 227 // } |
| 228 // [_clientToHostProxy mouseAction:_scene.mousePosition |
| 229 // wheelDelta:webrtc::DesktopVector(0, 0) |
| 230 // whichButton:1 |
| 231 // buttonDown:YES]; |
| 232 // if (_inputScheme == HostInputSchemeTouch) { |
| 233 // // location is going to be under the user's finger |
| 234 // // create a bigger bubble. |
| 235 // _circle.expandedRadius = 110.0f; |
| 236 // } else { |
| 237 // _circle.expandedRadius = 11.0f; |
| 238 // } |
| 239 // |
| 240 // [_circle doExpandingAnimationAtLocation:[_scene mouseLocationInView]]; |
| 241 // } else if (!([sender state] == UIGestureRecognizerStateBegan || |
| 242 // [sender state] == UIGestureRecognizerStateChanged)) { |
| 243 // [_clientToHostProxy mouseAction:_scene.mousePosition |
| 244 // wheelDelta:webrtc::DesktopVector(0, 0) |
| 245 // whichButton:1 |
| 246 // buttonDown:NO]; |
| 247 // if (_inputScheme == HostInputSchemeTouch) { |
| 248 // // Return to the center. |
| 249 // [_scene centerMouseInView]; |
| 250 // } |
| 251 // } |
| 252 } |
| 253 |
| 254 - (IBAction)twoFingerTapGestureTriggered:(UITapGestureRecognizer*)sender { |
| 255 // LOG_TRACE(INFO) << "twoFingerTapGestureTriggered"; |
| 256 if (_inputScheme == HostInputSchemeTouch) { |
| 257 // disabled |
| 258 return; |
| 259 } |
| 260 // if ([_scene containsTouchPoint:[sender locationInView:self.view]]) { |
| 261 // [Utility rightClickOn:_clientToHostProxy at:_scene.mousePosition]; |
| 262 // } |
| 263 } |
| 264 |
| 265 - (IBAction)threeFingerTapGestureTriggered:(UITapGestureRecognizer*)sender { |
| 266 // LOG_TRACE(INFO) << "threeFingerTapGestureTriggered"; |
| 267 if (_inputScheme == HostInputSchemeTouch) { |
| 268 // disabled |
| 269 return; |
| 270 } |
| 271 |
| 272 // if ([_scene containsTouchPoint:[sender locationInView:self.view]]) { |
| 273 // [Utility middleClickOn:_clientToHostProxy at:_scene.mousePosition]; |
| 274 // } |
| 275 } |
| 276 |
| 277 - (IBAction)threeFingerPanGestureTriggered:(UIPanGestureRecognizer*)sender { |
| 278 // LOG_TRACE(INFO) << "threeFingerPanGestureTriggered"; |
| 279 // if ([sender state] == UIGestureRecognizerStateChanged) { |
| 280 // CGPoint translation = [sender translationInView:self.view]; |
| 281 // if (translation.y > 0) { |
| 282 // // Swiped down - do nothing |
| 283 // } else if (translation.y < 0) { |
| 284 // // Swiped up |
| 285 // [_keyEntryView becomeFirstResponder]; |
| 286 // } |
| 287 // [sender setTranslation:CGPointZero inView:self.view]; |
| 288 // } |
| 289 } |
| 290 |
| 291 - (IBAction)edgeGestureTriggered:(UIScreenEdgePanGestureRecognizer*)sender { |
| 292 // LOG_TRACE(INFO) << "edgeGestureTriggered"; |
| 293 } |
| 294 |
| 295 - (IBAction)swipeGestureTriggered:(UISwipeGestureRecognizer*)sender { |
| 296 // LOG_TRACE(INFO) << "swipeGestureTriggered"; |
| 297 } |
| 298 |
| 299 #pragma mark - UIGestureRecognizerDelegate |
| 300 |
| 301 // Allow panning and zooming to occur simultaneously. |
| 302 // Allow panning and long press to occur simultaneously. |
| 303 // Pinch requires 2 touches, and long press requires a single touch, so they are |
| 304 // mutually exclusive regardless of if panning is the initiating gesture. |
| 305 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer |
| 306 shouldRecognizeSimultaneouslyWithGestureRecognizer: |
| 307 (UIGestureRecognizer*)otherGestureRecognizer { |
| 308 if (gestureRecognizer == _pinchRecognizer || |
| 309 (gestureRecognizer == _panRecognizer)) { |
| 310 if (otherGestureRecognizer == _pinchRecognizer || |
| 311 otherGestureRecognizer == _panRecognizer) { |
| 312 return YES; |
| 313 } |
| 314 } |
| 315 |
| 316 if (gestureRecognizer == _longPressRecognizer || |
| 317 gestureRecognizer == _panRecognizer) { |
| 318 if (otherGestureRecognizer == _longPressRecognizer || |
| 319 otherGestureRecognizer == _panRecognizer) { |
| 320 return YES; |
| 321 } |
| 322 } |
| 323 |
| 324 if (gestureRecognizer == _twoFingerTapRecognizer && |
| 325 otherGestureRecognizer == _longPressRecognizer) { |
| 326 return YES; |
| 327 } |
| 328 |
| 329 if (gestureRecognizer == _panRecognizer && |
| 330 otherGestureRecognizer == _edgeGesture) { |
| 331 return YES; |
| 332 } |
| 333 // TODO(nicholss): If we return NO here, it dismisses the other reconizers. |
| 334 // As we add more types of reconizers, they need to be accounted for in the |
| 335 // above logic. |
| 336 return NO; |
| 337 } |
| 338 |
| 339 @end |
| OLD | NEW |