| 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 part of touch; | 5 part of touch; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Implementation of a custom scrolling behavior. | 8 * Implementation of a custom scrolling behavior. |
| 9 * This behavior overrides native scrolling for an area. This area can be a | 9 * This behavior overrides native scrolling for an area. This area can be a |
| 10 * single defined part of a page, the entire page, or several different parts | 10 * single defined part of a page, the entire page, or several different parts |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 // Helper method to await the completion of 2 futures. | 37 // Helper method to await the completion of 2 futures. |
| 38 void joinFutures(List<Future> futures, Callback callback) { | 38 void joinFutures(List<Future> futures, Callback callback) { |
| 39 int count = 0; | 39 int count = 0; |
| 40 int len = futures.length; | 40 int len = futures.length; |
| 41 void helper(value) { | 41 void helper(value) { |
| 42 count++; | 42 count++; |
| 43 if (count == len) { | 43 if (count == len) { |
| 44 callback(); | 44 callback(); |
| 45 } | 45 } |
| 46 } | 46 } |
| 47 |
| 47 for (Future p in futures) { | 48 for (Future p in futures) { |
| 48 p.then(helper); | 49 p.then(helper); |
| 49 } | 50 } |
| 50 } | 51 } |
| 51 | 52 |
| 52 class Scroller implements Draggable, MomentumDelegate { | 53 class Scroller implements Draggable, MomentumDelegate { |
| 53 | |
| 54 /** Pixels to move each time an arrow key is pressed. */ | 54 /** Pixels to move each time an arrow key is pressed. */ |
| 55 static const ARROW_KEY_DELTA = 30; | 55 static const ARROW_KEY_DELTA = 30; |
| 56 static const SCROLL_WHEEL_VELOCITY = 0.01; | 56 static const SCROLL_WHEEL_VELOCITY = 0.01; |
| 57 static const FAST_SNAP_DECELERATION_FACTOR = 0.84; | 57 static const FAST_SNAP_DECELERATION_FACTOR = 0.84; |
| 58 static const PAGE_KEY_SCROLL_FRACTION = .85; | 58 static const PAGE_KEY_SCROLL_FRACTION = .85; |
| 59 | 59 |
| 60 // TODO(jacobr): remove this static variable. | 60 // TODO(jacobr): remove this static variable. |
| 61 static bool _dragInProgress = false; | 61 static bool _dragInProgress = false; |
| 62 | 62 |
| 63 /** The node that will actually scroll. */ | 63 /** The node that will actually scroll. */ |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 | 134 |
| 135 Size _scrollSize; | 135 Size _scrollSize; |
| 136 Size _contentSize; | 136 Size _contentSize; |
| 137 Coordinate _minPoint; | 137 Coordinate _minPoint; |
| 138 bool _isStopping = false; | 138 bool _isStopping = false; |
| 139 Coordinate _contentStartOffset; | 139 Coordinate _contentStartOffset; |
| 140 bool _started = false; | 140 bool _started = false; |
| 141 bool _activeGesture = false; | 141 bool _activeGesture = false; |
| 142 ScrollWatcher _scrollWatcher; | 142 ScrollWatcher _scrollWatcher; |
| 143 | 143 |
| 144 Scroller(Element scrollableElem, [this.verticalEnabled = false, | 144 Scroller(Element scrollableElem, |
| 145 this.horizontalEnabled = false, | 145 [this.verticalEnabled = false, |
| 146 momentumEnabled = true, | 146 this.horizontalEnabled = false, |
| 147 lookupContentSizeDelegate = null, | 147 momentumEnabled = true, |
| 148 num defaultDecelerationFactor = 1, | 148 lookupContentSizeDelegate = null, |
| 149 int scrollTechnique = null, bool capture = false]) | 149 num defaultDecelerationFactor = 1, |
| 150 int scrollTechnique = null, |
| 151 bool capture = false]) |
| 150 : _momentumEnabled = momentumEnabled, | 152 : _momentumEnabled = momentumEnabled, |
| 151 _lookupContentSizeDelegate = lookupContentSizeDelegate, | 153 _lookupContentSizeDelegate = lookupContentSizeDelegate, |
| 152 _element = scrollableElem, | 154 _element = scrollableElem, |
| 153 _frame = scrollableElem.parent, | 155 _frame = scrollableElem.parent, |
| 154 _scrollTechnique = scrollTechnique != null | 156 _scrollTechnique = scrollTechnique != null |
| 155 ? scrollTechnique : ScrollerScrollTechnique.TRANSFORM_3D, | 157 ? scrollTechnique |
| 158 : ScrollerScrollTechnique.TRANSFORM_3D, |
| 156 _minPoint = new Coordinate(0, 0), | 159 _minPoint = new Coordinate(0, 0), |
| 157 _maxPoint = new Coordinate(0, 0), | 160 _maxPoint = new Coordinate(0, 0), |
| 158 _maxOffset = new Coordinate(0, 0), | 161 _maxOffset = new Coordinate(0, 0), |
| 159 _minOffset = new Coordinate(0, 0), | 162 _minOffset = new Coordinate(0, 0), |
| 160 _contentOffset = new Coordinate(0, 0) { | 163 _contentOffset = new Coordinate(0, 0) { |
| 161 _touchHandler = new TouchHandler(this, scrollableElem.parent); | 164 _touchHandler = new TouchHandler(this, scrollableElem.parent); |
| 162 _momentum = new Momentum(this, defaultDecelerationFactor); | 165 _momentum = new Momentum(this, defaultDecelerationFactor); |
| 163 | 166 |
| 164 Element parentElem = scrollableElem.parent; | 167 Element parentElem = scrollableElem.parent; |
| 165 assert(parentElem != null); | 168 assert(parentElem != null); |
| 166 _setOffsetFunction = _getOffsetFunction(_scrollTechnique); | 169 _setOffsetFunction = _getOffsetFunction(_scrollTechnique); |
| 167 _touchHandler.setDraggable(this); | 170 _touchHandler.setDraggable(this); |
| 168 _touchHandler.enable(capture); | 171 _touchHandler.enable(capture); |
| 169 | 172 |
| 170 _frame.onMouseWheel.listen((e) { | 173 _frame.onMouseWheel.listen((e) { |
| 171 if (e.deltaY != 0 && verticalEnabled || | 174 if (e.deltaY != 0 && verticalEnabled || |
| 172 e.deltaX != 0 && horizontalEnabled) { | 175 e.deltaX != 0 && horizontalEnabled) { |
| 173 num x = horizontalEnabled ? e.deltaX : 0; | 176 num x = horizontalEnabled ? e.deltaX : 0; |
| 174 num y = verticalEnabled ? e.deltaY : 0; | 177 num y = verticalEnabled ? e.deltaY : 0; |
| 175 throwDelta(x, y, FAST_SNAP_DECELERATION_FACTOR); | 178 throwDelta(x, y, FAST_SNAP_DECELERATION_FACTOR); |
| 176 e.preventDefault(); | 179 e.preventDefault(); |
| 177 } | 180 } |
| 178 }); | 181 }); |
| 179 | 182 |
| 180 _frame.onKeyDown.listen((KeyboardEvent e) { | 183 _frame.onKeyDown.listen((KeyboardEvent e) { |
| 181 bool handled = false; | 184 bool handled = false; |
| 182 // We ignore key events where further scrolling in that direction | 185 // We ignore key events where further scrolling in that direction |
| 183 // would have no impact which matches default browser behavior with | 186 // would have no impact which matches default browser behavior with |
| 184 // nested scrollable areas. | 187 // nested scrollable areas. |
| 185 | 188 |
| 186 switch(e.keyCode) { | 189 switch (e.keyCode) { |
| 187 case 33: // page-up | 190 case 33: // page-up |
| 188 throwDelta( | 191 throwDelta(0, _scrollSize.height * PAGE_KEY_SCROLL_FRACTION); |
| 189 0, | 192 handled = true; |
| 190 _scrollSize.height * PAGE_KEY_SCROLL_FRACTION); | 193 break; |
| 191 handled = true; | 194 case 34: // page-down |
| 192 break; | 195 throwDelta(0, -_scrollSize.height * PAGE_KEY_SCROLL_FRACTION); |
| 193 case 34: // page-down | 196 handled = true; |
| 194 throwDelta( | 197 break; |
| 195 0, -_scrollSize.height * PAGE_KEY_SCROLL_FRACTION); | 198 case 35: // End |
| 196 handled = true; | 199 throwTo(_maxPoint.x, _minPoint.y, FAST_SNAP_DECELERATION_FACTOR); |
| 197 break; | 200 handled = true; |
| 198 case 35: // End | 201 break; |
| 199 throwTo(_maxPoint.x, _minPoint.y, | 202 case 36: // Home |
| 200 FAST_SNAP_DECELERATION_FACTOR); | 203 throwTo(_maxPoint.x, _maxPoint.y, FAST_SNAP_DECELERATION_FACTOR); |
| 201 handled = true; | 204 handled = true; |
| 202 break; | 205 break; |
| 203 case 36: // Home | |
| 204 throwTo(_maxPoint.x,_maxPoint.y, | |
| 205 FAST_SNAP_DECELERATION_FACTOR); | |
| 206 handled = true; | |
| 207 break; | |
| 208 /* TODO(jacobr): enable arrow keys when the don't conflict with other | 206 /* TODO(jacobr): enable arrow keys when the don't conflict with other |
| 209 application keyboard shortcuts. | 207 application keyboard shortcuts. |
| 210 case 38: // up | 208 case 38: // up |
| 211 handled = throwDelta( | 209 handled = throwDelta( |
| 212 0, | 210 0, |
| 213 ARROW_KEY_DELTA, | 211 ARROW_KEY_DELTA, |
| 214 FAST_SNAP_DECELERATION_FACTOR); | 212 FAST_SNAP_DECELERATION_FACTOR); |
| 215 break; | 213 break; |
| 216 case 40: // down | 214 case 40: // down |
| 217 handled = throwDelta( | 215 handled = throwDelta( |
| 218 0, -ARROW_KEY_DELTA, | 216 0, -ARROW_KEY_DELTA, |
| 219 FAST_SNAP_DECELERATION_FACTOR); | 217 FAST_SNAP_DECELERATION_FACTOR); |
| 220 break; | 218 break; |
| 221 case 37: // left | 219 case 37: // left |
| 222 handled = throwDelta( | 220 handled = throwDelta( |
| 223 ARROW_KEY_DELTA, 0, | 221 ARROW_KEY_DELTA, 0, |
| 224 FAST_SNAP_DECELERATION_FACTOR); | 222 FAST_SNAP_DECELERATION_FACTOR); |
| 225 break; | 223 break; |
| 226 case 39: // right | 224 case 39: // right |
| 227 handled = throwDelta( | 225 handled = throwDelta( |
| 228 -ARROW_KEY_DELTA, | 226 -ARROW_KEY_DELTA, |
| 229 0, | 227 0, |
| 230 FAST_SNAP_DECELERATION_FACTOR); | 228 FAST_SNAP_DECELERATION_FACTOR); |
| 231 break; | 229 break; |
| 232 */ | 230 */ |
| 233 } | 231 } |
| 234 if (handled) { | 232 if (handled) { |
| 235 e.preventDefault(); | 233 e.preventDefault(); |
| 236 } | 234 } |
| 237 }); | 235 }); |
| 238 // The scrollable element must be relatively positioned. | 236 // The scrollable element must be relatively positioned. |
| 239 // TODO(jacobr): this assert fires asynchronously which could be confusing. | 237 // TODO(jacobr): this assert fires asynchronously which could be confusing. |
| 240 if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) { | 238 if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) { |
| 241 assert(_element.getComputedStyle().position != "static"); | 239 assert(_element.getComputedStyle().position != "static"); |
| 242 } | 240 } |
| 243 | 241 |
| 244 _initLayer(); | 242 _initLayer(); |
| 245 } | 243 } |
| 246 | 244 |
| 247 Stream<Event> get onScrollerStart { | 245 Stream<Event> get onScrollerStart { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 277 } | 275 } |
| 278 | 276 |
| 279 Stream<Event> get onDecelStart { | 277 Stream<Event> get onDecelStart { |
| 280 if (_onDecelStart == null) { | 278 if (_onDecelStart == null) { |
| 281 _onDecelStart = new StreamController<Event>.broadcast(sync: true); | 279 _onDecelStart = new StreamController<Event>.broadcast(sync: true); |
| 282 _onDecelStartStream = _onDecelStart.stream; | 280 _onDecelStartStream = _onDecelStart.stream; |
| 283 } | 281 } |
| 284 return _onDecelStartStream; | 282 return _onDecelStartStream; |
| 285 } | 283 } |
| 286 | 284 |
| 287 | |
| 288 /** | 285 /** |
| 289 * Add a scroll listener. This allows other classes to subscribe to scroll | 286 * Add a scroll listener. This allows other classes to subscribe to scroll |
| 290 * notifications from this scroller. | 287 * notifications from this scroller. |
| 291 */ | 288 */ |
| 292 void addScrollListener(ScrollListener listener) { | 289 void addScrollListener(ScrollListener listener) { |
| 293 if (_scrollWatcher == null) { | 290 if (_scrollWatcher == null) { |
| 294 _scrollWatcher = new ScrollWatcher(this); | 291 _scrollWatcher = new ScrollWatcher(this); |
| 295 _scrollWatcher.initialize(); | 292 _scrollWatcher.initialize(); |
| 296 } | 293 } |
| 297 _scrollWatcher.addListener(listener); | 294 _scrollWatcher.addListener(listener); |
| 298 } | 295 } |
| 299 | 296 |
| 300 /** | 297 /** |
| 301 * Adjust the new calculated scroll position based on the minimum allowed | 298 * Adjust the new calculated scroll position based on the minimum allowed |
| 302 * position and returns the adjusted scroll value. | 299 * position and returns the adjusted scroll value. |
| 303 */ | 300 */ |
| 304 num _adjustValue(num newPosition, num minPosition, | 301 num _adjustValue(num newPosition, num minPosition, num maxPosition) { |
| 305 num maxPosition) { | |
| 306 assert(minPosition <= maxPosition); | 302 assert(minPosition <= maxPosition); |
| 307 | 303 |
| 308 if (newPosition < minPosition) { | 304 if (newPosition < minPosition) { |
| 309 newPosition -= (newPosition - minPosition) / 2; | 305 newPosition -= (newPosition - minPosition) / 2; |
| 310 } else { | 306 } else { |
| 311 if (newPosition > maxPosition) { | 307 if (newPosition > maxPosition) { |
| 312 newPosition -= (newPosition - maxPosition) / 2; | 308 newPosition -= (newPosition - maxPosition) / 2; |
| 313 } | 309 } |
| 314 } | 310 } |
| 315 return newPosition; | 311 return newPosition; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 340 // deceleration factor specified by the momentum simulator. | 336 // deceleration factor specified by the momentum simulator. |
| 341 if (decelerationFactor == null) { | 337 if (decelerationFactor == null) { |
| 342 decelerationFactor = _momentum.decelerationFactor; | 338 decelerationFactor = _momentum.decelerationFactor; |
| 343 } | 339 } |
| 344 | 340 |
| 345 if (snappedTarget != currentTarget) { | 341 if (snappedTarget != currentTarget) { |
| 346 _momentum.abort(); | 342 _momentum.abort(); |
| 347 | 343 |
| 348 _startDeceleration( | 344 _startDeceleration( |
| 349 _momentum.calculateVelocity( | 345 _momentum.calculateVelocity( |
| 350 _contentOffset, | 346 _contentOffset, snappedTarget, decelerationFactor), |
| 351 snappedTarget, | |
| 352 decelerationFactor), | |
| 353 decelerationFactor); | 347 decelerationFactor); |
| 354 if (_onDecelStart != null) { | 348 if (_onDecelStart != null) { |
| 355 _onDecelStart.add(new Event(ScrollerEventType.DECEL_START)); | 349 _onDecelStart.add(new Event(ScrollerEventType.DECEL_START)); |
| 356 } | 350 } |
| 357 } | 351 } |
| 358 }); | 352 }); |
| 359 } | 353 } |
| 360 | 354 |
| 361 void throwDelta(num deltaX, num deltaY, [num decelerationFactor = null]) { | 355 void throwDelta(num deltaX, num deltaY, [num decelerationFactor = null]) { |
| 362 Coordinate start = _contentOffset; | 356 Coordinate start = _contentOffset; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 376 throwTo(x, y, decelerationFactor); | 370 throwTo(x, y, decelerationFactor); |
| 377 } | 371 } |
| 378 | 372 |
| 379 void setPosition(num x, num y) { | 373 void setPosition(num x, num y) { |
| 380 _momentum.abort(); | 374 _momentum.abort(); |
| 381 _contentOffset.x = x; | 375 _contentOffset.x = x; |
| 382 _contentOffset.y = y; | 376 _contentOffset.y = y; |
| 383 _snapContentOffsetToBounds(); | 377 _snapContentOffsetToBounds(); |
| 384 _setContentOffset(_contentOffset.x, _contentOffset.y); | 378 _setContentOffset(_contentOffset.x, _contentOffset.y); |
| 385 } | 379 } |
| 380 |
| 386 /** | 381 /** |
| 387 * Adjusted content size is a size with the combined largest height and width | 382 * Adjusted content size is a size with the combined largest height and width |
| 388 * of both the content and the frame. | 383 * of both the content and the frame. |
| 389 */ | 384 */ |
| 390 Size _getAdjustedContentSize() { | 385 Size _getAdjustedContentSize() { |
| 391 return new Size(Math.max(_scrollSize.width, _contentSize.width), | 386 return new Size(Math.max(_scrollSize.width, _contentSize.width), |
| 392 Math.max(_scrollSize.height, _contentSize.height)); | 387 Math.max(_scrollSize.height, _contentSize.height)); |
| 393 } | 388 } |
| 394 | 389 |
| 395 // TODO(jmesserly): these should be properties instead of get* methods | 390 // TODO(jmesserly): these should be properties instead of get* methods |
| 396 num getDefaultVerticalOffset() => _maxPoint.y; | 391 num getDefaultVerticalOffset() => _maxPoint.y; |
| 397 Element getElement() => _element; | 392 Element getElement() => _element; |
| 398 Element getFrame() => _frame; | 393 Element getFrame() => _frame; |
| 399 num getHorizontalOffset() => _contentOffset.x; | 394 num getHorizontalOffset() => _contentOffset.x; |
| 400 | 395 |
| 401 /** | 396 /** |
| 402 * [x] Value to use as reference for percent measurement. If | 397 * [x] Value to use as reference for percent measurement. If |
| 403 * none is provided then the content's current x offset will be used. | 398 * none is provided then the content's current x offset will be used. |
| 404 * Returns the percent of the page scrolled horizontally. | 399 * Returns the percent of the page scrolled horizontally. |
| 405 */ | 400 */ |
| 406 num getHorizontalScrollPercent([num x = null]) { | 401 num getHorizontalScrollPercent([num x = null]) { |
| 407 x = x != null ? x : _contentOffset.x; | 402 x = x != null ? x : _contentOffset.x; |
| 408 return (x - _minPoint.x) / (_maxPoint.x - _minPoint.x); | 403 return (x - _minPoint.x) / (_maxPoint.x - _minPoint.x); |
| 409 } | 404 } |
| 410 | 405 |
| 411 num getMaxPointY()=> _maxPoint.y; | 406 num getMaxPointY() => _maxPoint.y; |
| 412 num getMinPointY() => _minPoint.y; | 407 num getMinPointY() => _minPoint.y; |
| 413 Momentum get momentum => _momentum; | 408 Momentum get momentum => _momentum; |
| 414 | 409 |
| 415 /** | 410 /** |
| 416 * Provide access to the touch handler that the scroller created to manage | 411 * Provide access to the touch handler that the scroller created to manage |
| 417 * touch events. | 412 * touch events. |
| 418 */ | 413 */ |
| 419 TouchHandler getTouchHandler() => _touchHandler; | 414 TouchHandler getTouchHandler() => _touchHandler; |
| 420 num getVerticalOffset() => _contentOffset.y; | 415 num getVerticalOffset() => _contentOffset.y; |
| 421 | 416 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 | 475 |
| 481 void onDragMove() { | 476 void onDragMove() { |
| 482 if (_isStopping || (!_activeGesture && _dragInProgress)) { | 477 if (_isStopping || (!_activeGesture && _dragInProgress)) { |
| 483 return; | 478 return; |
| 484 } | 479 } |
| 485 | 480 |
| 486 assert(_contentStartOffset != null); // Content start not set | 481 assert(_contentStartOffset != null); // Content start not set |
| 487 Coordinate contentStart = _contentStartOffset; | 482 Coordinate contentStart = _contentStartOffset; |
| 488 num newX = contentStart.x + _touchHandler.getDragDeltaX(); | 483 num newX = contentStart.x + _touchHandler.getDragDeltaX(); |
| 489 num newY = contentStart.y + _touchHandler.getDragDeltaY(); | 484 num newY = contentStart.y + _touchHandler.getDragDeltaY(); |
| 490 newY = _shouldScrollVertically() ? | 485 newY = _shouldScrollVertically() |
| 491 _adjustValue(newY, _minPoint.y, _maxPoint.y) : 0; | 486 ? _adjustValue(newY, _minPoint.y, _maxPoint.y) |
| 492 newX = _shouldScrollHorizontally() ? | 487 : 0; |
| 493 _adjustValue(newX, _minPoint.x, _maxPoint.x) : 0; | 488 newX = _shouldScrollHorizontally() |
| 489 ? _adjustValue(newX, _minPoint.x, _maxPoint.x) |
| 490 : 0; |
| 494 if (!_activeGesture) { | 491 if (!_activeGesture) { |
| 495 _activeGesture = true; | 492 _activeGesture = true; |
| 496 _dragInProgress = true; | 493 _dragInProgress = true; |
| 497 } | 494 } |
| 498 if (!_started) { | 495 if (!_started) { |
| 499 _started = true; | 496 _started = true; |
| 500 if (_onScrollerStart != null) { | 497 if (_onScrollerStart != null) { |
| 501 _onScrollerStart.add(new Event(ScrollerEventType.SCROLLER_START)); | 498 _onScrollerStart.add(new Event(ScrollerEventType.SCROLLER_START)); |
| 502 } | 499 } |
| 503 } | 500 } |
| 504 _setContentOffset(newX, newY); | 501 _setContentOffset(newX, newY); |
| 505 } | 502 } |
| 506 | 503 |
| 507 bool onDragStart(TouchEvent e) { | 504 bool onDragStart(TouchEvent e) { |
| 508 if (e.touches.length > 1) { | 505 if (e.touches.length > 1) { |
| 509 return false; | 506 return false; |
| 510 } | 507 } |
| 511 bool shouldHorizontal = _shouldScrollHorizontally(); | 508 bool shouldHorizontal = _shouldScrollHorizontally(); |
| 512 bool shouldVertical = _shouldScrollVertically(); | 509 bool shouldVertical = _shouldScrollVertically(); |
| 513 bool verticalish = _touchHandler.getDragDeltaY().abs() > | 510 bool verticalish = _touchHandler.getDragDeltaY().abs() > |
| 514 _touchHandler.getDragDeltaX().abs(); | 511 _touchHandler.getDragDeltaX().abs(); |
| 515 return !!(shouldVertical || shouldHorizontal && !verticalish); | 512 return !!(shouldVertical || shouldHorizontal && !verticalish); |
| 516 } | 513 } |
| 517 | 514 |
| 518 void onTouchEnd() { | 515 void onTouchEnd() {} |
| 519 } | |
| 520 | 516 |
| 521 /** | 517 /** |
| 522 * Prepare the scrollable area for possible movement. | 518 * Prepare the scrollable area for possible movement. |
| 523 */ | 519 */ |
| 524 bool onTouchStart(TouchEvent e) { | 520 bool onTouchStart(TouchEvent e) { |
| 525 reconfigure(() { | 521 reconfigure(() { |
| 526 final touch = e.touches[0]; | 522 final touch = e.touches[0]; |
| 527 if (_momentum.decelerating) { | 523 if (_momentum.decelerating) { |
| 528 e.preventDefault(); | 524 e.preventDefault(); |
| 529 e.stopPropagation(); | 525 e.stopPropagation(); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 563 * and maxPoint allowed for scrolling. | 559 * and maxPoint allowed for scrolling. |
| 564 */ | 560 */ |
| 565 void _resize(Callback callback) { | 561 void _resize(Callback callback) { |
| 566 scheduleMicrotask(() { | 562 scheduleMicrotask(() { |
| 567 if (_lookupContentSizeDelegate != null) { | 563 if (_lookupContentSizeDelegate != null) { |
| 568 _contentSize = _lookupContentSizeDelegate(); | 564 _contentSize = _lookupContentSizeDelegate(); |
| 569 } else { | 565 } else { |
| 570 _contentSize = new Size(_element.scrollWidth, _element.scrollHeight); | 566 _contentSize = new Size(_element.scrollWidth, _element.scrollHeight); |
| 571 } | 567 } |
| 572 | 568 |
| 573 _scrollSize = new Size(_frame.offset.width, | 569 _scrollSize = new Size(_frame.offset.width, _frame.offset.height); |
| 574 _frame.offset.height); | |
| 575 Size adjusted = _getAdjustedContentSize(); | 570 Size adjusted = _getAdjustedContentSize(); |
| 576 _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y); | 571 _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y); |
| 577 _minPoint = new Coordinate( | 572 _minPoint = new Coordinate( |
| 578 Math.min( | 573 Math.min( |
| 579 _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x), | 574 _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x), |
| 580 Math.min( | 575 Math.min(_scrollSize.height - adjusted.height + _minOffset.y, |
| 581 _scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y))
; | 576 _maxPoint.y)); |
| 582 callback(); | 577 callback(); |
| 583 }); | 578 }); |
| 584 } | 579 } |
| 585 | 580 |
| 586 Coordinate _snapToBounds(num x, num y) { | 581 Coordinate _snapToBounds(num x, num y) { |
| 587 num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x); | 582 num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x); |
| 588 num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y); | 583 num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y); |
| 589 return new Coordinate(clampX, clampY); | 584 return new Coordinate(clampX, clampY); |
| 590 } | 585 } |
| 591 | 586 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 609 } | 604 } |
| 610 | 605 |
| 611 /** | 606 /** |
| 612 * Sets the vertical scrolled offset of the element where [y] is the amount | 607 * Sets the vertical scrolled offset of the element where [y] is the amount |
| 613 * of vertical space to be scrolled, in pixels. | 608 * of vertical space to be scrolled, in pixels. |
| 614 */ | 609 */ |
| 615 void setVerticalOffset(num y) { | 610 void setVerticalOffset(num y) { |
| 616 _setContentOffset(_contentOffset.x, y); | 611 _setContentOffset(_contentOffset.x, y); |
| 617 } | 612 } |
| 618 | 613 |
| 619 /** | 614 /** |
| 620 * Whether the scrollable area should scroll horizontally. Only | 615 * Whether the scrollable area should scroll horizontally. Only |
| 621 * returns true if the client has enabled horizontal scrolling, and the | 616 * returns true if the client has enabled horizontal scrolling, and the |
| 622 * content is wider than the frame. | 617 * content is wider than the frame. |
| 623 */ | 618 */ |
| 624 bool _shouldScrollHorizontally() { | 619 bool _shouldScrollHorizontally() { |
| 625 return horizontalEnabled && _scrollSize.width < _contentSize.width; | 620 return horizontalEnabled && _scrollSize.width < _contentSize.width; |
| 626 } | 621 } |
| 627 | 622 |
| 628 /** | 623 /** |
| 629 * Whether the scrollable area should scroll vertically. Only | 624 * Whether the scrollable area should scroll vertically. Only |
| 630 * returns true if the client has enabled vertical scrolling. | 625 * returns true if the client has enabled vertical scrolling. |
| 631 * Vertical bouncing will occur even if frame is taller than content, because | 626 * Vertical bouncing will occur even if frame is taller than content, because |
| 632 * this is what iPhone web apps tend to do. If this is not the desired | 627 * this is what iPhone web apps tend to do. If this is not the desired |
| 633 * behavior, either disable vertical scrolling for this scroller or add a | 628 * behavior, either disable vertical scrolling for this scroller or add a |
| 634 * 'bouncing' parameter to this interface. | 629 * 'bouncing' parameter to this interface. |
| 635 */ | 630 */ |
| 636 bool _shouldScrollVertically() { | 631 bool _shouldScrollVertically() { |
| 637 return verticalEnabled; | 632 return verticalEnabled; |
| 638 } | 633 } |
| 639 | 634 |
| 640 /** | 635 /** |
| 641 * In the event that the content is currently beyond the bounds of | 636 * In the event that the content is currently beyond the bounds of |
| 642 * the frame, snap it back in to place. | 637 * the frame, snap it back in to place. |
| 643 */ | 638 */ |
| 644 void _snapContentOffsetToBounds() { | 639 void _snapContentOffsetToBounds() { |
| 645 num clampX = | 640 num clampX = GoogleMath.clamp(_minPoint.x, _contentOffset.x, _maxPoint.x); |
| 646 GoogleMath.clamp(_minPoint.x, _contentOffset.x, _maxPoint.x); | 641 num clampY = GoogleMath.clamp(_minPoint.y, _contentOffset.y, _maxPoint.y); |
| 647 num clampY = | |
| 648 GoogleMath.clamp(_minPoint.y, _contentOffset.y, _maxPoint.y); | |
| 649 if (_contentOffset.x != clampX || _contentOffset.y != clampY) { | 642 if (_contentOffset.x != clampX || _contentOffset.y != clampY) { |
| 650 _setContentOffset(clampX, clampY); | 643 _setContentOffset(clampX, clampY); |
| 651 } | 644 } |
| 652 } | 645 } |
| 653 | 646 |
| 654 /** | 647 /** |
| 655 * Initiate the deceleration behavior given a flick [velocity]. | 648 * Initiate the deceleration behavior given a flick [velocity]. |
| 656 * Returns true if deceleration has been initiated. | 649 * Returns true if deceleration has been initiated. |
| 657 */ | 650 */ |
| 658 bool _startDeceleration(Coordinate velocity, | 651 bool _startDeceleration(Coordinate velocity, |
| 659 [num decelerationFactor = null]) { | 652 [num decelerationFactor = null]) { |
| 660 if (!_shouldScrollHorizontally()) { | 653 if (!_shouldScrollHorizontally()) { |
| 661 velocity.x = 0; | 654 velocity.x = 0; |
| 662 } | 655 } |
| 663 if (!_shouldScrollVertically()) { | 656 if (!_shouldScrollVertically()) { |
| 664 velocity.y = 0; | 657 velocity.y = 0; |
| 665 } | 658 } |
| 666 assert(_minPoint != null); // Min point is not set | 659 assert(_minPoint != null); // Min point is not set |
| 667 assert(_maxPoint != null); // Max point is not set | 660 assert(_maxPoint != null); // Max point is not set |
| 668 return _momentum.start(velocity, _minPoint, _maxPoint, _contentOffset, | 661 return _momentum.start( |
| 669 decelerationFactor); | 662 velocity, _minPoint, _maxPoint, _contentOffset, decelerationFactor); |
| 670 } | 663 } |
| 671 | 664 |
| 672 Coordinate stop() { | 665 Coordinate stop() { |
| 673 return _momentum.stop(); | 666 return _momentum.stop(); |
| 674 } | 667 } |
| 675 | 668 |
| 676 /** | 669 /** |
| 677 * Stop the deceleration of the scrollable content given a new position in px. | 670 * Stop the deceleration of the scrollable content given a new position in px. |
| 678 */ | 671 */ |
| 679 void _stopDecelerating(num x, num y) { | 672 void _stopDecelerating(num x, num y) { |
| 680 _momentum.stop(); | 673 _momentum.stop(); |
| 681 _setContentOffset(x, y); | 674 _setContentOffset(x, y); |
| 682 } | 675 } |
| 683 | 676 |
| 684 static Function _getOffsetFunction(int scrollTechnique) { | 677 static Function _getOffsetFunction(int scrollTechnique) { |
| 685 return scrollTechnique == ScrollerScrollTechnique.TRANSFORM_3D ? | 678 return scrollTechnique == ScrollerScrollTechnique.TRANSFORM_3D |
| 686 (el, x, y) { FxUtil.setTranslate(el, x, y, 0); } : | 679 ? (el, x, y) { |
| 687 (el, x, y) { FxUtil.setLeftAndTop(el, x, y); }; | 680 FxUtil.setTranslate(el, x, y, 0); |
| 681 } |
| 682 : (el, x, y) { |
| 683 FxUtil.setLeftAndTop(el, x, y); |
| 684 }; |
| 688 } | 685 } |
| 689 } | 686 } |
| 690 | 687 |
| 691 // TODO(jacobr): cleanup this class of enum constants. | 688 // TODO(jacobr): cleanup this class of enum constants. |
| 692 class ScrollerEventType { | 689 class ScrollerEventType { |
| 693 static const SCROLLER_START = "scroller:scroll_start"; | 690 static const SCROLLER_START = "scroller:scroll_start"; |
| 694 static const SCROLLER_END = "scroller:scroll_end"; | 691 static const SCROLLER_END = "scroller:scroll_end"; |
| 695 static const DRAG_END = "scroller:drag_end"; | 692 static const DRAG_END = "scroller:drag_end"; |
| 696 static const CONTENT_MOVED = "scroller:content_moved"; | 693 static const CONTENT_MOVED = "scroller:content_moved"; |
| 697 static const DECEL_START = "scroller:decel_start"; | 694 static const DECEL_START = "scroller:decel_start"; |
| 698 } | 695 } |
| 699 | 696 |
| 700 class ScrollerScrollTechnique { | 697 class ScrollerScrollTechnique { |
| 701 static const TRANSFORM_3D = 1; | 698 static const TRANSFORM_3D = 1; |
| 702 static const RELATIVE_POSITIONING = 2; | 699 static const RELATIVE_POSITIONING = 2; |
| 703 } | 700 } |
| OLD | NEW |