| Index: client/touch/Scroller.dart
|
| ===================================================================
|
| --- client/touch/Scroller.dart (revision 4144)
|
| +++ client/touch/Scroller.dart (working copy)
|
| @@ -1,731 +0,0 @@
|
| -// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -/**
|
| - * Implementation of a custom scrolling behavior.
|
| - * This behavior overrides native scrolling for an area. This area can be a
|
| - * single defined part of a page, the entire page, or several different parts
|
| - * of a page.
|
| - *
|
| - * To use this scrolling behavior you need to define a frame and the content.
|
| - * The frame defines the area that the content will scroll within. The frame and
|
| - * content must both be HTML Elements, with the content being a direct child of
|
| - * the frame. Usually the frame is smaller in size than the content. This is
|
| - * not necessary though, if the content is smaller then bouncing will occur to
|
| - * provide feedback that you are past the scrollable area.
|
| - *
|
| - * The scrolling behavior works using the webkit translate3d transformation,
|
| - * which means browsers that do not have hardware accelerated transformations
|
| - * will not perform as well using this. Simple scrolling should be fine even
|
| - * without hardware acceleration, but animating momentum and deceleration is
|
| - * unacceptably slow without it. There is also the option to use relative
|
| - * positioning (setting the left and top styles).
|
| - *
|
| - * For this to work properly you need to set -webkit-text-size-adjust to 'none'
|
| - * on an ancestor element of the frame, or on the frame itself. If you forget
|
| - * this you may see the text content of the scrollable area changing size as it
|
| - * moves.
|
| - *
|
| - * The behavior is intended to support vertical and horizontal scrolling, and
|
| - * scrolling with momentum when a touch gesture flicks with enough velocity.
|
| - */
|
| -typedef void Callback();
|
| -
|
| -// Helper method to await the completion of 2 futures.
|
| -void joinFutures(List<Future> futures, Callback callback) {
|
| - int count = 0;
|
| - int len = futures.length;
|
| - void helper(value) {
|
| - count++;
|
| - if (count == len) {
|
| - callback();
|
| - }
|
| - }
|
| - for (Future p in futures) {
|
| - p.then(helper);
|
| - }
|
| -}
|
| -
|
| -class Scroller implements Draggable, MomentumDelegate {
|
| -
|
| - /** Pixels to move each time an arrow key is pressed. */
|
| - static final ARROW_KEY_DELTA = 30;
|
| - static final SCROLL_WHEEL_VELOCITY = 0.01;
|
| - static final FAST_SNAP_DECELERATION_FACTOR = 0.84;
|
| - static final PAGE_KEY_SCROLL_FRACTION = .85;
|
| -
|
| - // TODO(jacobr): remove this static variable.
|
| - static bool _dragInProgress = false;
|
| -
|
| - /** The node that will actually scroll. */
|
| - Element _element;
|
| -
|
| - /**
|
| - * Frame is the node that will serve as the container for the scrolling
|
| - * content.
|
| - */
|
| - Element _frame;
|
| -
|
| - /** Touch manager to track the events on the scrollable area. */
|
| - TouchHandler _touchHandler;
|
| -
|
| - Momentum _momentum;
|
| -
|
| - EventListenerList _onScrollerStart;
|
| - EventListenerList _onScrollerEnd;
|
| - EventListenerList _onScrollerDragEnd;
|
| - EventListenerList _onContentMoved;
|
| - EventListenerList _onDecelStart;
|
| -
|
| - /** Set if vertical scrolling should be enabled. */
|
| - bool verticalEnabled;
|
| -
|
| - /** Set if horizontal scrolling should be enabled. */
|
| - bool horizontalEnabled;
|
| -
|
| - /**
|
| - * Set if momentum should be enabled.
|
| - */
|
| - bool _momentumEnabled;
|
| -
|
| - /** Set which type of scrolling translation technique should be used. */
|
| - int _scrollTechnique;
|
| -
|
| - /**
|
| - * The maximum coordinate that the left upper corner of the content can scroll
|
| - * to.
|
| - */
|
| - Coordinate _maxPoint;
|
| -
|
| - /**
|
| - * An offset to subtract from the maximum coordinate that the left upper
|
| - * corner of the content can scroll to.
|
| - */
|
| - Coordinate _maxOffset;
|
| -
|
| - /**
|
| - * An offset to add to the minimum coordinate that the left upper corner of
|
| - * the content can scroll to.
|
| - */
|
| - Coordinate _minOffset;
|
| -
|
| - /** Initialize the current content offset. */
|
| - Coordinate _contentOffset;
|
| -
|
| - // TODO(jacobr): the function type is
|
| - // [:Function(Element, num, num)->void:].
|
| - /**
|
| - * The function to use that will actually translate the scrollable node.
|
| - */
|
| - Function _setOffsetFunction;
|
| - /**
|
| - * Function that returns the content size that can be specified instead of
|
| - * querying the DOM.
|
| - */
|
| - Function _lookupContentSizeDelegate;
|
| -
|
| - Size _scrollSize;
|
| - Size _contentSize;
|
| - Coordinate _minPoint;
|
| - bool _isStopping = false;
|
| - Coordinate _contentStartOffset;
|
| - bool _started = false;
|
| - bool _activeGesture = false;
|
| - ScrollWatcher _scrollWatcher;
|
| -
|
| - Scroller(Element scrollableElem, [this.verticalEnabled = false,
|
| - this.horizontalEnabled = false,
|
| - momentumEnabled = true,
|
| - lookupContentSizeDelegate = null,
|
| - num defaultDecelerationFactor = 1,
|
| - int scrollTechnique = null, bool capture = false])
|
| - : _momentumEnabled = momentumEnabled,
|
| - _lookupContentSizeDelegate = lookupContentSizeDelegate,
|
| - _element = scrollableElem,
|
| - _frame = scrollableElem.parent,
|
| - _scrollTechnique = scrollTechnique !== null
|
| - ? scrollTechnique : ScrollerScrollTechnique.TRANSFORM_3D,
|
| - _minPoint = new Coordinate(0, 0),
|
| - _maxPoint = new Coordinate(0, 0),
|
| - _maxOffset = new Coordinate(0, 0),
|
| - _minOffset = new Coordinate(0, 0),
|
| - _contentOffset = new Coordinate(0, 0) {
|
| - _touchHandler = new TouchHandler(this, scrollableElem.parent);
|
| - _momentum = new Momentum(this, defaultDecelerationFactor);
|
| -
|
| - Element parentElem = scrollableElem.parent;
|
| - assert(parentElem != null);
|
| - _setOffsetFunction = _getOffsetFunction(_scrollTechnique);
|
| - _touchHandler.setDraggable(this);
|
| - _touchHandler.enable(capture);
|
| -
|
| - _frame.on.mouseWheel.add((e) {
|
| - if (e.wheelDeltaY != 0 && verticalEnabled ||
|
| - e.wheelDeltaX != 0 && horizontalEnabled) {
|
| - num x = horizontalEnabled ? e.wheelDeltaX : 0;
|
| - num y = verticalEnabled ? e.wheelDeltaY : 0;
|
| - throwDelta(x, y, FAST_SNAP_DECELERATION_FACTOR);
|
| - e.preventDefault();
|
| - }
|
| - });
|
| -
|
| - _frame.on.keyDown.add((KeyboardEvent e) {
|
| - bool handled = false;
|
| - // We ignore key events where further scrolling in that direction
|
| - // would have no impact which matches default browser behavior with
|
| - // nested scrollable areas.
|
| -
|
| - switch(e.keyCode) {
|
| - case 33: // page-up
|
| - throwDelta(
|
| - 0,
|
| - _scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
|
| - handled = true;
|
| - break;
|
| - case 34: // page-down
|
| - throwDelta(
|
| - 0, -_scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
|
| - handled = true;
|
| - break;
|
| - case 35: // End
|
| - throwTo(_maxPoint.x, _minPoint.y,
|
| - FAST_SNAP_DECELERATION_FACTOR);
|
| - handled = true;
|
| - break;
|
| - case 36: // Home
|
| - throwTo(_maxPoint.x,_maxPoint.y,
|
| - FAST_SNAP_DECELERATION_FACTOR);
|
| - handled = true;
|
| - break;
|
| -/* TODO(jacobr): enable arrow keys when the don't conflict with other
|
| - application keyboard shortcuts.
|
| - case 38: // up
|
| - handled = throwDelta(
|
| - 0,
|
| - ARROW_KEY_DELTA,
|
| - FAST_SNAP_DECELERATION_FACTOR);
|
| - break;
|
| - case 40: // down
|
| - handled = throwDelta(
|
| - 0, -ARROW_KEY_DELTA,
|
| - FAST_SNAP_DECELERATION_FACTOR);
|
| - break;
|
| - case 37: // left
|
| - handled = throwDelta(
|
| - ARROW_KEY_DELTA, 0,
|
| - FAST_SNAP_DECELERATION_FACTOR);
|
| - break;
|
| - case 39: // right
|
| - handled = throwDelta(
|
| - -ARROW_KEY_DELTA,
|
| - 0,
|
| - FAST_SNAP_DECELERATION_FACTOR);
|
| - break;
|
| - */
|
| - }
|
| - if (handled) {
|
| - e.preventDefault();
|
| - }
|
| - });
|
| - // The scrollable element must be relatively positioned.
|
| - // TODO(jacobr): this assert fires asynchronously which could be confusing.
|
| - if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) {
|
| - _element.computedStyle.then((CSSStyleDeclaration style) {
|
| - assert(style.position != "static");
|
| - });
|
| - }
|
| -
|
| - _initLayer();
|
| - }
|
| -
|
| - EventListenerList get onScrollerStart() {
|
| - if (_onScrollerStart === null) {
|
| - _onScrollerStart = new SimpleEventListenerList();
|
| - }
|
| - return _onScrollerStart;
|
| - }
|
| -
|
| - EventListenerList get onScrollerEnd() {
|
| - if (_onScrollerEnd === null) {
|
| - _onScrollerEnd = new SimpleEventListenerList();
|
| - }
|
| - return _onScrollerEnd;
|
| - }
|
| -
|
| - EventListenerList get onScrollerDragEnd() {
|
| - if (_onScrollerDragEnd === null) {
|
| - _onScrollerDragEnd = new SimpleEventListenerList();
|
| - }
|
| - return _onScrollerDragEnd;
|
| - }
|
| -
|
| - EventListenerList get onContentMoved() {
|
| - if (_onContentMoved === null) {
|
| - _onContentMoved = new SimpleEventListenerList();
|
| - }
|
| - return _onContentMoved;
|
| - }
|
| -
|
| - EventListenerList get onDecelStart() {
|
| - if (_onDecelStart === null) {
|
| - _onDecelStart = new SimpleEventListenerList();
|
| - }
|
| - return _onDecelStart;
|
| - }
|
| -
|
| -
|
| - /**
|
| - * Add a scroll listener. This allows other classes to subscribe to scroll
|
| - * notifications from this scroller.
|
| - */
|
| - void addScrollListener(ScrollListener listener) {
|
| - if (_scrollWatcher === null) {
|
| - _scrollWatcher = new ScrollWatcher(this);
|
| - _scrollWatcher.initialize();
|
| - }
|
| - _scrollWatcher.addListener(listener);
|
| - }
|
| -
|
| - /**
|
| - * Adjust the new calculated scroll position based on the minimum allowed
|
| - * position and returns the adjusted scroll value.
|
| - */
|
| - num _adjustValue(num newPosition, num minPosition,
|
| - num maxPosition) {
|
| - assert(minPosition <= maxPosition);
|
| -
|
| - if (newPosition < minPosition) {
|
| - newPosition -= (newPosition - minPosition) / 2;
|
| - } else {
|
| - if (newPosition > maxPosition) {
|
| - newPosition -= (newPosition - maxPosition) / 2;
|
| - }
|
| - }
|
| - return newPosition;
|
| - }
|
| -
|
| - /**
|
| - * Coordinate we would end up at if we did nothing.
|
| - */
|
| - Coordinate get currentTarget() {
|
| - Coordinate end = _momentum.destination;
|
| - if (end === null) {
|
| - end = _contentOffset;
|
| - }
|
| - return end;
|
| - }
|
| -
|
| - Coordinate get contentOffset() => _contentOffset;
|
| -
|
| - /**
|
| - * Animate the position of the scroller to the specified [x], [y] coordinates
|
| - * by applying the throw gesture with the correct velocity to end at that
|
| - * location.
|
| - */
|
| - void throwTo(num x, num y, [num decelerationFactor = null]) {
|
| - reconfigure(() {
|
| - final snappedTarget = _snapToBounds(x, y);
|
| - // If a deceleration factor is not specified, use the existing
|
| - // deceleration factor specified by the momentum simulator.
|
| - if (decelerationFactor == null) {
|
| - decelerationFactor = _momentum.decelerationFactor;
|
| - }
|
| -
|
| - if (snappedTarget != currentTarget) {
|
| - _momentum.abort();
|
| -
|
| - _startDeceleration(
|
| - _momentum.calculateVelocity(
|
| - _contentOffset,
|
| - snappedTarget,
|
| - decelerationFactor),
|
| - decelerationFactor);
|
| - onDecelStart.dispatch(new Event(ScrollerEventType.DECEL_START));
|
| - }
|
| - });
|
| - }
|
| -
|
| - void throwDelta(num deltaX, num deltaY, [num decelerationFactor = null]) {
|
| - Coordinate start = _contentOffset;
|
| - Coordinate end = currentTarget;
|
| - int x = end.x.toInt();
|
| - int y = end.y.toInt();
|
| - // If we are throwing in the opposite direction of the existing momentum,
|
| - // cancel the current momentum.
|
| - if (deltaX != 0 && deltaX.isNegative() != (end.x - start.x).isNegative()) {
|
| - x = start.x;
|
| - }
|
| - if (deltaY != 0 && deltaY.isNegative() != (end.y - start.y).isNegative()) {
|
| - y = start.y;
|
| - }
|
| - x += deltaX.toInt();
|
| - y += deltaY.toInt();
|
| - throwTo(x, y, decelerationFactor);
|
| - }
|
| -
|
| - void setPosition(num x, num y) {
|
| - _momentum.abort();
|
| - _contentOffset.x = x;
|
| - _contentOffset.y = y;
|
| - _snapContentOffsetToBounds();
|
| - _setContentOffset(_contentOffset.x, _contentOffset.y);
|
| - }
|
| - /**
|
| - * Adjusted content size is a size with the combined largest height and width
|
| - * of both the content and the frame.
|
| - */
|
| - Size _getAdjustedContentSize() {
|
| - return new Size(Math.max(_scrollSize.width, _contentSize.width),
|
| - Math.max(_scrollSize.height, _contentSize.height));
|
| - }
|
| -
|
| - // TODO(jmesserly): these should be properties instead of get* methods
|
| - num getDefaultVerticalOffset() => _maxPoint.y;
|
| - Element getElement() => _element;
|
| - Element getFrame() => _frame;
|
| - num getHorizontalOffset() => _contentOffset.x;
|
| -
|
| - /**
|
| - * [x] Value to use as reference for percent measurement. If
|
| - * none is provided then the content's current x offset will be used.
|
| - * Returns the percent of the page scrolled horizontally.
|
| - */
|
| - num getHorizontalScrollPercent([num x = null]) {
|
| - x = x !== null ? x : _contentOffset.x;
|
| - return (x - _minPoint.x) / (_maxPoint.x - _minPoint.x);
|
| - }
|
| -
|
| - num getMaxPointY()=> _maxPoint.y;
|
| - num getMinPointY() => _minPoint.y;
|
| - Momentum get momentum() => _momentum;
|
| -
|
| - /**
|
| - * Provide access to the touch handler that the scroller created to manage
|
| - * touch events.
|
| - */
|
| - TouchHandler getTouchHandler() => _touchHandler;
|
| - num getVerticalOffset() => _contentOffset.y;
|
| -
|
| - /**
|
| - * [y] value is used as reference for percent measurement. If
|
| - * none is provided then the content's current y offset will be used.
|
| - */
|
| - num getVerticalScrollPercent([num y = null]) {
|
| - y = y !== null ? y : _contentOffset.y;
|
| - return (y - _minPoint.y) / Math.max(1, _maxPoint.y - _minPoint.y);
|
| - }
|
| -
|
| - /**
|
| - * Initialize the dom elements necessary for the scrolling to work.
|
| - */
|
| - void _initLayer() {
|
| - // The scrollable node provided to Scroller must be a direct child
|
| - // of the scrollable frame.
|
| - // TODO(jacobr): Figure out why this is failing on dartium.
|
| - // assert(_element.parent == _frame);
|
| - _setContentOffset(_maxPoint.x, _maxPoint.y);
|
| - }
|
| -
|
| - void onDecelerate(num x, num y) {
|
| - _setContentOffset(x, y);
|
| - }
|
| -
|
| - void onDecelerationEnd() {
|
| - onScrollerEnd.dispatch(new Event(ScrollerEventType.SCROLLER_END));
|
| - _started = false;
|
| - }
|
| -
|
| - void onDragEnd() {
|
| - _dragInProgress = false;
|
| -
|
| - bool decelerating = false;
|
| - if (_activeGesture) {
|
| - if (_momentumEnabled) {
|
| - decelerating = _startDeceleration(_touchHandler.getEndVelocity());
|
| - }
|
| - }
|
| -
|
| - onScrollerDragEnd.dispatch(new Event(ScrollerEventType.DRAG_END));
|
| -
|
| - if (!decelerating) {
|
| - _snapContentOffsetToBounds();
|
| - onScrollerEnd.dispatch(new Event(ScrollerEventType.SCROLLER_END));
|
| - _started = false;
|
| - } else {
|
| - onDecelStart.dispatch(new Event(ScrollerEventType.DECEL_START));
|
| - }
|
| - _activeGesture = false;
|
| - }
|
| -
|
| - void onDragMove() {
|
| - if (_isStopping || (!_activeGesture && _dragInProgress)) {
|
| - return;
|
| - }
|
| -
|
| - assert(_contentStartOffset != null); // Content start not set
|
| - Coordinate contentStart = _contentStartOffset;
|
| - num newX = contentStart.x + _touchHandler.getDragDeltaX();
|
| - num newY = contentStart.y + _touchHandler.getDragDeltaY();
|
| - newY = _shouldScrollVertically() ?
|
| - _adjustValue(newY, _minPoint.y, _maxPoint.y) : 0;
|
| - newX = _shouldScrollHorizontally() ?
|
| - _adjustValue(newX, _minPoint.x, _maxPoint.x) : 0;
|
| - if (!_activeGesture) {
|
| - _activeGesture = true;
|
| - _dragInProgress = true;
|
| - }
|
| - if (!_started) {
|
| - _started = true;
|
| - onScrollerStart.dispatch(new Event(ScrollerEventType.SCROLLER_START));
|
| - }
|
| - _setContentOffset(newX, newY);
|
| - }
|
| -
|
| - bool onDragStart(TouchEvent e) {
|
| - if (e.touches.length > 1) {
|
| - return false;
|
| - }
|
| - bool shouldHorizontal = _shouldScrollHorizontally();
|
| - bool shouldVertical = _shouldScrollVertically();
|
| - bool verticalish = _touchHandler.getDragDeltaY().abs() >
|
| - _touchHandler.getDragDeltaX().abs();
|
| - return !!(shouldVertical || shouldHorizontal && !verticalish);
|
| - }
|
| -
|
| - void onTouchEnd() {
|
| - }
|
| -
|
| - /**
|
| - * Prepare the scrollable area for possible movement.
|
| - */
|
| - bool onTouchStart(TouchEvent e) {
|
| - reconfigure(() {
|
| - final touch = e.touches[0];
|
| - if (_momentum.decelerating) {
|
| - e.preventDefault();
|
| - e.stopPropagation();
|
| - stop();
|
| - }
|
| - _contentStartOffset = _contentOffset.clone();
|
| - _snapContentOffsetToBounds();
|
| - });
|
| - return true;
|
| - }
|
| -
|
| - /**
|
| - * Recalculate dimensions of the frame and the content. Adjust the minPoint
|
| - * and maxPoint allowed for scrolling and scroll to a valid position. Call
|
| - * this method if you know the frame or content has been updated. Called
|
| - * internally on every touchstart event the frame receives.
|
| - */
|
| - void reconfigure(Callback callback) {
|
| - _resize(() {
|
| - _snapContentOffsetToBounds();
|
| - callback();
|
| - });
|
| - }
|
| -
|
| - void reset() {
|
| - stop();
|
| - _touchHandler.reset();
|
| - _maxOffset.x = 0;
|
| - _maxOffset.y = 0;
|
| - _minOffset.x = 0;
|
| - _minOffset.y = 0;
|
| - reconfigure(() => _setContentOffset(_maxPoint.x, _maxPoint.y));
|
| - }
|
| -
|
| - /**
|
| - * Recalculate dimensions of the frame and the content. Adjust the minPoint
|
| - * and maxPoint allowed for scrolling.
|
| - */
|
| - void _resize(Callback callback) {
|
| - final frameRect = _frame.rect;
|
| - Future contentSizeFuture;
|
| -
|
| - if (_lookupContentSizeDelegate !== null) {
|
| - contentSizeFuture = _lookupContentSizeDelegate();
|
| - contentSizeFuture.then((Size size) {
|
| - _contentSize = size;
|
| - });
|
| - } else {
|
| - contentSizeFuture = _element.rect;
|
| - contentSizeFuture.then((ElementRect rect) {
|
| - _contentSize = new Size(rect.scroll.width, rect.scroll.height);
|
| - });
|
| - }
|
| -
|
| - joinFutures(<Future>[frameRect, contentSizeFuture], () {
|
| - _scrollSize = new Size(frameRect.value.offset.width,
|
| - frameRect.value.offset.height);
|
| - Size adjusted = _getAdjustedContentSize();
|
| - _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y);
|
| - _minPoint = new Coordinate(
|
| - Math.min(
|
| - _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x),
|
| - Math.min(
|
| - _scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y));
|
| - callback();
|
| - });
|
| - }
|
| -
|
| - Coordinate _snapToBounds(num x, num y) {
|
| - num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x);
|
| - num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y);
|
| - return new Coordinate(clampX, clampY);
|
| - }
|
| -
|
| - /**
|
| - * Translate the content to a new position specified in px.
|
| - */
|
| - void _setContentOffset(num x, num y) {
|
| - _contentOffset.x = x;
|
| - _contentOffset.y = y;
|
| - _setOffsetFunction(_element, x, y);
|
| - onContentMoved.dispatch(new Event(ScrollerEventType.CONTENT_MOVED));
|
| - }
|
| -
|
| - /**
|
| - * Enable or disable momentum.
|
| - */
|
| - void setMomentum(bool enable) {
|
| - _momentumEnabled = enable;
|
| - }
|
| -
|
| - /**
|
| - * Sets the vertical scrolled offset of the element where [y] is the amount
|
| - * of vertical space to be scrolled, in pixels.
|
| - */
|
| - void setVerticalOffset(num y) {
|
| - _setContentOffset(_contentOffset.x, y);
|
| - }
|
| -
|
| - /**
|
| - * Whether the scrollable area should scroll horizontally. Only
|
| - * returns true if the client has enabled horizontal scrolling, and the
|
| - * content is wider than the frame.
|
| - */
|
| - bool _shouldScrollHorizontally() {
|
| - return horizontalEnabled && _scrollSize.width < _contentSize.width;
|
| - }
|
| -
|
| - /**
|
| - * Whether the scrollable area should scroll vertically. Only
|
| - * returns true if the client has enabled vertical scrolling.
|
| - * Vertical bouncing will occur even if frame is taller than content, because
|
| - * this is what iPhone web apps tend to do. If this is not the desired
|
| - * behavior, either disable vertical scrolling for this scroller or add a
|
| - * 'bouncing' parameter to this interface.
|
| - */
|
| - bool _shouldScrollVertically() {
|
| - return verticalEnabled;
|
| - }
|
| -
|
| - /**
|
| - * In the event that the content is currently beyond the bounds of
|
| - * the frame, snap it back in to place.
|
| - */
|
| - void _snapContentOffsetToBounds() {
|
| - num clampX =
|
| - GoogleMath.clamp(_minPoint.x, _contentOffset.x, _maxPoint.x);
|
| - num clampY =
|
| - GoogleMath.clamp(_minPoint.y, _contentOffset.y, _maxPoint.y);
|
| - if (_contentOffset.x != clampX || _contentOffset.y != clampY) {
|
| - _setContentOffset(clampX, clampY);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Initiate the deceleration behavior given a flick [velocity].
|
| - * Returns true if deceleration has been initiated.
|
| - */
|
| - bool _startDeceleration(Coordinate velocity,
|
| - [num decelerationFactor = null]) {
|
| - if (!_shouldScrollHorizontally()) {
|
| - velocity.x = 0;
|
| - }
|
| - if (!_shouldScrollVertically()) {
|
| - velocity.y = 0;
|
| - }
|
| - assert(_minPoint != null); // Min point is not set
|
| - assert(_maxPoint != null); // Max point is not set
|
| - return _momentum.start(velocity, _minPoint, _maxPoint, _contentOffset,
|
| - decelerationFactor);
|
| - }
|
| -
|
| - Coordinate stop() {
|
| - return _momentum.stop();
|
| - }
|
| -
|
| - /**
|
| - * Stop the deceleration of the scrollable content given a new position in px.
|
| - */
|
| - void _stopDecelerating(num x, num y) {
|
| - _momentum.stop();
|
| - _setContentOffset(x, y);
|
| - }
|
| -
|
| - static Function _getOffsetFunction(int scrollTechnique) {
|
| - return scrollTechnique == ScrollerScrollTechnique.TRANSFORM_3D ?
|
| - (el, x, y) { FxUtil.setTranslate(el, x, y, 0); } :
|
| - (el, x, y) { FxUtil.setLeftAndTop(el, x, y); };
|
| - }
|
| -}
|
| -
|
| -// TODO(jacobr): cleanup this class of enum constants.
|
| -class ScrollerEventType {
|
| - static final SCROLLER_START = "scroller:scroll_start";
|
| - static final SCROLLER_END = "scroller:scroll_end";
|
| - static final DRAG_END = "scroller:drag_end";
|
| - static final CONTENT_MOVED = "scroller:content_moved";
|
| - static final DECEL_START = "scroller:decel_start";
|
| -}
|
| -
|
| -// TODO(jacobr): for now this ignores capture.
|
| -class SimpleEventListenerList implements EventListenerList {
|
| - // Ignores capture for now.
|
| - List<EventListener> _listeners;
|
| -
|
| - SimpleEventListenerList() : _listeners = new List<EventListener>() { }
|
| -
|
| - EventListenerList add(EventListener handler, [bool useCapture = false]) {
|
| - _add(handler, useCapture);
|
| - return this;
|
| - }
|
| -
|
| - EventListenerList remove(EventListener handler, [bool useCapture = false]) {
|
| - _remove(handler, useCapture);
|
| - return this;
|
| - }
|
| -
|
| - EventListenerList addCapture(EventListener handler) {
|
| - _add(handler, true);
|
| - return this;
|
| - }
|
| -
|
| - EventListenerList removeCapture(EventListener handler) {
|
| - _remove(handler, true);
|
| - return this;
|
| - }
|
| -
|
| - void _add(EventListener handler, bool useCapture) {
|
| - _listeners.add(handler);
|
| - }
|
| -
|
| - void _remove(EventListener handler, bool useCapture) {
|
| - // TODO(jacobr): implemenet as needed.
|
| - throw 'Not implemented yet.';
|
| - }
|
| -
|
| - bool dispatch(Event evt) {
|
| - for (EventListener listener in _listeners) {
|
| - listener(evt);
|
| - }
|
| - }
|
| -}
|
| -
|
| -class ScrollerScrollTechnique {
|
| - static final TRANSFORM_3D = 1;
|
| - static final RELATIVE_POSITIONING = 2;
|
| -}
|
|
|