Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |