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

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

Issue 9382027: Move client/{base, observable, layout, touch, util, view} to samples/ui_lib . (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « client/touch/Scrollbar.dart ('k') | client/touch/TimeUtil.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Implementation of a custom scrolling behavior.
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
9 * of a page.
10 *
11 * To use this scrolling behavior you need to define a frame and the content.
12 * The frame defines the area that the content will scroll within. The frame and
13 * content must both be HTML Elements, with the content being a direct child of
14 * the frame. Usually the frame is smaller in size than the content. This is
15 * not necessary though, if the content is smaller then bouncing will occur to
16 * provide feedback that you are past the scrollable area.
17 *
18 * The scrolling behavior works using the webkit translate3d transformation,
19 * which means browsers that do not have hardware accelerated transformations
20 * will not perform as well using this. Simple scrolling should be fine even
21 * without hardware acceleration, but animating momentum and deceleration is
22 * unacceptably slow without it. There is also the option to use relative
23 * positioning (setting the left and top styles).
24 *
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
27 * this you may see the text content of the scrollable area changing size as it
28 * moves.
29 *
30 * The behavior is intended to support vertical and horizontal scrolling, and
31 * scrolling with momentum when a touch gesture flicks with enough velocity.
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 }
49
50 class Scroller implements Draggable, MomentumDelegate {
51
52 /** Pixels to move each time an arrow key is pressed. */
53 static final ARROW_KEY_DELTA = 30;
54 static final SCROLL_WHEEL_VELOCITY = 0.01;
55 static final FAST_SNAP_DECELERATION_FACTOR = 0.84;
56 static final PAGE_KEY_SCROLL_FRACTION = .85;
57
58 // TODO(jacobr): remove this static variable.
59 static bool _dragInProgress = false;
60
61 /** The node that will actually scroll. */
62 Element _element;
63
64 /**
65 * Frame is the node that will serve as the container for the scrolling
66 * content.
67 */
68 Element _frame;
69
70 /** Touch manager to track the events on the scrollable area. */
71 TouchHandler _touchHandler;
72
73 Momentum _momentum;
74
75 EventListenerList _onScrollerStart;
76 EventListenerList _onScrollerEnd;
77 EventListenerList _onScrollerDragEnd;
78 EventListenerList _onContentMoved;
79 EventListenerList _onDecelStart;
80
81 /** Set if vertical scrolling should be enabled. */
82 bool verticalEnabled;
83
84 /** Set if horizontal scrolling should be enabled. */
85 bool horizontalEnabled;
86
87 /**
88 * Set if momentum should be enabled.
89 */
90 bool _momentumEnabled;
91
92 /** Set which type of scrolling translation technique should be used. */
93 int _scrollTechnique;
94
95 /**
96 * The maximum coordinate that the left upper corner of the content can scroll
97 * to.
98 */
99 Coordinate _maxPoint;
100
101 /**
102 * An offset to subtract from the maximum coordinate that the left upper
103 * corner of the content can scroll to.
104 */
105 Coordinate _maxOffset;
106
107 /**
108 * An offset to add to the minimum coordinate that the left upper corner of
109 * the content can scroll to.
110 */
111 Coordinate _minOffset;
112
113 /** Initialize the current content offset. */
114 Coordinate _contentOffset;
115
116 // TODO(jacobr): the function type is
117 // [:Function(Element, num, num)->void:].
118 /**
119 * The function to use that will actually translate the scrollable node.
120 */
121 Function _setOffsetFunction;
122 /**
123 * Function that returns the content size that can be specified instead of
124 * querying the DOM.
125 */
126 Function _lookupContentSizeDelegate;
127
128 Size _scrollSize;
129 Size _contentSize;
130 Coordinate _minPoint;
131 bool _isStopping = false;
132 Coordinate _contentStartOffset;
133 bool _started = false;
134 bool _activeGesture = false;
135 ScrollWatcher _scrollWatcher;
136
137 Scroller(Element scrollableElem, [this.verticalEnabled = false,
138 this.horizontalEnabled = false,
139 momentumEnabled = true,
140 lookupContentSizeDelegate = null,
141 num defaultDecelerationFactor = 1,
142 int scrollTechnique = null, bool capture = false])
143 : _momentumEnabled = momentumEnabled,
144 _lookupContentSizeDelegate = lookupContentSizeDelegate,
145 _element = scrollableElem,
146 _frame = scrollableElem.parent,
147 _scrollTechnique = scrollTechnique !== null
148 ? scrollTechnique : ScrollerScrollTechnique.TRANSFORM_3D,
149 _minPoint = new Coordinate(0, 0),
150 _maxPoint = new Coordinate(0, 0),
151 _maxOffset = new Coordinate(0, 0),
152 _minOffset = new Coordinate(0, 0),
153 _contentOffset = new Coordinate(0, 0) {
154 _touchHandler = new TouchHandler(this, scrollableElem.parent);
155 _momentum = new Momentum(this, defaultDecelerationFactor);
156
157 Element parentElem = scrollableElem.parent;
158 assert(parentElem != null);
159 _setOffsetFunction = _getOffsetFunction(_scrollTechnique);
160 _touchHandler.setDraggable(this);
161 _touchHandler.enable(capture);
162
163 _frame.on.mouseWheel.add((e) {
164 if (e.wheelDeltaY != 0 && verticalEnabled ||
165 e.wheelDeltaX != 0 && horizontalEnabled) {
166 num x = horizontalEnabled ? e.wheelDeltaX : 0;
167 num y = verticalEnabled ? e.wheelDeltaY : 0;
168 throwDelta(x, y, FAST_SNAP_DECELERATION_FACTOR);
169 e.preventDefault();
170 }
171 });
172
173 _frame.on.keyDown.add((KeyboardEvent e) {
174 bool handled = false;
175 // We ignore key events where further scrolling in that direction
176 // would have no impact which matches default browser behavior with
177 // nested scrollable areas.
178
179 switch(e.keyCode) {
180 case 33: // page-up
181 throwDelta(
182 0,
183 _scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
184 handled = true;
185 break;
186 case 34: // page-down
187 throwDelta(
188 0, -_scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
189 handled = true;
190 break;
191 case 35: // End
192 throwTo(_maxPoint.x, _minPoint.y,
193 FAST_SNAP_DECELERATION_FACTOR);
194 handled = true;
195 break;
196 case 36: // Home
197 throwTo(_maxPoint.x,_maxPoint.y,
198 FAST_SNAP_DECELERATION_FACTOR);
199 handled = true;
200 break;
201 /* TODO(jacobr): enable arrow keys when the don't conflict with other
202 application keyboard shortcuts.
203 case 38: // up
204 handled = throwDelta(
205 0,
206 ARROW_KEY_DELTA,
207 FAST_SNAP_DECELERATION_FACTOR);
208 break;
209 case 40: // down
210 handled = throwDelta(
211 0, -ARROW_KEY_DELTA,
212 FAST_SNAP_DECELERATION_FACTOR);
213 break;
214 case 37: // left
215 handled = throwDelta(
216 ARROW_KEY_DELTA, 0,
217 FAST_SNAP_DECELERATION_FACTOR);
218 break;
219 case 39: // right
220 handled = throwDelta(
221 -ARROW_KEY_DELTA,
222 0,
223 FAST_SNAP_DECELERATION_FACTOR);
224 break;
225 */
226 }
227 if (handled) {
228 e.preventDefault();
229 }
230 });
231 // The scrollable element must be relatively positioned.
232 // TODO(jacobr): this assert fires asynchronously which could be confusing.
233 if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) {
234 _element.computedStyle.then((CSSStyleDeclaration style) {
235 assert(style.position != "static");
236 });
237 }
238
239 _initLayer();
240 }
241
242 EventListenerList get onScrollerStart() {
243 if (_onScrollerStart === null) {
244 _onScrollerStart = new SimpleEventListenerList();
245 }
246 return _onScrollerStart;
247 }
248
249 EventListenerList get onScrollerEnd() {
250 if (_onScrollerEnd === null) {
251 _onScrollerEnd = new SimpleEventListenerList();
252 }
253 return _onScrollerEnd;
254 }
255
256 EventListenerList get onScrollerDragEnd() {
257 if (_onScrollerDragEnd === null) {
258 _onScrollerDragEnd = new SimpleEventListenerList();
259 }
260 return _onScrollerDragEnd;
261 }
262
263 EventListenerList get onContentMoved() {
264 if (_onContentMoved === null) {
265 _onContentMoved = new SimpleEventListenerList();
266 }
267 return _onContentMoved;
268 }
269
270 EventListenerList get onDecelStart() {
271 if (_onDecelStart === null) {
272 _onDecelStart = new SimpleEventListenerList();
273 }
274 return _onDecelStart;
275 }
276
277
278 /**
279 * Add a scroll listener. This allows other classes to subscribe to scroll
280 * notifications from this scroller.
281 */
282 void addScrollListener(ScrollListener listener) {
283 if (_scrollWatcher === null) {
284 _scrollWatcher = new ScrollWatcher(this);
285 _scrollWatcher.initialize();
286 }
287 _scrollWatcher.addListener(listener);
288 }
289
290 /**
291 * Adjust the new calculated scroll position based on the minimum allowed
292 * position and returns the adjusted scroll value.
293 */
294 num _adjustValue(num newPosition, num minPosition,
295 num maxPosition) {
296 assert(minPosition <= maxPosition);
297
298 if (newPosition < minPosition) {
299 newPosition -= (newPosition - minPosition) / 2;
300 } else {
301 if (newPosition > maxPosition) {
302 newPosition -= (newPosition - maxPosition) / 2;
303 }
304 }
305 return newPosition;
306 }
307
308 /**
309 * Coordinate we would end up at if we did nothing.
310 */
311 Coordinate get currentTarget() {
312 Coordinate end = _momentum.destination;
313 if (end === null) {
314 end = _contentOffset;
315 }
316 return end;
317 }
318
319 Coordinate get contentOffset() => _contentOffset;
320
321 /**
322 * Animate the position of the scroller to the specified [x], [y] coordinates
323 * by applying the throw gesture with the correct velocity to end at that
324 * location.
325 */
326 void throwTo(num x, num y, [num decelerationFactor = null]) {
327 reconfigure(() {
328 final snappedTarget = _snapToBounds(x, y);
329 // If a deceleration factor is not specified, use the existing
330 // deceleration factor specified by the momentum simulator.
331 if (decelerationFactor == null) {
332 decelerationFactor = _momentum.decelerationFactor;
333 }
334
335 if (snappedTarget != currentTarget) {
336 _momentum.abort();
337
338 _startDeceleration(
339 _momentum.calculateVelocity(
340 _contentOffset,
341 snappedTarget,
342 decelerationFactor),
343 decelerationFactor);
344 onDecelStart.dispatch(new Event(ScrollerEventType.DECEL_START));
345 }
346 });
347 }
348
349 void throwDelta(num deltaX, num deltaY, [num decelerationFactor = null]) {
350 Coordinate start = _contentOffset;
351 Coordinate end = currentTarget;
352 int x = end.x.toInt();
353 int y = end.y.toInt();
354 // If we are throwing in the opposite direction of the existing momentum,
355 // cancel the current momentum.
356 if (deltaX != 0 && deltaX.isNegative() != (end.x - start.x).isNegative()) {
357 x = start.x;
358 }
359 if (deltaY != 0 && deltaY.isNegative() != (end.y - start.y).isNegative()) {
360 y = start.y;
361 }
362 x += deltaX.toInt();
363 y += deltaY.toInt();
364 throwTo(x, y, decelerationFactor);
365 }
366
367 void setPosition(num x, num y) {
368 _momentum.abort();
369 _contentOffset.x = x;
370 _contentOffset.y = y;
371 _snapContentOffsetToBounds();
372 _setContentOffset(_contentOffset.x, _contentOffset.y);
373 }
374 /**
375 * Adjusted content size is a size with the combined largest height and width
376 * of both the content and the frame.
377 */
378 Size _getAdjustedContentSize() {
379 return new Size(Math.max(_scrollSize.width, _contentSize.width),
380 Math.max(_scrollSize.height, _contentSize.height));
381 }
382
383 // TODO(jmesserly): these should be properties instead of get* methods
384 num getDefaultVerticalOffset() => _maxPoint.y;
385 Element getElement() => _element;
386 Element getFrame() => _frame;
387 num getHorizontalOffset() => _contentOffset.x;
388
389 /**
390 * [x] Value to use as reference for percent measurement. If
391 * none is provided then the content's current x offset will be used.
392 * Returns the percent of the page scrolled horizontally.
393 */
394 num getHorizontalScrollPercent([num x = null]) {
395 x = x !== null ? x : _contentOffset.x;
396 return (x - _minPoint.x) / (_maxPoint.x - _minPoint.x);
397 }
398
399 num getMaxPointY()=> _maxPoint.y;
400 num getMinPointY() => _minPoint.y;
401 Momentum get momentum() => _momentum;
402
403 /**
404 * Provide access to the touch handler that the scroller created to manage
405 * touch events.
406 */
407 TouchHandler getTouchHandler() => _touchHandler;
408 num getVerticalOffset() => _contentOffset.y;
409
410 /**
411 * [y] value is used as reference for percent measurement. If
412 * none is provided then the content's current y offset will be used.
413 */
414 num getVerticalScrollPercent([num y = null]) {
415 y = y !== null ? y : _contentOffset.y;
416 return (y - _minPoint.y) / Math.max(1, _maxPoint.y - _minPoint.y);
417 }
418
419 /**
420 * Initialize the dom elements necessary for the scrolling to work.
421 */
422 void _initLayer() {
423 // The scrollable node provided to Scroller must be a direct child
424 // of the scrollable frame.
425 // TODO(jacobr): Figure out why this is failing on dartium.
426 // assert(_element.parent == _frame);
427 _setContentOffset(_maxPoint.x, _maxPoint.y);
428 }
429
430 void onDecelerate(num x, num y) {
431 _setContentOffset(x, y);
432 }
433
434 void onDecelerationEnd() {
435 onScrollerEnd.dispatch(new Event(ScrollerEventType.SCROLLER_END));
436 _started = false;
437 }
438
439 void onDragEnd() {
440 _dragInProgress = false;
441
442 bool decelerating = false;
443 if (_activeGesture) {
444 if (_momentumEnabled) {
445 decelerating = _startDeceleration(_touchHandler.getEndVelocity());
446 }
447 }
448
449 onScrollerDragEnd.dispatch(new Event(ScrollerEventType.DRAG_END));
450
451 if (!decelerating) {
452 _snapContentOffsetToBounds();
453 onScrollerEnd.dispatch(new Event(ScrollerEventType.SCROLLER_END));
454 _started = false;
455 } else {
456 onDecelStart.dispatch(new Event(ScrollerEventType.DECEL_START));
457 }
458 _activeGesture = false;
459 }
460
461 void onDragMove() {
462 if (_isStopping || (!_activeGesture && _dragInProgress)) {
463 return;
464 }
465
466 assert(_contentStartOffset != null); // Content start not set
467 Coordinate contentStart = _contentStartOffset;
468 num newX = contentStart.x + _touchHandler.getDragDeltaX();
469 num newY = contentStart.y + _touchHandler.getDragDeltaY();
470 newY = _shouldScrollVertically() ?
471 _adjustValue(newY, _minPoint.y, _maxPoint.y) : 0;
472 newX = _shouldScrollHorizontally() ?
473 _adjustValue(newX, _minPoint.x, _maxPoint.x) : 0;
474 if (!_activeGesture) {
475 _activeGesture = true;
476 _dragInProgress = true;
477 }
478 if (!_started) {
479 _started = true;
480 onScrollerStart.dispatch(new Event(ScrollerEventType.SCROLLER_START));
481 }
482 _setContentOffset(newX, newY);
483 }
484
485 bool onDragStart(TouchEvent e) {
486 if (e.touches.length > 1) {
487 return false;
488 }
489 bool shouldHorizontal = _shouldScrollHorizontally();
490 bool shouldVertical = _shouldScrollVertically();
491 bool verticalish = _touchHandler.getDragDeltaY().abs() >
492 _touchHandler.getDragDeltaX().abs();
493 return !!(shouldVertical || shouldHorizontal && !verticalish);
494 }
495
496 void onTouchEnd() {
497 }
498
499 /**
500 * Prepare the scrollable area for possible movement.
501 */
502 bool onTouchStart(TouchEvent e) {
503 reconfigure(() {
504 final touch = e.touches[0];
505 if (_momentum.decelerating) {
506 e.preventDefault();
507 e.stopPropagation();
508 stop();
509 }
510 _contentStartOffset = _contentOffset.clone();
511 _snapContentOffsetToBounds();
512 });
513 return true;
514 }
515
516 /**
517 * Recalculate dimensions of the frame and the content. Adjust the minPoint
518 * and maxPoint allowed for scrolling and scroll to a valid position. Call
519 * this method if you know the frame or content has been updated. Called
520 * internally on every touchstart event the frame receives.
521 */
522 void reconfigure(Callback callback) {
523 _resize(() {
524 _snapContentOffsetToBounds();
525 callback();
526 });
527 }
528
529 void reset() {
530 stop();
531 _touchHandler.reset();
532 _maxOffset.x = 0;
533 _maxOffset.y = 0;
534 _minOffset.x = 0;
535 _minOffset.y = 0;
536 reconfigure(() => _setContentOffset(_maxPoint.x, _maxPoint.y));
537 }
538
539 /**
540 * Recalculate dimensions of the frame and the content. Adjust the minPoint
541 * and maxPoint allowed for scrolling.
542 */
543 void _resize(Callback callback) {
544 final frameRect = _frame.rect;
545 Future contentSizeFuture;
546
547 if (_lookupContentSizeDelegate !== null) {
548 contentSizeFuture = _lookupContentSizeDelegate();
549 contentSizeFuture.then((Size size) {
550 _contentSize = size;
551 });
552 } else {
553 contentSizeFuture = _element.rect;
554 contentSizeFuture.then((ElementRect rect) {
555 _contentSize = new Size(rect.scroll.width, rect.scroll.height);
556 });
557 }
558
559 joinFutures(<Future>[frameRect, contentSizeFuture], () {
560 _scrollSize = new Size(frameRect.value.offset.width,
561 frameRect.value.offset.height);
562 Size adjusted = _getAdjustedContentSize();
563 _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y);
564 _minPoint = new Coordinate(
565 Math.min(
566 _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x),
567 Math.min(
568 _scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y)) ;
569 callback();
570 });
571 }
572
573 Coordinate _snapToBounds(num x, num y) {
574 num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x);
575 num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y);
576 return new Coordinate(clampX, clampY);
577 }
578
579 /**
580 * Translate the content to a new position specified in px.
581 */
582 void _setContentOffset(num x, num y) {
583 _contentOffset.x = x;
584 _contentOffset.y = y;
585 _setOffsetFunction(_element, x, y);
586 onContentMoved.dispatch(new Event(ScrollerEventType.CONTENT_MOVED));
587 }
588
589 /**
590 * Enable or disable momentum.
591 */
592 void setMomentum(bool enable) {
593 _momentumEnabled = enable;
594 }
595
596 /**
597 * Sets the vertical scrolled offset of the element where [y] is the amount
598 * of vertical space to be scrolled, in pixels.
599 */
600 void setVerticalOffset(num y) {
601 _setContentOffset(_contentOffset.x, y);
602 }
603
604 /**
605 * Whether the scrollable area should scroll horizontally. Only
606 * returns true if the client has enabled horizontal scrolling, and the
607 * content is wider than the frame.
608 */
609 bool _shouldScrollHorizontally() {
610 return horizontalEnabled && _scrollSize.width < _contentSize.width;
611 }
612
613 /**
614 * Whether the scrollable area should scroll vertically. Only
615 * returns true if the client has enabled vertical scrolling.
616 * Vertical bouncing will occur even if frame is taller than content, because
617 * this is what iPhone web apps tend to do. If this is not the desired
618 * behavior, either disable vertical scrolling for this scroller or add a
619 * 'bouncing' parameter to this interface.
620 */
621 bool _shouldScrollVertically() {
622 return verticalEnabled;
623 }
624
625 /**
626 * In the event that the content is currently beyond the bounds of
627 * the frame, snap it back in to place.
628 */
629 void _snapContentOffsetToBounds() {
630 num clampX =
631 GoogleMath.clamp(_minPoint.x, _contentOffset.x, _maxPoint.x);
632 num clampY =
633 GoogleMath.clamp(_minPoint.y, _contentOffset.y, _maxPoint.y);
634 if (_contentOffset.x != clampX || _contentOffset.y != clampY) {
635 _setContentOffset(clampX, clampY);
636 }
637 }
638
639 /**
640 * Initiate the deceleration behavior given a flick [velocity].
641 * Returns true if deceleration has been initiated.
642 */
643 bool _startDeceleration(Coordinate velocity,
644 [num decelerationFactor = null]) {
645 if (!_shouldScrollHorizontally()) {
646 velocity.x = 0;
647 }
648 if (!_shouldScrollVertically()) {
649 velocity.y = 0;
650 }
651 assert(_minPoint != null); // Min point is not set
652 assert(_maxPoint != null); // Max point is not set
653 return _momentum.start(velocity, _minPoint, _maxPoint, _contentOffset,
654 decelerationFactor);
655 }
656
657 Coordinate stop() {
658 return _momentum.stop();
659 }
660
661 /**
662 * Stop the deceleration of the scrollable content given a new position in px.
663 */
664 void _stopDecelerating(num x, num y) {
665 _momentum.stop();
666 _setContentOffset(x, y);
667 }
668
669 static Function _getOffsetFunction(int scrollTechnique) {
670 return scrollTechnique == ScrollerScrollTechnique.TRANSFORM_3D ?
671 (el, x, y) { FxUtil.setTranslate(el, x, y, 0); } :
672 (el, x, y) { FxUtil.setLeftAndTop(el, x, y); };
673 }
674 }
675
676 // TODO(jacobr): cleanup this class of enum constants.
677 class ScrollerEventType {
678 static final SCROLLER_START = "scroller:scroll_start";
679 static final SCROLLER_END = "scroller:scroll_end";
680 static final DRAG_END = "scroller:drag_end";
681 static final CONTENT_MOVED = "scroller:content_moved";
682 static final DECEL_START = "scroller:decel_start";
683 }
684
685 // TODO(jacobr): for now this ignores capture.
686 class SimpleEventListenerList implements EventListenerList {
687 // Ignores capture for now.
688 List<EventListener> _listeners;
689
690 SimpleEventListenerList() : _listeners = new List<EventListener>() { }
691
692 EventListenerList add(EventListener handler, [bool useCapture = false]) {
693 _add(handler, useCapture);
694 return this;
695 }
696
697 EventListenerList remove(EventListener handler, [bool useCapture = false]) {
698 _remove(handler, useCapture);
699 return this;
700 }
701
702 EventListenerList addCapture(EventListener handler) {
703 _add(handler, true);
704 return this;
705 }
706
707 EventListenerList removeCapture(EventListener handler) {
708 _remove(handler, true);
709 return this;
710 }
711
712 void _add(EventListener handler, bool useCapture) {
713 _listeners.add(handler);
714 }
715
716 void _remove(EventListener handler, bool useCapture) {
717 // TODO(jacobr): implemenet as needed.
718 throw 'Not implemented yet.';
719 }
720
721 bool dispatch(Event evt) {
722 for (EventListener listener in _listeners) {
723 listener(evt);
724 }
725 }
726 }
727
728 class ScrollerScrollTechnique {
729 static final TRANSFORM_3D = 1;
730 static final RELATIVE_POSITIONING = 2;
731 }
OLDNEW
« no previous file with comments | « client/touch/Scrollbar.dart ('k') | client/touch/TimeUtil.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698