Revision: 9989
Author: [email protected]
Date: Wed Apr 13 11:00:31 2011
Log: Cherry picking r9988 into releases/2.3.
http://code.google.com/p/google-web-toolkit/source/detail?r=9989
Modified:
/releases/2.3/user/src/com/google/gwt/touch/client/TouchScroller.java
/releases/2.3/user/test/com/google/gwt/touch/client/TouchScrollTest.java
=======================================
--- /releases/2.3/user/src/com/google/gwt/touch/client/TouchScroller.java
Tue Mar 22 11:44:40 2011
+++ /releases/2.3/user/src/com/google/gwt/touch/client/TouchScroller.java
Wed Apr 13 11:00:31 2011
@@ -30,11 +30,14 @@
import com.google.gwt.event.dom.client.TouchMoveHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.dom.client.TouchStartHandler;
+import com.google.gwt.event.logical.shared.ResizeEvent;
+import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.touch.client.Momentum.State;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
+import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HasScrolling;
import java.util.ArrayList;
@@ -99,6 +102,7 @@
private final Point initialPosition = getWidgetScrollPosition();
private int lastElapsedMillis = 0;
private State state;
+ private HandlerRegistration windowResizeHandler;
/**
* Construct a {@link MomentumCommand}.
@@ -107,6 +111,18 @@
*/
public MomentumCommand(Point endVelocity) {
state = momentum.createState(initialPosition, endVelocity);
+
+ /**
+ * If the user resizes the window (which happens on orientation
change of
+ * a mobile device), cancel the momentum. The scrollable widget may
be
+ * resized, which will cause its content to reflow and invalidates
the
+ * current scrolling position.
+ */
+ windowResizeHandler = Window.addResizeHandler(new ResizeHandler() {
+ public void onResize(ResizeEvent event) {
+ finish();
+ }
+ });
}
public boolean execute() {
@@ -115,6 +131,7 @@
* disabled.
*/
if (this != momentumCommand) {
+ finish();
return false;
}
@@ -127,9 +144,9 @@
// Calculate the new state.
boolean notDone = momentum.updateState(state);
- // Momementum is finished, so the user is free to click.
+ // Momentum is finished, so the user is free to click.
if (!notDone) {
- setBustNextClick(false);
+ finish();
}
/*
@@ -139,6 +156,20 @@
setWidgetScrollPosition(state.getPosition());
return notDone;
}
+
+ /**
+ * Finish and cleanup this momentum command.
+ */
+ private void finish() {
+ if (windowResizeHandler != null) {
+ windowResizeHandler.removeHandler();
+ windowResizeHandler = null;
+ }
+ if (this == momentumCommand) {
+ momentumCommand = null;
+ setBustNextClick(false);
+ }
+ }
}
/**
@@ -493,9 +524,6 @@
if (!touching) {
return;
}
-
- // Prevent native scrolling.
- event.preventDefault();
// Check if we should start dragging.
Touch touch = getTouchFromEvent(event);
@@ -507,11 +535,60 @@
double absDiffX = Math.abs(diff.getX());
double absDiffY = Math.abs(diff.getY());
if (absDiffX > MIN_TRACKING_FOR_DRAG || absDiffY >
MIN_TRACKING_FOR_DRAG) {
+ /*
+ * Check if we should defer to native scrolling. If the scrollable
+ * widget is already scrolled as far as it will go, then we don't
want
+ * to prevent scrolling of the document.
+ *
+ * We cannot prevent native scrolling in only one direction (ie. we
+ * cannot allow native horizontal scrolling but prevent native
vertical
+ * scrolling), so we make a best guess based on the direction of
the
+ * drag.
+ */
+ if (absDiffX > absDiffY) {
+ /*
+ * The user scrolled primarily in the horizontal direction, so
check
+ * if we should defer left/right scrolling to the document.
+ */
+ int hPosition = widget.getHorizontalScrollPosition();
+ int hMin = widget.getMinimumHorizontalScrollPosition();
+ int hMax = widget.getMaximumHorizontalScrollPosition();
+ if (diff.getX() < 0 && hMax <= hPosition) {
+ // Already scrolled to the right.
+ cancelAll();
+ return;
+ } else if (diff.getX() > 0 && hMin >= hPosition) {
+ // Already scrolled to the left.
+ cancelAll();
+ return;
+ }
+ } else {
+ /*
+ * The user scrolled primarily in the vertical direction, so
check if
+ * we should defer up/down scrolling to the document.
+ */
+ int vPosition = widget.getVerticalScrollPosition();
+ int vMin = widget.getMinimumVerticalScrollPosition();
+ int vMax = widget.getMaximumVerticalScrollPosition();
+ if (diff.getY() < 0 && vMax <= vPosition) {
+ // Already scrolled to the bottom.
+ cancelAll();
+ return;
+ } else if (diff.getY() > 0 && vMin >= vPosition) {
+ // Already scrolled to the top.
+ cancelAll();
+ return;
+ }
+ }
+
// Start dragging.
dragging = true;
onDragStart(event);
}
}
+
+ // Prevent native document level scrolling.
+ event.preventDefault();
if (dragging) {
// Continue dragging.
@@ -648,8 +725,6 @@
/**
* Get the scroll position of the widget.
- *
- * @param position the current scroll position
*/
private Point getWidgetScrollPosition() {
return new Point(widget.getHorizontalScrollPosition(),
widget.getVerticalScrollPosition());
@@ -669,7 +744,7 @@
event.getNativeEvent().preventDefault();
setBustNextClick(false);
}
- };
+ }
});
} else if (!doBust && bustClickHandler != null) {
bustClickHandler.removeHandler();
=======================================
---
/releases/2.3/user/test/com/google/gwt/touch/client/TouchScrollTest.java
Tue Mar 22 11:44:40 2011
+++
/releases/2.3/user/test/com/google/gwt/touch/client/TouchScrollTest.java
Wed Apr 13 11:00:31 2011
@@ -16,6 +16,7 @@
package com.google.gwt.touch.client;
import com.google.gwt.core.client.Duration;
+import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.event.dom.client.TouchEvent;
@@ -56,9 +57,9 @@
*/
public CustomScrollPanel() {
this.minVerticalScrollPosition = 0;
- this.maxVerticalScrollPosition = Integer.MAX_VALUE;
+ this.maxVerticalScrollPosition = 5000;
this.minHorizontalScrollPosition = 0;
- this.maxHorizontalScrollPosition = Integer.MAX_VALUE;
+ this.maxHorizontalScrollPosition = 5000;
}
@Override
@@ -206,7 +207,7 @@
CustomTouchEvent event = new CustomTouchEvent();
event.setNativeEvent(createNativeTouchEvent());
return event;
- };
+ }
/**
* Create a mock TouchMoveEvent for the specified x and y coordinate.
@@ -218,7 +219,7 @@
private static TouchEvent<?> createTouchMoveEvent(int x, int y) {
// TouchScroller doesn't care about the actual event subclass.
return createTouchStartEvent(x, y);
- };
+ }
/**
* Create a mock {@link TouchStartEvent} for the specified x and y
coordinate.
@@ -233,7 +234,7 @@
nativeEvent.getTouches().push(createTouch(x, y));
event.setNativeEvent(nativeEvent);
return event;
- };
+ }
private CustomTouchScroller scroller;
private CustomScrollPanel scrollPanel;
@@ -276,6 +277,22 @@
assertNull("TouchScroll created, but touch is not supported",
scroller);
}
}
+
+ public void testDeferToNativeScrollingBottom() {
+ testDeferToNativeScrolling(0,
scrollPanel.getMaximumVerticalScrollPosition(), 0, -100);
+ }
+
+ public void testDeferToNativeScrollingLeft() {
+ testDeferToNativeScrolling(0, 0, 100, 0);
+ }
+
+ public void testDeferToNativeScrollingRight() {
+
testDeferToNativeScrolling(scrollPanel.getMaximumHorizontalScrollPosition(),
0, -100, 0);
+ }
+
+ public void testDeferToNativeScrollingTop() {
+ testDeferToNativeScrolling(0, 0, 0, 100);
+ }
/**
* Test that touch events correctly initiate drag events.
@@ -295,21 +312,21 @@
assertFalse(scroller.isDragging());
// Move, but not enough to drag.
- scroller.onTouchMove(createTouchMoveEvent(1, 0));
+ scroller.onTouchMove(createTouchMoveEvent(-1, 0));
scroller.assertOnDragStartCalled(false);
scroller.assertOnDragMoveCalled(false);
assertTrue(scroller.isTouching());
assertFalse(scroller.isDragging());
// Move.
- scroller.onTouchMove(createTouchMoveEvent(100, 0));
+ scroller.onTouchMove(createTouchMoveEvent(-100, 0));
scroller.assertOnDragStartCalled(true);
scroller.assertOnDragMoveCalled(true);
assertTrue(scroller.isTouching());
assertTrue(scroller.isDragging());
// Move again.
- scroller.onTouchMove(createTouchMoveEvent(200, 0));
+ scroller.onTouchMove(createTouchMoveEvent(-200, 0));
scroller.assertOnDragStartCalled(false); // drag already started.
scroller.assertOnDragMoveCalled(true);
assertTrue(scroller.isTouching());
@@ -323,6 +340,39 @@
assertFalse(scroller.isTouching());
assertFalse(scroller.isDragging());
}
+
+ /**
+ * Test that when momentum ends, the momentum command is set to null (and
+ * isMomentumActive() returns false).
+ */
+ public void testMomentumEnd() {
+ // Use a short lived momentum.
+ scroller.setMomentum(new DefaultMomentum() {
+ @Override
+ public boolean updateState(State state) {
+ // Immediately end momentum.
+ return false;
+ }
+ });
+
+ // Start a drag sequence.
+ double millis = Duration.currentTimeMillis();
+ scroller.getRecentTouchPosition().setTemporalPoint(new Point(0, 0),
millis);
+ scroller.getLastTouchPosition().setTemporalPoint(new Point(100, 100),
millis + 100);
+
+ // End the drag sequence.
+ scroller.onDragEnd(createTouchEndEvent());
+ scroller.assertOnDragEndCalled(true);
+ assertFalse(scroller.isTouching());
+ assertFalse(scroller.isDragging());
+ assertTrue(scroller.isMomentumActive());
+
+ // Force momentum to run, which causes it to end.
+ getMomentumCommand(scroller).execute();
+ assertFalse(scroller.isTouching());
+ assertFalse(scroller.isDragging());
+ assertFalse(scroller.isMomentumActive());
+ }
public void testOnDragEnd() {
// Start a drag sequence.
@@ -486,12 +536,14 @@
protected void gwtSetUp() throws Exception {
// Create and attach a widget that has scrolling.
scrollPanel = new CustomScrollPanel();
- scrollPanel.setTouchScrollingDisabled(true);
scrollPanel.setPixelSize(500, 500);
Label content = new Label("Content");
content.setPixelSize(10000, 10000);
RootPanel.get().add(scrollPanel);
+ // Disabled touch scrolling because we will add our own scroller.
+ scrollPanel.setTouchScrollingDisabled(true);
+
// Add scrolling support.
scroller = new CustomTouchScroller(scrollPanel);
}
@@ -511,4 +563,42 @@
scrollPanel = null;
scroller = null;
}
-}
+
+ /**
+ * Get the momentum command from the specified {@link TouchScroller}.
+ */
+ private native RepeatingCommand getMomentumCommand(TouchScroller
scroller) /*-{
+ return
[email protected]::momentumCommand;
+ }-*/;
+
+ /**
+ * Test that {@link TouchScroller} defers to native scrolling if the
+ * scrollable widget is already scrolled as far as it can go.
+ *
+ * @param hStart the starting horizontal scroll position
+ * @param vStart the starting vertical scroll position
+ * @param xEnd the ending x touch coordinate
+ * @param yEnd the ending y touch coordinate
+ */
+ private void testDeferToNativeScrolling(int hStart, int vStart, int
xEnd, int yEnd) {
+ // Disable momentum for this test.
+ scroller.setMomentum(null);
+
+ // Scroll to the left.
+ scrollPanel.setHorizontalScrollPosition(hStart);
+ scrollPanel.setVerticalScrollPosition(vStart);
+
+ // Start touching.
+ scroller.onTouchStart(createTouchStartEvent(0, 0));
+ scroller.assertOnDragStartCalled(false);
+ assertTrue(scroller.isTouching());
+ assertFalse(scroller.isDragging());
+
+ // Move to the left.
+ scroller.onTouchMove(createTouchMoveEvent(xEnd, yEnd));
+ scroller.assertOnDragStartCalled(false);
+ scroller.assertOnDragMoveCalled(false);
+ assertFalse(scroller.isTouching());
+ assertFalse(scroller.isDragging());
+ }
+}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors