client_gestures.mm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. #if !defined(__has_feature) || !__has_feature(objc_arc)
  5. #error "This file requires ARC support."
  6. #endif
  7. #import "remoting/ios/client_gestures.h"
  8. #import "remoting/ios/session/remoting_client.h"
  9. #include "remoting/client/gesture_interpreter.h"
  10. static remoting::GestureInterpreter::GestureState toGestureState(
  11. UIGestureRecognizerState state) {
  12. switch (state) {
  13. case UIGestureRecognizerStateBegan:
  14. return remoting::GestureInterpreter::GESTURE_BEGAN;
  15. case UIGestureRecognizerStateChanged:
  16. return remoting::GestureInterpreter::GESTURE_CHANGED;
  17. default:
  18. return remoting::GestureInterpreter::GESTURE_ENDED;
  19. }
  20. }
  21. @interface ClientGestures ()<UIGestureRecognizerDelegate> {
  22. @private
  23. UILongPressGestureRecognizer* _longPressRecognizer;
  24. UIPanGestureRecognizer* _panRecognizer;
  25. UIPanGestureRecognizer* _flingRecognizer;
  26. UIPanGestureRecognizer* _scrollRecognizer;
  27. // TODO(yuweih): Commented out because this makes two-finger gestures not
  28. // quite responsive. Clean these up if it's really unnecessary.
  29. // UIPanGestureRecognizer* _threeFingerPanRecognizer;
  30. UIPinchGestureRecognizer* _pinchRecognizer;
  31. UITapGestureRecognizer* _singleTapRecognizer;
  32. UITapGestureRecognizer* _twoFingerTapRecognizer;
  33. UITapGestureRecognizer* _threeFingerTapRecognizer;
  34. UITapGestureRecognizer* _fourFingerTapRecognizer;
  35. __weak UIView* _view;
  36. base::WeakPtr<remoting::GestureInterpreter> _gestureInterpreter;
  37. }
  38. @end
  39. @implementation ClientGestures
  40. @synthesize delegate = _delegate;
  41. - (instancetype)initWithView:(UIView*)view client:(RemotingClient*)client {
  42. _view = view;
  43. _gestureInterpreter = client.gestureInterpreter->GetWeakPtr();
  44. _longPressRecognizer = [[UILongPressGestureRecognizer alloc]
  45. initWithTarget:self
  46. action:@selector(longPressGestureTriggered:)];
  47. _longPressRecognizer.delegate = self;
  48. [view addGestureRecognizer:_longPressRecognizer];
  49. _panRecognizer = [[UIPanGestureRecognizer alloc]
  50. initWithTarget:self
  51. action:@selector(panGestureTriggered:)];
  52. _panRecognizer.minimumNumberOfTouches = 1;
  53. _panRecognizer.maximumNumberOfTouches = 2;
  54. _panRecognizer.delegate = self;
  55. [view addGestureRecognizer:_panRecognizer];
  56. _flingRecognizer = [[UIPanGestureRecognizer alloc]
  57. initWithTarget:self
  58. action:@selector(flingGestureTriggered:)];
  59. _flingRecognizer.minimumNumberOfTouches = 1;
  60. _flingRecognizer.maximumNumberOfTouches = 1;
  61. _flingRecognizer.delegate = self;
  62. [view addGestureRecognizer:_flingRecognizer];
  63. _scrollRecognizer = [[UIPanGestureRecognizer alloc]
  64. initWithTarget:self
  65. action:@selector(scrollGestureTriggered:)];
  66. _scrollRecognizer.minimumNumberOfTouches = 2;
  67. _scrollRecognizer.maximumNumberOfTouches = 2;
  68. _scrollRecognizer.delegate = self;
  69. [view addGestureRecognizer:_scrollRecognizer];
  70. // _threeFingerPanRecognizer = [[UIPanGestureRecognizer alloc]
  71. // initWithTarget:self
  72. // action:@selector(threeFingerPanGestureTriggered:)];
  73. // _threeFingerPanRecognizer.minimumNumberOfTouches = 3;
  74. // _threeFingerPanRecognizer.maximumNumberOfTouches = 3;
  75. // _threeFingerPanRecognizer.delegate = self;
  76. // [view addGestureRecognizer:_threeFingerPanRecognizer];
  77. _pinchRecognizer = [[UIPinchGestureRecognizer alloc]
  78. initWithTarget:self
  79. action:@selector(pinchGestureTriggered:)];
  80. _pinchRecognizer.delegate = self;
  81. [view addGestureRecognizer:_pinchRecognizer];
  82. _singleTapRecognizer = [[UITapGestureRecognizer alloc]
  83. initWithTarget:self
  84. action:@selector(tapGestureTriggered:)];
  85. _singleTapRecognizer.delegate = self;
  86. [view addGestureRecognizer:_singleTapRecognizer];
  87. _twoFingerTapRecognizer = [[UITapGestureRecognizer alloc]
  88. initWithTarget:self
  89. action:@selector(twoFingerTapGestureTriggered:)];
  90. _twoFingerTapRecognizer.numberOfTouchesRequired = 2;
  91. _twoFingerTapRecognizer.delegate = self;
  92. [view addGestureRecognizer:_twoFingerTapRecognizer];
  93. _threeFingerTapRecognizer = [[UITapGestureRecognizer alloc]
  94. initWithTarget:self
  95. action:@selector(threeFingerTapGestureTriggered:)];
  96. _threeFingerTapRecognizer.numberOfTouchesRequired = 3;
  97. _threeFingerTapRecognizer.delegate = self;
  98. [view addGestureRecognizer:_threeFingerTapRecognizer];
  99. _fourFingerTapRecognizer = [[UITapGestureRecognizer alloc]
  100. initWithTarget:self
  101. action:@selector(fourFingerTapGestureTriggered:)];
  102. _fourFingerTapRecognizer.numberOfTouchesRequired = 4;
  103. _fourFingerTapRecognizer.delegate = self;
  104. [view addGestureRecognizer:_fourFingerTapRecognizer];
  105. [_singleTapRecognizer requireGestureRecognizerToFail:_twoFingerTapRecognizer];
  106. [_twoFingerTapRecognizer
  107. requireGestureRecognizerToFail:_threeFingerTapRecognizer];
  108. [_pinchRecognizer requireGestureRecognizerToFail:_singleTapRecognizer];
  109. // [_pinchRecognizer
  110. // requireGestureRecognizerToFail:_threeFingerPanRecognizer];
  111. [_panRecognizer requireGestureRecognizerToFail:_singleTapRecognizer];
  112. // [_threeFingerPanRecognizer
  113. // requireGestureRecognizerToFail:_threeFingerTapRecognizer];
  114. [_panRecognizer requireGestureRecognizerToFail:_scrollRecognizer];
  115. return self;
  116. }
  117. // Resize the view of the desktop - Zoom in/out. This can occur during a Pan.
  118. - (IBAction)pinchGestureTriggered:(UIPinchGestureRecognizer*)sender {
  119. if (!_gestureInterpreter) {
  120. return;
  121. }
  122. CGPoint pivot = [sender locationInView:_view];
  123. _gestureInterpreter->Zoom(pivot.x, pivot.y, sender.scale,
  124. toGestureState([sender state]));
  125. sender.scale = 1.0; // reset scale so next iteration is a relative ratio
  126. }
  127. - (IBAction)tapGestureTriggered:(UITapGestureRecognizer*)sender {
  128. if (!_gestureInterpreter) {
  129. return;
  130. }
  131. CGPoint touchPoint = [sender locationInView:_view];
  132. _gestureInterpreter->Tap(touchPoint.x, touchPoint.y);
  133. }
  134. // Change position of the viewport. This can occur during a pinch or long press.
  135. - (IBAction)panGestureTriggered:(UIPanGestureRecognizer*)sender {
  136. if (!_gestureInterpreter) {
  137. return;
  138. }
  139. if ([sender state] == UIGestureRecognizerStateChanged) {
  140. CGPoint translation = [sender translationInView:_view];
  141. _gestureInterpreter->Pan(translation.x, translation.y);
  142. // Reset translation so next iteration is relative
  143. [sender setTranslation:CGPointZero inView:_view];
  144. }
  145. }
  146. // Do fling on the viewport. This will happen at the end of the one-finger
  147. // panning.
  148. - (IBAction)flingGestureTriggered:(UIPanGestureRecognizer*)sender {
  149. if (!_gestureInterpreter) {
  150. return;
  151. }
  152. if ([sender state] == UIGestureRecognizerStateEnded) {
  153. CGPoint velocity = [sender velocityInView:_view];
  154. if (velocity.x != 0 || velocity.y != 0) {
  155. _gestureInterpreter->OneFingerFling(velocity.x, velocity.y);
  156. }
  157. }
  158. }
  159. // Handles the two finger scrolling gesture.
  160. - (IBAction)scrollGestureTriggered:(UIPanGestureRecognizer*)sender {
  161. if (!_gestureInterpreter) {
  162. return;
  163. }
  164. if ([sender state] == UIGestureRecognizerStateEnded) {
  165. CGPoint velocity = [sender velocityInView:_view];
  166. _gestureInterpreter->ScrollWithVelocity(velocity.x, velocity.y);
  167. return;
  168. }
  169. CGPoint scrollPoint = [sender locationInView:_view];
  170. CGPoint translation = [sender translationInView:_view];
  171. _gestureInterpreter->Scroll(scrollPoint.x, scrollPoint.y, translation.x,
  172. translation.y);
  173. // Reset translation so next iteration is relative
  174. [sender setTranslation:CGPointZero inView:_view];
  175. }
  176. // Click-Drag mouse operation. This can occur during a Pan.
  177. - (IBAction)longPressGestureTriggered:(UILongPressGestureRecognizer*)sender {
  178. if (!_gestureInterpreter) {
  179. return;
  180. }
  181. CGPoint touchPoint = [sender locationInView:_view];
  182. _gestureInterpreter->Drag(touchPoint.x, touchPoint.y,
  183. toGestureState([sender state]));
  184. }
  185. - (IBAction)twoFingerTapGestureTriggered:(UITapGestureRecognizer*)sender {
  186. if (!_gestureInterpreter) {
  187. return;
  188. }
  189. CGPoint touchPoint = [sender locationInView:_view];
  190. _gestureInterpreter->TwoFingerTap(touchPoint.x, touchPoint.y);
  191. }
  192. - (IBAction)threeFingerTapGestureTriggered:(UITapGestureRecognizer*)sender {
  193. if (!_gestureInterpreter) {
  194. return;
  195. }
  196. CGPoint touchPoint = [sender locationInView:_view];
  197. _gestureInterpreter->ThreeFingerTap(touchPoint.x, touchPoint.y);
  198. }
  199. - (IBAction)threeFingerPanGestureTriggered:(UIPanGestureRecognizer*)sender {
  200. if (!_gestureInterpreter) {
  201. return;
  202. }
  203. if ([sender state] != UIGestureRecognizerStateEnded) {
  204. return;
  205. }
  206. CGPoint translation = [sender translationInView:_view];
  207. if (translation.y > 0) {
  208. // Swiped down - hide keyboard (for now)
  209. [_delegate keyboardShouldHide];
  210. } else if (translation.y < 0) {
  211. // Swiped up - show keyboard
  212. [_delegate keyboardShouldShow];
  213. }
  214. }
  215. // To trigger the menu.
  216. - (IBAction)fourFingerTapGestureTriggered:
  217. (UILongPressGestureRecognizer*)sender {
  218. if (!_gestureInterpreter) {
  219. return;
  220. }
  221. [_delegate menuShouldShow];
  222. }
  223. #pragma mark - UIGestureRecognizerDelegate
  224. // Allow panning and zooming to occur simultaneously.
  225. // Allow panning and long press to occur simultaneously.
  226. // Pinch requires 2 touches, and long press requires a single touch, so they are
  227. // mutually exclusive regardless of if panning is the initiating gesture.
  228. // Pinch and Scroll are both two-finger gestures. They are mutually exclusive
  229. // and whatever comes first should disable the other gesture recognizer.
  230. - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
  231. shouldRecognizeSimultaneouslyWithGestureRecognizer:
  232. (UIGestureRecognizer*)otherGestureRecognizer {
  233. if (gestureRecognizer == _pinchRecognizer ||
  234. (gestureRecognizer == _panRecognizer)) {
  235. if (otherGestureRecognizer == _pinchRecognizer ||
  236. otherGestureRecognizer == _panRecognizer) {
  237. return YES;
  238. }
  239. }
  240. if (gestureRecognizer == _flingRecognizer ||
  241. (gestureRecognizer == _panRecognizer)) {
  242. if (otherGestureRecognizer == _flingRecognizer ||
  243. otherGestureRecognizer == _panRecognizer) {
  244. return YES;
  245. }
  246. }
  247. if (gestureRecognizer == _longPressRecognizer ||
  248. gestureRecognizer == _panRecognizer) {
  249. if (otherGestureRecognizer == _longPressRecognizer ||
  250. otherGestureRecognizer == _panRecognizer) {
  251. return YES;
  252. }
  253. }
  254. if (gestureRecognizer == _twoFingerTapRecognizer &&
  255. otherGestureRecognizer == _longPressRecognizer) {
  256. return YES;
  257. }
  258. // TODO(nicholss): If we return NO here, it dismisses the other recognizers.
  259. // As we add more types of recognizers, they need to be accounted for in the
  260. // above logic.
  261. return NO;
  262. }
  263. @end