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

Side by Side Diff: client/touch/Scroller.dart

Issue 8363040: Implement measurement using futures (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: take2 Created 9 years, 1 month 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * Implementation of a custom scrolling behavior. 6 * Implementation of a custom scrolling behavior.
7 * This behavior overrides native scrolling for an area. This area can be a 7 * This behavior overrides native scrolling for an area. This area can be a
8 * single defined part of a page, the entire page, or several different parts 8 * single defined part of a page, the entire page, or several different parts
9 * of a page. 9 * of a page.
10 * 10 *
(...skipping 12 matching lines...) Expand all
23 * positioning (setting the left and top styles). 23 * positioning (setting the left and top styles).
24 * 24 *
25 * For this to work properly you need to set -webkit-text-size-adjust to 'none' 25 * For this to work properly you need to set -webkit-text-size-adjust to 'none'
26 * on an ancestor element of the frame, or on the frame itself. If you forget 26 * on an ancestor element of the frame, or on the frame itself. If you forget
27 * this you may see the text content of the scrollable area changing size as it 27 * this you may see the text content of the scrollable area changing size as it
28 * moves. 28 * moves.
29 * 29 *
30 * The behavior is intended to support vertical and horizontal scrolling, and 30 * The behavior is intended to support vertical and horizontal scrolling, and
31 * scrolling with momentum when a touch gesture flicks with enough velocity. 31 * scrolling with momentum when a touch gesture flicks with enough velocity.
32 */ 32 */
33 typedef void Callback();
34
35 // Helper method to await the completion of 2 futures.
36 void joinFutures(List<Future> futures, Callback callback) {
37 int count = 0;
38 int len = futures.length;
39 void helper(value) {
40 count++;
41 if (count == len) {
42 callback();
43 }
44 }
45 for (Future p in futures) {
46 p.then(helper);
47 }
48 }
33 49
34 class Scroller implements Draggable, MomentumDelegate { 50 class Scroller implements Draggable, MomentumDelegate {
35 51
36 /** Pixels to move each time an arrow key is pressed. */ 52 /** Pixels to move each time an arrow key is pressed. */
37 static final ARROW_KEY_DELTA = 30; 53 static final ARROW_KEY_DELTA = 30;
38 static final SCROLL_WHEEL_VELOCITY = 0.01; 54 static final SCROLL_WHEEL_VELOCITY = 0.01;
39 static final FAST_SNAP_DECELERATION_FACTOR = 0.84; 55 static final FAST_SNAP_DECELERATION_FACTOR = 0.84;
40 static final PAGE_KEY_SCROLL_FRACTION = .85; 56 static final PAGE_KEY_SCROLL_FRACTION = .85;
41 57
42 // TODO(jacobr): remove this static variable. 58 // TODO(jacobr): remove this static variable.
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 Function _setOffsetFunction; 121 Function _setOffsetFunction;
106 /** 122 /**
107 * Function that returns the content size that can be specified instead of 123 * Function that returns the content size that can be specified instead of
108 * querying the DOM. 124 * querying the DOM.
109 */ 125 */
110 Function _lookupContentSizeDelegate; 126 Function _lookupContentSizeDelegate;
111 127
112 Size _scrollSize; 128 Size _scrollSize;
113 Size _contentSize; 129 Size _contentSize;
114 Coordinate _minPoint; 130 Coordinate _minPoint;
115 bool _activeTransition = false;
116 bool _isStopping = false; 131 bool _isStopping = false;
117 Coordinate _contentStartOffset; 132 Coordinate _contentStartOffset;
118 bool _started = false; 133 bool _started = false;
119 bool _activeGesture = false; 134 bool _activeGesture = false;
120 ScrollWatcher _scrollWatcher; 135 ScrollWatcher _scrollWatcher;
121 136
122 Scroller(Element scrollableElem, [this.verticalEnabled = false, 137 Scroller(Element scrollableElem, [this.verticalEnabled = false,
123 this.horizontalEnabled = false, 138 this.horizontalEnabled = false,
124 this._momentumEnabled = true, 139 this._momentumEnabled = true,
125 this._lookupContentSizeDelegate = null, 140 this._lookupContentSizeDelegate = null,
126 num defaultDecelerationFactor = 1, 141 num defaultDecelerationFactor = 1,
127 int scrollTechnique = null, bool capture = false]) 142 int scrollTechnique = null, bool capture = false])
128 : _element = scrollableElem, 143 : _element = scrollableElem,
129 _frame = scrollableElem.parent, 144 _frame = scrollableElem.parent,
130 _scrollTechnique = scrollTechnique !== null 145 _scrollTechnique = scrollTechnique !== null
131 ? scrollTechnique : ScrollerScrollTechnique.TRANSFORM_3D, 146 ? scrollTechnique : ScrollerScrollTechnique.TRANSFORM_3D,
132 _minPoint = new Coordinate(0, 0), 147 _minPoint = new Coordinate(0, 0),
133 _maxPoint = new Coordinate(0, 0), 148 _maxPoint = new Coordinate(0, 0),
134 _maxOffset = new Coordinate(0, 0), 149 _maxOffset = new Coordinate(0, 0),
135 _minOffset = new Coordinate(0, 0), 150 _minOffset = new Coordinate(0, 0),
136 _contentOffset = new Coordinate(0, 0) { 151 _contentOffset = new Coordinate(0, 0) {
137 _touchHandler = new TouchHandler(this, scrollableElem.parent); 152 _touchHandler = new TouchHandler(this, scrollableElem.parent);
138 _momentum = new Momentum(this, defaultDecelerationFactor); 153 _momentum = new Momentum(this, defaultDecelerationFactor);
139 154
140 Element parentElem = scrollableElem.parent; 155 Element parentElem = scrollableElem.parent;
141 assert(parentElem != null); 156 assert(parentElem != null);
142 _setOffsetFunction = _getOffsetFunction(_scrollTechnique); 157 _setOffsetFunction = _getOffsetFunction(_scrollTechnique);
143 _element.on.transitionEnd.add((event) { onTransitionEnd(event); });
144 _touchHandler.setDraggable(this); 158 _touchHandler.setDraggable(this);
145 _touchHandler.enable(capture); 159 _touchHandler.enable(capture);
146 160
147 _frame.on.mouseWheel.add((e) { 161 _frame.on.mouseWheel.add((e) {
148 if (e.wheelDeltaY != 0 && verticalEnabled || 162 if (e.wheelDeltaY != 0 && verticalEnabled ||
149 e.wheelDeltaX != 0 && horizontalEnabled) { 163 e.wheelDeltaX != 0 && horizontalEnabled) {
150 num x = horizontalEnabled ? e.wheelDeltaX : 0; 164 num x = horizontalEnabled ? e.wheelDeltaX : 0;
151 num y = verticalEnabled ? e.wheelDeltaY : 0; 165 num y = verticalEnabled ? e.wheelDeltaY : 0;
152 if (throwDelta(x, y, FAST_SNAP_DECELERATION_FACTOR)) { 166 throwDelta(x, y, FAST_SNAP_DECELERATION_FACTOR);
153 e.preventDefault(); 167 e.preventDefault();
154 }
155 } 168 }
156 }); 169 });
157 170
158 _frame.on.keyDown.add((KeyboardEvent e) { 171 _frame.on.keyDown.add((KeyboardEvent e) {
159 bool handled = false; 172 bool handled = false;
160 // We ignore key events where further scrolling in that direction 173 // We ignore key events where further scrolling in that direction
161 // would have no impact which matches default browser behavior with 174 // would have no impact which matches default browser behavior with
162 // nested scrollable areas. 175 // nested scrollable areas.
163 176
164 switch(e.keyCode) { 177 switch(e.keyCode) {
165 case 33: // page-up 178 case 33: // page-up
166 handled = throwDelta( 179 throwDelta(
167 0, 180 0,
168 _scrollSize.height * PAGE_KEY_SCROLL_FRACTION); 181 _scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
182 handled = true;
169 break; 183 break;
170 case 34: // page-down 184 case 34: // page-down
171 handled = throwDelta( 185 throwDelta(
172 0, -_scrollSize.height * PAGE_KEY_SCROLL_FRACTION); 186 0, -_scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
187 handled = true;
173 break; 188 break;
174 case 35: // End 189 case 35: // End
175 handled = throwTo(_maxPoint.x, _minPoint.y, 190 throwTo(_maxPoint.x, _minPoint.y,
176 FAST_SNAP_DECELERATION_FACTOR); 191 FAST_SNAP_DECELERATION_FACTOR);
192 handled = true;
177 break; 193 break;
178 case 36: // Home 194 case 36: // Home
179 handled = throwTo(_maxPoint.x,_maxPoint.y, 195 throwTo(_maxPoint.x,_maxPoint.y,
180 FAST_SNAP_DECELERATION_FACTOR); 196 FAST_SNAP_DECELERATION_FACTOR);
197 handled = true;
181 break; 198 break;
182 /* TODO(jacobr): enable arrow keys when the don't conflict with other 199 /* TODO(jacobr): enable arrow keys when the don't conflict with other
183 application keyboard shortcuts. 200 application keyboard shortcuts.
184 case 38: // up 201 case 38: // up
185 handled = throwDelta( 202 handled = throwDelta(
186 0, 203 0,
187 ARROW_KEY_DELTA, 204 ARROW_KEY_DELTA,
188 FAST_SNAP_DECELERATION_FACTOR); 205 FAST_SNAP_DECELERATION_FACTOR);
189 break; 206 break;
190 case 40: // down 207 case 40: // down
(...skipping 12 matching lines...) Expand all
203 0, 220 0,
204 FAST_SNAP_DECELERATION_FACTOR); 221 FAST_SNAP_DECELERATION_FACTOR);
205 break; 222 break;
206 */ 223 */
207 } 224 }
208 if (handled) { 225 if (handled) {
209 e.preventDefault(); 226 e.preventDefault();
210 } 227 }
211 }); 228 });
212 // The scrollable element must be relatively positioned. 229 // The scrollable element must be relatively positioned.
213 assert(_scrollTechnique != ScrollerScrollTechnique.RELATIVE_POSITIONING || 230 // TODO(jacobr): this assert fires asynchronously which could be confusing.
214 window.getComputedStyle(_element, null).position != "static"); 231 if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) {
232 _element.computedStyle.then((CSSStyleDeclaration style) {
233 assert(style.position != "static");
234 });
235 }
236
215 _initLayer(); 237 _initLayer();
216 } 238 }
217 239
218 EventListenerList get onScrollerStart() { 240 EventListenerList get onScrollerStart() {
219 if (_onScrollerStart === null) { 241 if (_onScrollerStart === null) {
220 _onScrollerStart = new SimpleEventListenerList(); 242 _onScrollerStart = new SimpleEventListenerList();
221 } 243 }
222 return _onScrollerStart; 244 return _onScrollerStart;
223 } 245 }
224 246
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
291 } 313 }
292 return end; 314 return end;
293 } 315 }
294 316
295 Coordinate get contentOffset() => _contentOffset; 317 Coordinate get contentOffset() => _contentOffset;
296 318
297 /** 319 /**
298 * Animate the position of the scroller to the specified [x], [y] coordinates 320 * Animate the position of the scroller to the specified [x], [y] coordinates
299 * by applying the throw gesture with the correct velocity to end at that 321 * by applying the throw gesture with the correct velocity to end at that
300 * location. 322 * location.
301 * Return false if no delta needs to be applied.
302 */ 323 */
303 bool throwTo(num x, num y, 324 void throwTo(num x, num y, [num decelerationFactor = null]) {
304 [num decelerationFactor = null]) { 325 reconfigure(() {
305 reconfigure(); 326 final snappedTarget = _snapToBounds(x, y);
306 final snappedTarget = _snapToBounds(x, y); 327 // If a deceleration factor is not specified, use the existing
307 // If a deceleration factor is not specified, use the existing 328 // deceleration factor specified by the momentum simulator.
308 // deceleration factor specified by the momentum simulator. 329 if (decelerationFactor == null) {
309 if (decelerationFactor == null) { 330 decelerationFactor = _momentum.decelerationFactor;
310 decelerationFactor = _momentum.decelerationFactor; 331 }
311 }
312 332
313 if (snappedTarget != currentTarget) { 333 if (snappedTarget != currentTarget) {
314 _momentum.abort(); 334 _momentum.abort();
315 reconfigure();
316 335
317 _startDeceleration( 336 _startDeceleration(
318 _momentum.calculateVelocity( 337 _momentum.calculateVelocity(
319 _contentOffset, 338 _contentOffset,
320 snappedTarget, 339 snappedTarget,
321 decelerationFactor), 340 decelerationFactor),
322 decelerationFactor); 341 decelerationFactor);
323 onDecelStart.dispatch( 342 onDecelStart.dispatch(
324 EventUtil.createEvent(ScrollerEventType.DECEL_START)); 343 EventUtil.createEvent(ScrollerEventType.DECEL_START));
325 return true; 344 }
326 } else { 345 });
327 return false;
328 }
329 } 346 }
330 347
331 bool throwDelta(num deltaX, num deltaY, [num decelerationFactor = null]) { 348 void throwDelta(num deltaX, num deltaY, [num decelerationFactor = null]) {
332 Coordinate start = _contentOffset; 349 Coordinate start = _contentOffset;
333 Coordinate end = currentTarget; 350 Coordinate end = currentTarget;
334 int x = end.x.toInt(); 351 int x = end.x.toInt();
335 int y = end.y.toInt(); 352 int y = end.y.toInt();
336 // If we are throwing in the opposite direction of the existing momentum, 353 // If we are throwing in the opposite direction of the existing momentum,
337 // cancel the current momentum. 354 // cancel the current momentum.
338 if (deltaX != 0 && deltaX.isNegative() != (end.x - start.x).isNegative()) { 355 if (deltaX != 0 && deltaX.isNegative() != (end.x - start.x).isNegative()) {
339 x = start.x; 356 x = start.x;
340 } 357 }
341 if (deltaY != 0 && deltaY.isNegative() != (end.y - start.y).isNegative()) { 358 if (deltaY != 0 && deltaY.isNegative() != (end.y - start.y).isNegative()) {
342 y = start.y; 359 y = start.y;
343 } 360 }
344 x += deltaX.toInt(); 361 x += deltaX.toInt();
345 y += deltaY.toInt(); 362 y += deltaY.toInt();
346 return throwTo(x, y, decelerationFactor); 363 throwTo(x, y, decelerationFactor);
347 }
348
349 void animateTo(num x, num y, [num duration = null,
350 String timingFunction = null]) {
351 if (duration !== null && duration != 0
352 && _scrollTechnique == ScrollerScrollTechnique.TRANSFORM_3D) {
353 _setWebkitTransition(_element, duration, StyleUtil.TRANSFORM_STYLE,
354 timingFunction);
355 }
356 _setContentOffset(x, y);
357 } 364 }
358 365
359 void setPosition(num x, num y) { 366 void setPosition(num x, num y) {
360 _momentum.abort(); 367 _momentum.abort();
361 _contentOffset.x = x; 368 _contentOffset.x = x;
362 _contentOffset.y = y; 369 _contentOffset.y = y;
363 _snapContentOffsetToBounds(); 370 _snapContentOffsetToBounds();
364 _setContentOffset(_contentOffset.x, _contentOffset.y); 371 _setContentOffset(_contentOffset.x, _contentOffset.y);
365 } 372 }
366 /** 373 /**
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
412 * Initialize the dom elements necessary for the scrolling to work. 419 * Initialize the dom elements necessary for the scrolling to work.
413 */ 420 */
414 void _initLayer() { 421 void _initLayer() {
415 // The scrollable node provided to Scroller must be a direct child 422 // The scrollable node provided to Scroller must be a direct child
416 // of the scrollable frame. 423 // of the scrollable frame.
417 // TODO(jacobr): Figure out why this is failing on dartium. 424 // TODO(jacobr): Figure out why this is failing on dartium.
418 // assert(_element.parent == _frame); 425 // assert(_element.parent == _frame);
419 _setContentOffset(_maxPoint.x, _maxPoint.y); 426 _setContentOffset(_maxPoint.x, _maxPoint.y);
420 } 427 }
421 428
422 void onDecelerate(num x, num y, [num duration = 0, 429 void onDecelerate(num x, num y) {
423 String timingFunction = null]) { 430 _setContentOffset(x, y);
424 // TODO(jacobr): remove once dartc fixes default args.
425 if (duration === null) {
426 duration = 0;
427 }
428 animateTo(x, y, duration, timingFunction);
429 } 431 }
430 432
431 void onDecelerationEnd() { 433 void onDecelerationEnd() {
432 _setWebkitTransition(_element, 0);
433 onScrollerEnd.dispatch( 434 onScrollerEnd.dispatch(
434 EventUtil.createEvent(ScrollerEventType.SCROLLER_END)); 435 EventUtil.createEvent(ScrollerEventType.SCROLLER_END));
435 _started = false; 436 _started = false;
436 } 437 }
437 438
438 void onDragEnd() { 439 void onDragEnd() {
439 _dragInProgress = false; 440 _dragInProgress = false;
440 441
441 bool decelerating = false; 442 bool decelerating = false;
442 if (_activeGesture) { 443 if (_activeGesture) {
443 if (_momentumEnabled && !_activeTransition) { 444 if (_momentumEnabled) {
444 decelerating = _startDeceleration(_touchHandler.getEndVelocity()); 445 decelerating = _startDeceleration(_touchHandler.getEndVelocity());
445 } 446 }
446 } 447 }
447 448
448 onScrollerDragEnd.dispatch( 449 onScrollerDragEnd.dispatch(
449 EventUtil.createEvent(ScrollerEventType.DRAG_END)); 450 EventUtil.createEvent(ScrollerEventType.DRAG_END));
450 451
451 if (!decelerating) { 452 if (!decelerating) {
452 _snapContentOffsetToBounds(); 453 _snapContentOffsetToBounds();
453 onScrollerEnd.dispatch( 454 onScrollerEnd.dispatch(
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 return !!(shouldVertical || shouldHorizontal && !verticalish); 497 return !!(shouldVertical || shouldHorizontal && !verticalish);
497 } 498 }
498 499
499 void onTouchEnd() { 500 void onTouchEnd() {
500 } 501 }
501 502
502 /** 503 /**
503 * Prepare the scrollable area for possible movement. 504 * Prepare the scrollable area for possible movement.
504 */ 505 */
505 bool onTouchStart(TouchEvent e) { 506 bool onTouchStart(TouchEvent e) {
506 reconfigure(); 507 reconfigure(() {
507 final touch = e.touches[0]; 508 final touch = e.touches[0];
508 if (_momentum.decelerating) { 509 if (_momentum.decelerating) {
509 e.preventDefault(); 510 e.preventDefault();
510 e.stopPropagation(); 511 e.stopPropagation();
511 stop(); 512 stop();
512 } else { 513 }
513 _setWebkitTransition(_element, 0); 514 _contentStartOffset = _contentOffset.clone();
514 } 515 _snapContentOffsetToBounds();
515 _contentStartOffset = _contentOffset.clone(); 516 });
516 _snapContentOffsetToBounds();
517 return true; 517 return true;
518 } 518 }
519 519
520 /** 520 /**
521 * Transition end event handler.
522 */
523 void onTransitionEnd(Event e) {
524 if (e.target == _element) {
525 _activeTransition = false;
526 _momentum.onTransitionEnd();
527 }
528 }
529
530 /**
531 * Recalculate dimensions of the frame and the content. Adjust the minPoint 521 * Recalculate dimensions of the frame and the content. Adjust the minPoint
532 * and maxPoint allowed for scrolling and scroll to a valid position. Call 522 * and maxPoint allowed for scrolling and scroll to a valid position. Call
533 * this method if you know the frame or content has been updated. Called 523 * this method if you know the frame or content has been updated. Called
534 * internally on every touchstart event the frame receives. 524 * internally on every touchstart event the frame receives.
535 */ 525 */
536 void reconfigure() { 526 void reconfigure(Callback callback) {
537 _resize(); 527 _resize(() {
538 _snapContentOffsetToBounds(); 528 _snapContentOffsetToBounds();
529 callback();
530 });
539 } 531 }
540 532
541 void reset() { 533 void reset() {
542 stop(); 534 stop();
543 _touchHandler.reset(); 535 _touchHandler.reset();
544 _setWebkitTransition(_element, 0); 536 _maxOffset.x = 0;
545 setMinOffset(0, 0); 537 _maxOffset.y = 0;
546 setMaxOffset(0, 0); 538 _minOffset.x = 0;
547 reconfigure(); 539 _minOffset.y = 0;
548 _setContentOffset(_maxPoint.x, _maxPoint.y); 540 reconfigure(() => _setContentOffset(_maxPoint.x, _maxPoint.y));
549 } 541 }
550 542
551 /** 543 /**
552 * Recalculate dimensions of the frame and the content. Adjust the minPoint 544 * Recalculate dimensions of the frame and the content. Adjust the minPoint
553 * and maxPoint allowed for scrolling. 545 * and maxPoint allowed for scrolling.
554 */ 546 */
555 void _resize() { 547 void _resize(Callback callback) {
arv (Not doing code reviews) 2011/10/27 05:50:24 Maybe this should just return a future instead?
Jacob 2011/10/27 20:59:25 What would it return a future of? Future<void> see
556 _scrollSize = new Size(_frame.offsetWidth, _frame.offsetHeight); 548 final frameRect = _frame.rect;
557 _contentSize = _lookupContentSizeDelegate !== null ? 549 Future contentSizeFuture;
558 _lookupContentSizeDelegate() : 550
559 new Size(_element.scrollWidth, _element.scrollHeight); 551 if (_lookupContentSizeDelegate !== null) {
560 Size adjusted = _getAdjustedContentSize(); 552 contentSizeFuture = _lookupContentSizeDelegate();
561 _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y); 553 contentSizeFuture.then((Size size) {
562 _minPoint = new Coordinate( 554 _contentSize = size;
563 Math.min( 555 });
564 _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x), 556 } else {
565 Math.min( 557 contentSizeFuture = _element.rect;
566 _scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y)); 558 contentSizeFuture.then((ElementRect rect) {
559 _contentSize = new Size(rect.scroll.width, rect.scroll.height);
560 });
561 }
562
563 joinFutures(<Future>[frameRect, contentSizeFuture], () {
564 _scrollSize = new Size(frameRect.value.offset.width,
565 frameRect.value.offset.height);
566 Size adjusted = _getAdjustedContentSize();
567 _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y);
568 _minPoint = new Coordinate(
569 Math.min(
570 _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x),
571 Math.min(
572 _scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y)) ;
573 callback();
574 });
567 } 575 }
568 576
569 Coordinate _snapToBounds(num x, num y) { 577 Coordinate _snapToBounds(num x, num y) {
570 num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x); 578 num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x);
571 num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y); 579 num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y);
572 return new Coordinate(clampX, clampY); 580 return new Coordinate(clampX, clampY);
573 } 581 }
574 582
575 /** 583 /**
576 * Translate the content to a new position specified in px. 584 * Translate the content to a new position specified in px.
577 */ 585 */
578 void _setContentOffset(num x, num y) { 586 void _setContentOffset(num x, num y) {
579 _contentOffset.x = x; 587 _contentOffset.x = x;
580 _contentOffset.y = y; 588 _contentOffset.y = y;
581 _setOffsetFunction(_element, x, y); 589 _setOffsetFunction(_element, x, y);
582 onContentMoved.dispatch( 590 onContentMoved.dispatch(
583 EventUtil.createEvent(ScrollerEventType.CONTENT_MOVED)); 591 EventUtil.createEvent(ScrollerEventType.CONTENT_MOVED));
584 } 592 }
585 593
586 /** 594 /**
587 * Sets the offset to subtract from the maximum coordinate that the left upper
588 * corner of the content can scroll to.
589 */
590 void setMaxOffset(num x, num y) {
591 _maxOffset.x = x;
592 _maxOffset.y = y;
593 _resize();
594 }
595
596 /**
597 * Sets the offset to add to the minimum coordinate that the left upper corner
598 * of the content can scroll to.
599 */
600 void setMinOffset(num x, num y) {
601 _minOffset.x = x;
602 _minOffset.y = y;
603 _resize();
604 }
605
606 /**
607 * Enable or disable momentum. 595 * Enable or disable momentum.
608 */ 596 */
609 void setMomentum(bool enable) { 597 void setMomentum(bool enable) {
610 _momentumEnabled = enable; 598 _momentumEnabled = enable;
611 } 599 }
612 600
613 /** 601 /**
614 * Update the scroll technique used for animating the scrollable area.
615 */
616 void setScrollTechnique(int technique) {
617 _scrollTechnique = technique;
618 _setOffsetFunction = _getOffsetFunction(technique);
619
620 // The scrollable element must be relatively positioned.
621 assert(technique != ScrollerScrollTechnique.RELATIVE_POSITIONING ||
622 window.getComputedStyle(_element, null).position != "static");
623
624 if (technique != ScrollerScrollTechnique.TRANSFORM_3D) {
625 FxUtil.clearWebkitTransition(_element);
626 FxUtil.clearWebkitTransform(_element);
627 }
628 if (technique != ScrollerScrollTechnique.RELATIVE_POSITIONING) {
629 FxUtil.setLeftAndTop(_element, 0, 0);
630 }
631 _setOffsetFunction(_element, _contentOffset.x, _contentOffset.y);
632 }
633
634 /**
635 * Sets the vertical scrolled offset of the element where [y] is the amount 602 * Sets the vertical scrolled offset of the element where [y] is the amount
636 * of vertical space to be scrolled, in pixels. 603 * of vertical space to be scrolled, in pixels.
637 */ 604 */
638 void setVerticalOffset(num y) { 605 void setVerticalOffset(num y) {
639 _setContentOffset(_contentOffset.x, y); 606 _setContentOffset(_contentOffset.x, y);
640 } 607 }
641 608
642 /** 609 /**
643 * Applies a webkit transition on the element where the [duration] of the
644 * animation is specified in ms.
645 */
646 void _setWebkitTransition(Element el, num duration, [String property = null,
647 String timingFunction = null]) {
648 _activeTransition = duration > 0;
649 FxUtil.setWebkitTransition(el, duration, property, timingFunction);
650 }
651
652 /**
653 * Whether the scrollable area should scroll horizontally. Only 610 * Whether the scrollable area should scroll horizontally. Only
654 * returns true if the client has enabled horizontal scrolling, and the 611 * returns true if the client has enabled horizontal scrolling, and the
655 * content is wider than the frame. 612 * content is wider than the frame.
656 */ 613 */
657 bool _shouldScrollHorizontally() { 614 bool _shouldScrollHorizontally() {
658 return horizontalEnabled && _scrollSize.width < _contentSize.width; 615 return horizontalEnabled && _scrollSize.width < _contentSize.width;
659 } 616 }
660 617
661 /** 618 /**
662 * Whether the scrollable area should scroll vertically. Only 619 * Whether the scrollable area should scroll vertically. Only
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
696 if (!_shouldScrollVertically()) { 653 if (!_shouldScrollVertically()) {
697 velocity.y = 0; 654 velocity.y = 0;
698 } 655 }
699 assert(_minPoint != null); // Min point is not set 656 assert(_minPoint != null); // Min point is not set
700 assert(_maxPoint != null); // Max point is not set 657 assert(_maxPoint != null); // Max point is not set
701 return _momentum.start(velocity, _minPoint, _maxPoint, _contentOffset, 658 return _momentum.start(velocity, _minPoint, _maxPoint, _contentOffset,
702 decelerationFactor); 659 decelerationFactor);
703 } 660 }
704 661
705 Coordinate stop() { 662 Coordinate stop() {
706 Coordinate velocity = _momentum.stop(); 663 return _momentum.stop();
707
708 if (_momentum.decelerating) {
709 CSSMatrix transform1 = StyleUtil.getCurrentTransformMatrix(
710 _element);
711 if (!_activeTransition) {
712 _stopDecelerating(transform1.m41, transform1.m42);
713 return velocity;
714 }
715 _contentOffset.x = transform1.m41;
716 _contentOffset.y = transform1.m42;
717 _isStopping = true;
718 window.setTimeout(() {
719 CSSMatrix transform2 = StyleUtil.getCurrentTransformMatrix(
720 _element);
721 _setWebkitTransition(_element, 0);
722 window.setTimeout(function() { _isStopping = false; }, 0);
723 num deltaX = transform2.m41 - transform1.m41;
724 num deltaY = transform2.m42 - transform1.m42;
725 num newX = transform2.m41 + 2 * deltaX;
726 num newY = transform2.m42 + 2 * deltaY;
727 newX = GoogleMath.clamp(newX, _minPoint.x, _maxPoint.x);
728 newY = GoogleMath.clamp(newY, _minPoint.y, _maxPoint.y);
729 _stopDecelerating(newX, newY);
730 }, 0);
731 }
732 return velocity;
733 } 664 }
734 665
735 /** 666 /**
736 * Stop the deceleration of the scrollable content given a new position in px. 667 * Stop the deceleration of the scrollable content given a new position in px.
737 */ 668 */
738 void _stopDecelerating(num x, num y) { 669 void _stopDecelerating(num x, num y) {
739 _momentum.stop(); 670 _momentum.stop();
740 _setContentOffset(x, y); 671 _setContentOffset(x, y);
741 } 672 }
742 673
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
796 for (EventListener listener in _listeners) { 727 for (EventListener listener in _listeners) {
797 listener(evt); 728 listener(evt);
798 } 729 }
799 } 730 }
800 } 731 }
801 732
802 class ScrollerScrollTechnique { 733 class ScrollerScrollTechnique {
803 static final TRANSFORM_3D = 1; 734 static final TRANSFORM_3D = 1;
804 static final RELATIVE_POSITIONING = 2; 735 static final RELATIVE_POSITIONING = 2;
805 } 736 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698