Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Side by Side Diff: remoting/ios/ui/scene_view.mm

Issue 475333004: Remove old Chromoting iOS client (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/ios/ui/scene_view.h ('k') | remoting/ios/ui/scene_view_unittest.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 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/ios/ui/scene_view.h"
10
11 #import "remoting/ios/utility.h"
12
13 namespace {
14
15 // TODO (aboone) Some of the layout is not yet set in stone, so variables have
16 // been used to position and turn items on and off. Eventually these may be
17 // stabilized and removed.
18
19 // Scroll speed multiplier for swiping
20 const static int kMouseSensitivity = 2.5;
21
22 // Input Axis inversion
23 // 1 for standard, -1 for inverted
24 const static int kXAxisInversion = -1;
25 const static int kYAxisInversion = -1;
26
27 // Experimental value for bounding the maximum zoom ratio
28 const static int kMaxZoomSize = 3;
29 } // namespace
30
31 @interface SceneView (Private)
32 // Returns the number of pixels displayed per device pixel when the scaling is
33 // such that the entire frame would fit perfectly in content. Note the ratios
34 // are different for width and height, some people have multiple monitors, some
35 // have 16:9 or 4:3 while iPad is always single screen, but different iOS
36 // devices have different resolutions.
37 - (CGPoint)pixelRatio;
38
39 // Return the FrameSize in perspective of the CLIENT resolution
40 - (webrtc::DesktopSize)frameSizeToScale:(float)scale;
41
42 // When bounded on the top and right, this point is where the scene must be
43 // positioned given a scene size
44 - (webrtc::DesktopVector)getBoundsForSize:(const webrtc::DesktopSize&)size;
45
46 // Converts a point in the the CLIENT resolution to a similar point in the HOST
47 // resolution. Additionally, CLIENT resolution is expressed in float values
48 // while HOST operates in integer values.
49 - (BOOL)convertTouchPointToMousePoint:(CGPoint)touchPoint
50 targetPoint:(webrtc::DesktopVector&)desktopPoint;
51
52 // Converts a point in the the HOST resolution to a similar point in the CLIENT
53 // resolution. Additionally, CLIENT resolution is expressed in float values
54 // while HOST operates in integer values.
55 - (BOOL)convertMousePointToTouchPoint:(const webrtc::DesktopVector&)mousePoint
56 targetPoint:(CGPoint&)touchPoint;
57 @end
58
59 @implementation SceneView
60
61 - (id)init {
62 self = [super init];
63 if (self) {
64
65 _frameSize = webrtc::DesktopSize(1, 1);
66 _contentSize = webrtc::DesktopSize(1, 1);
67 _mousePosition = webrtc::DesktopVector(0, 0);
68
69 _position = GLKVector3Make(0, 0, 1);
70 _margin.left = 0;
71 _margin.right = 0;
72 _margin.top = 0;
73 _margin.bottom = 0;
74 _anchored.left = false;
75 _anchored.right = false;
76 _anchored.top = false;
77 _anchored.bottom = false;
78 }
79 return self;
80 }
81
82 - (const GLKMatrix4&)projectionMatrix {
83 return _projectionMatrix;
84 }
85
86 - (const GLKMatrix4&)modelViewMatrix {
87 // Start by using the entire scene
88 _modelViewMatrix = GLKMatrix4Identity;
89
90 // Position scene according to any panning or bounds
91 _modelViewMatrix = GLKMatrix4Translate(_modelViewMatrix,
92 _position.x + _margin.left,
93 _position.y + _margin.bottom,
94 0.0);
95
96 // Apply zoom
97 _modelViewMatrix = GLKMatrix4Scale(_modelViewMatrix,
98 _position.z / self.pixelRatio.x,
99 _position.z / self.pixelRatio.y,
100 1.0);
101
102 // We are directly above the screen and looking down.
103 static const GLKMatrix4 viewMatrix = GLKMatrix4MakeLookAt(
104 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // center view
105
106 _modelViewMatrix = GLKMatrix4Multiply(viewMatrix, _modelViewMatrix);
107
108 return _modelViewMatrix;
109 }
110
111 - (const webrtc::DesktopSize&)contentSize {
112 return _contentSize;
113 }
114
115 - (void)setContentSize:(const CGSize&)size {
116
117 _contentSize.set(size.width, size.height);
118
119 _projectionMatrix = GLKMatrix4MakeOrtho(
120 0.0, _contentSize.width(), 0.0, _contentSize.height(), 1.0, -1.0);
121
122 TexturedQuad newQuad;
123 newQuad.bl.geometryVertex = CGPointMake(0.0, 0.0);
124 newQuad.br.geometryVertex = CGPointMake(_contentSize.width(), 0.0);
125 newQuad.tl.geometryVertex = CGPointMake(0.0, _contentSize.height());
126 newQuad.tr.geometryVertex =
127 CGPointMake(_contentSize.width(), _contentSize.height());
128
129 newQuad.bl.textureVertex = CGPointMake(0.0, 1.0);
130 newQuad.br.textureVertex = CGPointMake(1.0, 1.0);
131 newQuad.tl.textureVertex = CGPointMake(0.0, 0.0);
132 newQuad.tr.textureVertex = CGPointMake(1.0, 0.0);
133
134 _glQuad = newQuad;
135 }
136
137 - (const webrtc::DesktopSize&)frameSize {
138 return _frameSize;
139 }
140
141 - (void)setFrameSize:(const webrtc::DesktopSize&)size {
142 DCHECK(size.width() > 0 && size.height() > 0);
143 // Don't do anything if the size has not changed.
144 if (_frameSize.equals(size))
145 return;
146
147 _frameSize.set(size.width(), size.height());
148
149 _position.x = 0;
150 _position.y = 0;
151
152 float verticalPixelScaleRatio =
153 (static_cast<float>(_contentSize.height() - _margin.top -
154 _margin.bottom) /
155 static_cast<float>(_frameSize.height())) /
156 _position.z;
157
158 // Anchored at the position (0,0)
159 _anchored.left = YES;
160 _anchored.right = NO;
161 _anchored.top = NO;
162 _anchored.bottom = YES;
163
164 [self panAndZoom:CGPointMake(0.0, 0.0) scaleBy:verticalPixelScaleRatio];
165
166 // Center the mouse on the CLIENT screen
167 webrtc::DesktopVector centerMouseLocation;
168 if ([self convertTouchPointToMousePoint:CGPointMake(_contentSize.width() / 2,
169 _contentSize.height() / 2)
170 targetPoint:centerMouseLocation]) {
171 _mousePosition.set(centerMouseLocation.x(), centerMouseLocation.y());
172 }
173
174 #if DEBUG
175 NSLog(@"resized frame:%d:%d scale:%f",
176 _frameSize.width(),
177 _frameSize.height(),
178 _position.z);
179 #endif // DEBUG
180 }
181
182 - (const webrtc::DesktopVector&)mousePosition {
183 return _mousePosition;
184 }
185
186 - (void)setPanVelocity:(const CGPoint&)delta {
187 _panVelocity.x = delta.x;
188 _panVelocity.y = delta.y;
189 }
190
191 - (void)setMarginsFromLeft:(int)left
192 right:(int)right
193 top:(int)top
194 bottom:(int)bottom {
195 _margin.left = left;
196 _margin.right = right;
197 _margin.top = top;
198 _margin.bottom = bottom;
199 }
200
201 - (void)draw {
202 glEnableVertexAttribArray(GLKVertexAttribPosition);
203 glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
204 glEnableVertexAttribArray(GLKVertexAttribTexCoord1);
205
206 // Define our scene space
207 glVertexAttribPointer(GLKVertexAttribPosition,
208 2,
209 GL_FLOAT,
210 GL_FALSE,
211 sizeof(TexturedVertex),
212 &(_glQuad.bl.geometryVertex));
213 // Define the desktop plane
214 glVertexAttribPointer(GLKVertexAttribTexCoord0,
215 2,
216 GL_FLOAT,
217 GL_FALSE,
218 sizeof(TexturedVertex),
219 &(_glQuad.bl.textureVertex));
220 // Define the cursor plane
221 glVertexAttribPointer(GLKVertexAttribTexCoord1,
222 2,
223 GL_FLOAT,
224 GL_FALSE,
225 sizeof(TexturedVertex),
226 &(_glQuad.bl.textureVertex));
227
228 // Draw!
229 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
230
231 [Utility logGLErrorCode:@"SceneView draw"];
232 }
233
234 - (CGPoint)pixelRatio {
235
236 CGPoint r = CGPointMake(static_cast<float>(_contentSize.width()) /
237 static_cast<float>(_frameSize.width()),
238 static_cast<float>(_contentSize.height()) /
239 static_cast<float>(_frameSize.height()));
240 return r;
241 }
242
243 - (webrtc::DesktopSize)frameSizeToScale:(float)scale {
244 return webrtc::DesktopSize(_frameSize.width() * scale,
245 _frameSize.height() * scale);
246 }
247
248 - (webrtc::DesktopVector)getBoundsForSize:(const webrtc::DesktopSize&)size {
249 webrtc::DesktopVector r(
250 _contentSize.width() - _margin.left - _margin.right - size.width(),
251 _contentSize.height() - _margin.bottom - _margin.top - size.height());
252
253 if (r.x() > 0) {
254 r.set((_contentSize.width() - size.width()) / 2, r.y());
255 }
256
257 if (r.y() > 0) {
258 r.set(r.x(), (_contentSize.height() - size.height()) / 2);
259 }
260
261 return r;
262 }
263
264 - (BOOL)containsTouchPoint:(CGPoint)point {
265 // Here frame is from the top-left corner, most other calculations are framed
266 // from the bottom left.
267 CGRect frame =
268 CGRectMake(_margin.left,
269 _margin.top,
270 _contentSize.width() - _margin.left - _margin.right,
271 _contentSize.height() - _margin.top - _margin.bottom);
272 return CGRectContainsPoint(frame, point);
273 }
274
275 - (BOOL)convertTouchPointToMousePoint:(CGPoint)touchPoint
276 targetPoint:(webrtc::DesktopVector&)mousePoint {
277 if (![self containsTouchPoint:touchPoint]) {
278 return NO;
279 }
280 // A touch location occurs in respect to the user's entire view surface.
281
282 // The GL Context is upside down from the User's perspective so flip it.
283 CGPoint glOrientedTouchPoint =
284 CGPointMake(touchPoint.x, _contentSize.height() - touchPoint.y);
285
286 // The GL surface generally is not at the same origination point as the touch,
287 // so translate by the scene's position.
288 CGPoint glOrientedPointInRespectToFrame =
289 CGPointMake(glOrientedTouchPoint.x - _position.x,
290 glOrientedTouchPoint.y - _position.y);
291
292 // The perspective exists in relative to the CLIENT resolution at 1:1, zoom
293 // our perspective so we are relative to the HOST at 1:1
294 CGPoint glOrientedPointInFrame =
295 CGPointMake(glOrientedPointInRespectToFrame.x / _position.z,
296 glOrientedPointInRespectToFrame.y / _position.z);
297
298 // Finally, flip the perspective back over to the Users, but this time in
299 // respect to the HOST desktop. Floor to ensure the result is always in
300 // frame.
301 CGPoint deskTopOrientedPointInFrame =
302 CGPointMake(floorf(glOrientedPointInFrame.x),
303 floorf(_frameSize.height() - glOrientedPointInFrame.y));
304
305 // Convert from float to integer
306 mousePoint.set(deskTopOrientedPointInFrame.x, deskTopOrientedPointInFrame.y);
307
308 return CGRectContainsPoint(
309 CGRectMake(0, 0, _frameSize.width(), _frameSize.height()),
310 deskTopOrientedPointInFrame);
311 }
312
313 - (BOOL)convertMousePointToTouchPoint:(const webrtc::DesktopVector&)mousePoint
314 targetPoint:(CGPoint&)touchPoint {
315 // A mouse point is in respect to the desktop frame.
316
317 // Flip the perspective back over to the Users, in
318 // respect to the HOST desktop.
319 CGPoint deskTopOrientedPointInFrame =
320 CGPointMake(mousePoint.x(), _frameSize.height() - mousePoint.y());
321
322 // The perspective exists in relative to the CLIENT resolution at 1:1, zoom
323 // our perspective so we are relative to the HOST at 1:1
324 CGPoint glOrientedPointInFrame =
325 CGPointMake(deskTopOrientedPointInFrame.x * _position.z,
326 deskTopOrientedPointInFrame.y * _position.z);
327
328 // The GL surface generally is not at the same origination point as the touch,
329 // so translate by the scene's position.
330 CGPoint glOrientedPointInRespectToFrame =
331 CGPointMake(glOrientedPointInFrame.x + _position.x,
332 glOrientedPointInFrame.y + _position.y);
333
334 // Convert from float to integer
335 touchPoint.x = floorf(glOrientedPointInRespectToFrame.x);
336 touchPoint.y = floorf(glOrientedPointInRespectToFrame.y);
337
338 return [self containsTouchPoint:touchPoint];
339 }
340
341 - (void)panAndZoom:(CGPoint)translation scaleBy:(float)ratio {
342 CGPoint ratios = [self pixelRatio];
343
344 // New Scaling factor bounded by a min and max
345 float resultScale = _position.z * ratio;
346 float scaleUpperBound = MAX(ratios.x, MAX(ratios.y, kMaxZoomSize));
347 float scaleLowerBound = MIN(ratios.x, ratios.y);
348
349 if (resultScale < scaleLowerBound) {
350 resultScale = scaleLowerBound;
351 } else if (resultScale > scaleUpperBound) {
352 resultScale = scaleUpperBound;
353 }
354
355 DCHECK(isnormal(resultScale) && resultScale > 0);
356
357 // The GL perspective is upside down in relation to the User's view, so flip
358 // the translation
359 translation.y = -translation.y;
360
361 // The constants here could be user options later.
362 translation.x =
363 translation.x * kXAxisInversion * (1 / (ratios.x * kMouseSensitivity));
364 translation.y =
365 translation.y * kYAxisInversion * (1 / (ratios.y * kMouseSensitivity));
366
367 CGPoint delta = CGPointMake(0, 0);
368 CGPoint scaleDelta = CGPointMake(0, 0);
369
370 webrtc::DesktopSize currentSize = [self frameSizeToScale:_position.z];
371
372 {
373 // Closure for this variable, so the variable is not available to the rest
374 // of this function
375 webrtc::DesktopVector currentBounds = [self getBoundsForSize:currentSize];
376 // There are rounding errors in the scope of this function, see the
377 // butterfly effect. In successive calls, the resulting position isn't
378 // always exactly the calculated position. If we know we are Anchored, then
379 // go ahead and reposition it to the values above.
380 if (_anchored.right) {
381 _position.x = currentBounds.x();
382 }
383
384 if (_anchored.top) {
385 _position.y = currentBounds.y();
386 }
387 }
388
389 if (_position.z != resultScale) {
390 // When scaling the scene, the origination of scaling is the mouse's
391 // location. But when the frame is anchored, adjust the origination to the
392 // anchor point.
393
394 CGPoint mousePositionInClientResolution;
395 [self convertMousePointToTouchPoint:_mousePosition
396 targetPoint:mousePositionInClientResolution];
397
398 // Prefer to zoom based on the left anchor when there is a choice
399 if (_anchored.left) {
400 mousePositionInClientResolution.x = 0;
401 } else if (_anchored.right) {
402 mousePositionInClientResolution.x = _contentSize.width();
403 }
404
405 // Prefer to zoom out from the top anchor when there is a choice
406 if (_anchored.top) {
407 mousePositionInClientResolution.y = _contentSize.height();
408 } else if (_anchored.bottom) {
409 mousePositionInClientResolution.y = 0;
410 }
411
412 scaleDelta.x -=
413 [SceneView positionDeltaFromScaling:ratio
414 position:_position.x
415 length:currentSize.width()
416 anchor:mousePositionInClientResolution.x];
417
418 scaleDelta.y -=
419 [SceneView positionDeltaFromScaling:ratio
420 position:_position.y
421 length:currentSize.height()
422 anchor:mousePositionInClientResolution.y];
423 }
424
425 delta.x = [SceneView
426 positionDeltaFromTranslation:translation.x
427 position:_position.x
428 freeSpace:_contentSize.width() - currentSize.width()
429 scaleingPositionDelta:scaleDelta.x
430 isAnchoredLow:_anchored.left
431 isAnchoredHigh:_anchored.right];
432
433 delta.y = [SceneView
434 positionDeltaFromTranslation:translation.y
435 position:_position.y
436 freeSpace:_contentSize.height() - currentSize.height()
437 scaleingPositionDelta:scaleDelta.y
438 isAnchoredLow:_anchored.bottom
439 isAnchoredHigh:_anchored.top];
440 {
441 // Closure for this variable, so the variable is not available to the rest
442 // of this function
443 webrtc::DesktopVector bounds =
444 [self getBoundsForSize:[self frameSizeToScale:resultScale]];
445
446 delta.x = [SceneView boundDeltaFromPosition:_position.x
447 delta:delta.x
448 lowerBound:bounds.x()
449 upperBound:0];
450
451 delta.y = [SceneView boundDeltaFromPosition:_position.y
452 delta:delta.y
453 lowerBound:bounds.y()
454 upperBound:0];
455 }
456
457 BOOL isLeftAndRightAnchored = _anchored.left && _anchored.right;
458 BOOL isTopAndBottomAnchored = _anchored.top && _anchored.bottom;
459
460 [self updateMousePositionAndAnchorsWithTranslation:translation
461 scale:resultScale];
462
463 // If both anchors were lost, then keep the one that is easier to predict
464 if (isLeftAndRightAnchored && !_anchored.left && !_anchored.right) {
465 delta.x = -_position.x;
466 _anchored.left = YES;
467 }
468
469 // If both anchors were lost, then keep the one that is easier to predict
470 if (isTopAndBottomAnchored && !_anchored.top && !_anchored.bottom) {
471 delta.y = -_position.y;
472 _anchored.bottom = YES;
473 }
474
475 // FINALLY, update the scene's position
476 _position.x += delta.x;
477 _position.y += delta.y;
478 _position.z = resultScale;
479 }
480
481 - (void)updateMousePositionAndAnchorsWithTranslation:(CGPoint)translation
482 scale:(float)scale {
483 webrtc::DesktopVector centerMouseLocation;
484 [self convertTouchPointToMousePoint:CGPointMake(_contentSize.width() / 2,
485 _contentSize.height() / 2)
486 targetPoint:centerMouseLocation];
487
488 webrtc::DesktopVector currentBounds =
489 [self getBoundsForSize:[self frameSizeToScale:_position.z]];
490 webrtc::DesktopVector nextBounds =
491 [self getBoundsForSize:[self frameSizeToScale:scale]];
492
493 webrtc::DesktopVector predictedMousePosition(
494 _mousePosition.x() - translation.x, _mousePosition.y() + translation.y);
495
496 _mousePosition.set(
497 [SceneView boundMouseGivenNextPosition:predictedMousePosition.x()
498 maxPosition:_frameSize.width()
499 centerPosition:centerMouseLocation.x()
500 isAnchoredLow:_anchored.left
501 isAnchoredHigh:_anchored.right],
502 [SceneView boundMouseGivenNextPosition:predictedMousePosition.y()
503 maxPosition:_frameSize.height()
504 centerPosition:centerMouseLocation.y()
505 isAnchoredLow:_anchored.top
506 isAnchoredHigh:_anchored.bottom]);
507
508 _panVelocity.x = [SceneView boundVelocity:_panVelocity.x
509 axisLength:_frameSize.width()
510 mousePosition:_mousePosition.x()];
511 _panVelocity.y = [SceneView boundVelocity:_panVelocity.y
512 axisLength:_frameSize.height()
513 mousePosition:_mousePosition.y()];
514
515 _anchored.left = (nextBounds.x() >= 0) ||
516 (_position.x == 0 &&
517 predictedMousePosition.x() <= centerMouseLocation.x());
518
519 _anchored.right =
520 (nextBounds.x() >= 0) ||
521 (_position.x == currentBounds.x() &&
522 predictedMousePosition.x() >= centerMouseLocation.x()) ||
523 (_mousePosition.x() == _frameSize.width() - 1 && !_anchored.left);
524
525 _anchored.bottom = (nextBounds.y() >= 0) ||
526 (_position.y == 0 &&
527 predictedMousePosition.y() >= centerMouseLocation.y());
528
529 _anchored.top =
530 (nextBounds.y() >= 0) ||
531 (_position.y == currentBounds.y() &&
532 predictedMousePosition.y() <= centerMouseLocation.y()) ||
533 (_mousePosition.y() == _frameSize.height() - 1 && !_anchored.bottom);
534 }
535
536 + (float)positionDeltaFromScaling:(float)ratio
537 position:(float)position
538 length:(float)length
539 anchor:(float)anchor {
540 float newSize = length * ratio;
541 float scaleXBy = fabs(position - anchor) / length;
542 float delta = (newSize - length) * scaleXBy;
543 return delta;
544 }
545
546 + (int)positionDeltaFromTranslation:(int)translation
547 position:(int)position
548 freeSpace:(int)freeSpace
549 scaleingPositionDelta:(int)scaleingPositionDelta
550 isAnchoredLow:(BOOL)isAnchoredLow
551 isAnchoredHigh:(BOOL)isAnchoredHigh {
552 if (isAnchoredLow && isAnchoredHigh) {
553 // center the view
554 return (freeSpace / 2) - position;
555 } else if (isAnchoredLow) {
556 return 0;
557 } else if (isAnchoredHigh) {
558 return scaleingPositionDelta;
559 } else {
560 return translation + scaleingPositionDelta;
561 }
562 }
563
564 + (int)boundDeltaFromPosition:(float)position
565 delta:(int)delta
566 lowerBound:(int)lowerBound
567 upperBound:(int)upperBound {
568 int result = position + delta;
569
570 if (lowerBound < upperBound) { // the view is larger than the bounds
571 if (result > upperBound) {
572 result = upperBound;
573 } else if (result < lowerBound) {
574 result = lowerBound;
575 }
576 } else {
577 // the view is smaller than the bounds so we'll always be at the lowerBound
578 result = lowerBound;
579 }
580 return result - position;
581 }
582
583 + (int)boundMouseGivenNextPosition:(int)nextPosition
584 maxPosition:(int)maxPosition
585 centerPosition:(int)centerPosition
586 isAnchoredLow:(BOOL)isAnchoredLow
587 isAnchoredHigh:(BOOL)isAnchoredHigh {
588 if (nextPosition < 0) {
589 return 0;
590 }
591 if (nextPosition > maxPosition - 1) {
592 return maxPosition - 1;
593 }
594
595 if ((isAnchoredLow && nextPosition <= centerPosition) ||
596 (isAnchoredHigh && nextPosition >= centerPosition)) {
597 return nextPosition;
598 }
599
600 return centerPosition;
601 }
602
603 + (float)boundVelocity:(float)velocity
604 axisLength:(int)axisLength
605 mousePosition:(int)mousePosition {
606 if (velocity != 0) {
607 if (mousePosition <= 0 || mousePosition >= (axisLength - 1)) {
608 return 0;
609 }
610 }
611
612 return velocity;
613 }
614
615 - (BOOL)tickPanVelocity {
616 BOOL inMotion = ((_panVelocity.x != 0.0) || (_panVelocity.y != 0.0));
617
618 if (inMotion) {
619
620 uint32_t divisor = 50 / _position.z;
621 float reducer = .95;
622
623 if (_panVelocity.x != 0.0 && ABS(_panVelocity.x) < divisor) {
624 _panVelocity = CGPointMake(0.0, _panVelocity.y);
625 }
626
627 if (_panVelocity.y != 0.0 && ABS(_panVelocity.y) < divisor) {
628 _panVelocity = CGPointMake(_panVelocity.x, 0.0);
629 }
630
631 [self panAndZoom:CGPointMake(_panVelocity.x / divisor,
632 _panVelocity.y / divisor)
633 scaleBy:1.0];
634
635 _panVelocity.x *= reducer;
636 _panVelocity.y *= reducer;
637 }
638
639 return inMotion;
640 }
641
642 @end
OLDNEW
« no previous file with comments | « remoting/ios/ui/scene_view.h ('k') | remoting/ios/ui/scene_view_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698