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