aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java27
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java268
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java28
3 files changed, 109 insertions, 214 deletions
diff --git a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
index 96cb4b8a35..1025752ca9 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
@@ -3,8 +3,6 @@
*/
package com.vaadin.terminal.gwt.client.ui;
-import java.util.ArrayList;
-
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.DivElement;
@@ -24,7 +22,6 @@ import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.impl.FocusImpl;
import com.vaadin.terminal.gwt.client.BrowserInfo;
-import com.vaadin.terminal.gwt.client.VConsole;
/**
* A scrollhandlers similar to {@link ScrollPanel}.
@@ -139,11 +136,7 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
* @return the vertical scroll position, in pixels
*/
public int getScrollPosition() {
- if (getElement().getPropertyJSO("_vScrollTop") != null) {
- return getElement().getPropertyInt("_vScrollTop");
- } else {
- return getElement().getScrollTop();
- }
+ return getElement().getScrollTop();
}
/**
@@ -163,23 +156,7 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
* the new vertical scroll position, in pixels
*/
public void setScrollPosition(int position) {
- if (isAndroidWithBrokenScrollTop()) {
- ArrayList<com.google.gwt.dom.client.Element> elements = TouchScrollDelegate
- .getElements(getElement());
- for (com.google.gwt.dom.client.Element el : elements) {
- final Style style = el.getStyle();
- style.setProperty("webkitTransform", "translate3d(0px,"
- + -position + "px,0px)");
- }
- getElement().setPropertyInt("_vScrollTop", position);
- } else {
- getElement().setScrollTop(position);
- }
- }
-
- private boolean isAndroidWithBrokenScrollTop() {
- return BrowserInfo.getBrowserString().contains("Android 3")
- || BrowserInfo.getBrowserString().contains("Android 4");
+ getElement().setScrollTop(position);
}
public void onScroll(ScrollEvent event) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java b/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java
index d4b59ed23a..3c08741de5 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java
@@ -4,22 +4,19 @@
package com.vaadin.terminal.gwt.client.ui;
import java.util.ArrayList;
+import java.util.Date;
-import com.google.gwt.animation.client.Animation;
-import com.google.gwt.core.client.Duration;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Touch;
-import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.shared.HandlerRegistration;
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.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.VConsole;
/**
@@ -67,34 +64,27 @@ public class TouchScrollDelegate implements NativePreviewHandler {
private static final double FRICTION = 0.002;
private static final double DECELERATION = 0.002;
private static final int MAX_DURATION = 1500;
+ private int origX;
private int origY;
private Element[] scrollableElements;
private Element scrolledElement;
private int origScrollTop;
private HandlerRegistration handlerRegistration;
- private double lastAnimatedTranslateY;
private int lastClientY;
+ private double pixxelsPerMs;
+ private boolean transitionPending = false;
private int deltaScrollPos;
private boolean transitionOn = false;
private int finalScrollTop;
private ArrayList<Element> layers;
private boolean moved;
- private ScrollHandler scrollHandler;
private static TouchScrollDelegate activeScrollDelegate;
- private static final boolean androidWithBrokenScrollTop = BrowserInfo
- .getBrowserString().contains("Android 3")
- || BrowserInfo.getBrowserString().contains("Android 4");
-
public TouchScrollDelegate(Element... elements) {
scrollableElements = elements;
}
- public void setScrollHandler(ScrollHandler scrollHandler) {
- this.scrollHandler = scrollHandler;
- }
-
public static TouchScrollDelegate getActiveScrollDelegate() {
return activeScrollDelegate;
}
@@ -126,8 +116,37 @@ public class TouchScrollDelegate implements NativePreviewHandler {
public void onTouchStart(TouchStartEvent event) {
if (activeScrollDelegate == null && event.getTouches().length() == 1) {
- NativeEvent nativeEvent = event.getNativeEvent();
- doTouchStart(nativeEvent);
+
+ Touch touch = event.getTouches().get(0);
+ if (detectScrolledElement(touch)) {
+ VConsole.log("TouchDelegate takes over");
+ event.stopPropagation();
+ handlerRegistration = Event.addNativePreviewHandler(this);
+ activeScrollDelegate = this;
+ hookTransitionEndListener(scrolledElement
+ .getFirstChildElement());
+ origX = touch.getClientX();
+ origY = touch.getClientY();
+ yPositions[0] = origY;
+ eventTimeStamps[0] = new Date();
+ nextEvent = 1;
+
+ if (transitionOn) {
+ // TODO calculate current position of ongoing transition,
+ // fix to that and start scroll from there. Another option
+ // is to investigate if we can get even close the same
+ // framerate with scheduler based impl instead of using
+ // transitions (GWT examples has impl of this, with jsni
+ // though). This is very smooth on native ipad, now we
+ // ignore touch starts during animation.
+ origScrollTop = scrolledElement.getScrollTop();
+ } else {
+ origScrollTop = scrolledElement.getScrollTop();
+ }
+ moved = false;
+ // event.preventDefault();
+ // event.stopPropagation();
+ }
} else {
/*
* Touch scroll is currenly on (possibly bouncing). Ignore.
@@ -135,39 +154,16 @@ public class TouchScrollDelegate implements NativePreviewHandler {
}
}
- private void doTouchStart(NativeEvent nativeEvent) {
- if (transitionOn) {
- momentum.cancel();
- }
- Touch touch = nativeEvent.getTouches().get(0);
- if (detectScrolledElement(touch)) {
- VConsole.log("TouchDelegate takes over");
- nativeEvent.stopPropagation();
- handlerRegistration = Event.addNativePreviewHandler(this);
- activeScrollDelegate = this;
- origY = touch.getClientY();
- yPositions[0] = origY;
- eventTimeStamps[0] = getTimeStamp();
- nextEvent = 1;
-
- origScrollTop = getScrollTop();
- VConsole.log("ST" + origScrollTop);
-
- moved = false;
- // event.preventDefault();
- // event.stopPropagation();
- }
- }
-
- private int getScrollTop() {
- if (androidWithBrokenScrollTop) {
- if (scrolledElement.getPropertyJSO("_vScrollTop") != null) {
- return scrolledElement.getPropertyInt("_vScrollTop");
- }
- return 0;
+ private native void hookTransitionEndListener(Element element)
+ /*-{
+ if(!element.hasTransitionEndListener) {
+ var that = this;
+ element.addEventListener("webkitTransitionEnd",function(event){
+ that.@com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate::onTransitionEnd()();
+ },false);
+ element.hasTransitionEndListener = true;
}
- return scrolledElement.getScrollTop();
- }
+ }-*/;
private void onTransitionEnd() {
if (finalScrollTop < 0) {
@@ -179,6 +175,7 @@ public class TouchScrollDelegate implements NativePreviewHandler {
} else {
moveTransformationToScrolloffset();
}
+ transitionOn = false;
}
private void animateToScrollPosition(int to, int from) {
@@ -187,14 +184,7 @@ public class TouchScrollDelegate implements NativePreviewHandler {
if (time <= 0) {
time = 1; // get animation and transition end event
}
- VConsole.log("Animate " + time + " " + from + " " + to);
- int translateTo = -to + origScrollTop;
- int fromY = -from + origScrollTop;
- if (androidWithBrokenScrollTop) {
- fromY -= origScrollTop;
- translateTo -= origScrollTop;
- }
- translateTo(time, fromY, translateTo);
+ translateTo(time, -to + origScrollTop);
}
private int getAnimationTimeForDistance(int dist) {
@@ -210,21 +200,16 @@ public class TouchScrollDelegate implements NativePreviewHandler {
* scrolltop, causing onscroll event.
*/
private void moveTransformationToScrolloffset() {
- if (androidWithBrokenScrollTop) {
- scrolledElement.setPropertyInt("_vScrollTop", finalScrollTop);
- if (scrollHandler != null) {
- scrollHandler.onScroll(null);
- }
- } else {
- for (Element el : layers) {
- Style style = el.getStyle();
- style.setProperty("webkitTransform", "translate3d(0,0,0)");
- }
- scrolledElement.setScrollTop(finalScrollTop);
+ for (Element el : layers) {
+ Style style = el.getStyle();
+ style.setProperty("webkitTransitionProperty", "none");
+ style.setProperty("webkitTransform", "translate3d(0,0,0)");
}
+ scrolledElement.setScrollTop(finalScrollTop);
activeScrollDelegate = null;
handlerRegistration.removeHandler();
handlerRegistration = null;
+
}
/**
@@ -240,7 +225,14 @@ public class TouchScrollDelegate implements NativePreviewHandler {
if (el.isOrHasChild(target)
&& el.getScrollHeight() > el.getClientHeight()) {
scrolledElement = el;
- layers = getElements(scrolledElement);
+ NodeList<Node> childNodes = scrolledElement.getChildNodes();
+ layers = new ArrayList<Element>();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node item = childNodes.getItem(i);
+ if (item.getNodeType() == Node.ELEMENT_NODE) {
+ layers.add((Element) item);
+ }
+ }
return true;
}
@@ -248,21 +240,10 @@ public class TouchScrollDelegate implements NativePreviewHandler {
return false;
}
- public static ArrayList<Element> getElements(Element scrolledElement2) {
- NodeList<Node> childNodes = scrolledElement2.getChildNodes();
- ArrayList<Element> l = new ArrayList<Element>();
- for (int i = 0; i < childNodes.getLength(); i++) {
- Node item = childNodes.getItem(i);
- if (item.getNodeType() == Node.ELEMENT_NODE) {
- l.add((Element) item);
- }
- }
- return l;
- }
-
private void onTouchMove(NativeEvent event) {
if (!moved) {
- double l = (getTimeStamp() - eventTimeStamps[0]);
+ Date date = new Date();
+ long l = (date.getTime() - eventTimeStamps[0].getTime());
VConsole.log(l + " ms from start to move");
}
boolean handleMove = readPositionAndSpeed(event);
@@ -274,15 +255,15 @@ public class TouchScrollDelegate implements NativePreviewHandler {
int overscroll = (deltaScrollTop + origScrollTop)
- getMaxFinalY();
overscroll = overscroll / 2;
- if (overscroll > getMaxOverScroll()) {
- overscroll = getMaxOverScroll();
+ if (overscroll > scrolledElement.getClientHeight() / 2) {
+ overscroll = scrolledElement.getClientHeight() / 2;
}
deltaScrollTop = getMaxFinalY() + overscroll - origScrollTop;
} else if (finalPos < 0) {
// spring effect at the beginning
int overscroll = finalPos / 2;
- if (-overscroll > getMaxOverScroll()) {
- overscroll = -getMaxOverScroll();
+ if (-overscroll > scrolledElement.getClientHeight() / 2) {
+ overscroll = -scrolledElement.getClientHeight() / 2;
}
deltaScrollTop = overscroll - origScrollTop;
}
@@ -295,20 +276,16 @@ public class TouchScrollDelegate implements NativePreviewHandler {
private void quickSetScrollPosition(int deltaX, int deltaY) {
deltaScrollPos = deltaY;
- if (androidWithBrokenScrollTop) {
- deltaY += origScrollTop;
- translateTo(-deltaY);
- } else {
- translateTo(-deltaScrollPos);
- }
+ translateTo(0, -deltaScrollPos);
}
private static final int EVENTS_FOR_SPEED_CALC = 3;
public static final int SIGNIFICANT_MOVE_THRESHOLD = 3;
private int[] yPositions = new int[EVENTS_FOR_SPEED_CALC];
- private double[] eventTimeStamps = new double[EVENTS_FOR_SPEED_CALC];
+ private Date[] eventTimeStamps = new Date[EVENTS_FOR_SPEED_CALC];
private int nextEvent = 0;
- private Animation momentum;
+ private Date transitionStart;
+ private Date transitionDuration;
/**
*
@@ -316,11 +293,12 @@ public class TouchScrollDelegate implements NativePreviewHandler {
* @return
*/
private boolean readPositionAndSpeed(NativeEvent event) {
+ Date now = new Date();
Touch touch = event.getChangedTouches().get(0);
lastClientY = touch.getClientY();
int eventIndx = nextEvent++;
eventIndx = eventIndx % EVENTS_FOR_SPEED_CALC;
- eventTimeStamps[eventIndx] = getTimeStamp();
+ eventTimeStamps[eventIndx] = now;
yPositions[eventIndx] = lastClientY;
return isMovedSignificantly();
}
@@ -370,11 +348,15 @@ public class TouchScrollDelegate implements NativePreviewHandler {
// VConsole.log("To max overscroll");
finalY = getMaxFinalY() + getMaxOverScroll();
int fixedPixelsToMove = finalY - currentY;
+ pixelsPerMs = pixelsPerMs * pixelsToMove / fixedPixelsToMove
+ / FRICTION;
pixelsToMove = fixedPixelsToMove;
} else if (finalY < 0 - getMaxOverScroll()) {
// VConsole.log("to min overscroll");
finalY = -getMaxOverScroll();
int fixedPixelsToMove = finalY - currentY;
+ pixelsPerMs = pixelsPerMs * pixelsToMove / fixedPixelsToMove
+ / FRICTION;
pixelsToMove = fixedPixelsToMove;
} else {
duration = (int) (Math.abs(pixelsPerMs / DECELERATION));
@@ -398,13 +380,8 @@ public class TouchScrollDelegate implements NativePreviewHandler {
return;
}
- int translateTo = -finalY + origScrollTop;
- int fromY = -currentY + origScrollTop;
- if (androidWithBrokenScrollTop) {
- fromY -= origScrollTop;
- translateTo -= origScrollTop;
- }
- translateTo(duration, fromY, translateTo);
+ int translateY = -finalY + origScrollTop;
+ translateTo(duration, translateY);
}
private double calculateSpeed() {
@@ -415,75 +392,47 @@ public class TouchScrollDelegate implements NativePreviewHandler {
}
int idx = nextEvent % EVENTS_FOR_SPEED_CALC;
final int firstPos = yPositions[idx];
- final double firstTs = eventTimeStamps[idx];
+ final Date firstTs = eventTimeStamps[idx];
idx += EVENTS_FOR_SPEED_CALC;
idx--;
idx = idx % EVENTS_FOR_SPEED_CALC;
final int lastPos = yPositions[idx];
- final double lastTs = eventTimeStamps[idx];
+ final Date lastTs = eventTimeStamps[idx];
// speed as in change of scrolltop == -speedOfTouchPos
- return (firstPos - lastPos) / (lastTs - firstTs);
+ return (firstPos - lastPos)
+ / (double) (lastTs.getTime() - firstTs.getTime());
}
/**
* Note positive scrolltop moves layer up, positive translate moves layer
* down.
+ *
+ * @param duration
+ * @param translateY
*/
- private void translateTo(double translateY) {
+ private void translateTo(int duration, int translateY) {
for (Element el : layers) {
- Style style = el.getStyle();
+ final Style style = el.getStyle();
+ if (duration > 0) {
+ style.setProperty("webkitTransitionDuration", duration + "ms");
+ style.setProperty("webkitTransitionTimingFunction",
+ "cubic-bezier(0,0,0.25,1)");
+ style.setProperty("webkitTransitionProperty",
+ "-webkit-transform");
+ transitionOn = true;
+ transitionStart = new Date();
+ transitionDuration = new Date();
+ } else {
+ style.setProperty("webkitTransitionProperty", "none");
+ }
style.setProperty("webkitTransform", "translate3d(0px,"
+ translateY + "px,0px)");
}
}
- /**
- * Note positive scrolltop moves layer up, positive translate moves layer
- * down.
- *
- * @param duration
- */
- private void translateTo(int duration, final int fromY, final int finalY) {
- if (duration > 0) {
- transitionOn = true;
-
- momentum = new Animation() {
-
- @Override
- protected void onUpdate(double progress) {
- lastAnimatedTranslateY = (fromY + (finalY - fromY)
- * progress);
- translateTo(lastAnimatedTranslateY);
- }
-
- @Override
- protected double interpolate(double progress) {
- return 1 + Math.pow(progress - 1, 3);
- }
-
- @Override
- protected void onComplete() {
- super.onComplete();
- transitionOn = false;
- onTransitionEnd();
- }
-
- @Override
- protected void onCancel() {
- int delta = (int) (finalY - lastAnimatedTranslateY);
- finalScrollTop -= delta;
- moveTransformationToScrolloffset();
- transitionOn = false;
- }
- };
- momentum.run(duration);
- }
- }
-
private int getMaxOverScroll() {
- return androidWithBrokenScrollTop ? 0 : scrolledElement
- .getClientHeight() / 3;
+ return scrolledElement.getClientHeight() / 4;
}
private int getMaxFinalY() {
@@ -492,18 +441,14 @@ public class TouchScrollDelegate implements NativePreviewHandler {
}
public void onPreviewNativeEvent(NativePreviewEvent event) {
- int typeInt = event.getTypeInt();
if (transitionOn) {
/*
* TODO allow starting new events. See issue in onTouchStart
*/
event.cancel();
-
- if (typeInt == Event.ONTOUCHSTART) {
- doTouchStart(event.getNativeEvent());
- }
return;
}
+ int typeInt = event.getTypeInt();
switch (typeInt) {
case Event.ONTOUCHMOVE:
if (!event.isCanceled()) {
@@ -539,15 +484,4 @@ public class TouchScrollDelegate implements NativePreviewHandler {
public void setElements(com.google.gwt.user.client.Element[] elements) {
scrollableElements = elements;
}
-
- /**
- * long calcucation are not very efficient in GWT, so this helper method
- * returns timestamp in double.
- *
- * @return
- */
- public static double getTimeStamp() {
- return Duration.currentTimeMillis();
- }
-
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java
index a022a2bd83..6bbc2a6ceb 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java
@@ -18,7 +18,6 @@ import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Display;
@@ -513,7 +512,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
if (touchScrollDelegate == null) {
touchScrollDelegate = new TouchScrollDelegate(
scrollBodyPanel.getElement());
- touchScrollDelegate.setScrollHandler(this);
}
return touchScrollDelegate;
@@ -4064,14 +4062,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
DOM.appendChild(container, preSpacer);
DOM.appendChild(container, table);
DOM.appendChild(container, postSpacer);
- if (BrowserInfo.get().isTouchDevice()) {
- NodeList<Node> childNodes = container.getChildNodes();
- for (int i = 0; i < childNodes.getLength(); i++) {
- Element item = (Element) childNodes.getItem(i);
- item.getStyle().setProperty("webkitTransform",
- "translate3d(0,0,0)");
- }
- }
}
@@ -4617,7 +4607,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
public class VScrollTableRow extends Panel implements ActionOwner,
Container {
- private static final int TOUCHSCROLL_TIMEOUT = 100;
+ private static final int TOUCHSCROLL_TIMEOUT = 70;
private static final int DRAGMODE_MULTIROW = 2;
protected ArrayList<Widget> childWidgets = new ArrayList<Widget>();
private boolean selected = false;
@@ -5291,11 +5281,9 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
};
}
- if (contextTouchTimeout != null) {
- contextTouchTimeout.cancel();
- contextTouchTimeout
- .schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
- }
+ contextTouchTimeout.cancel();
+ contextTouchTimeout
+ .schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
}
break;
case Event.ONMOUSEDOWN:
@@ -5452,7 +5440,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
int top = Util.getTouchOrMouseClientY(event);
top += Window.getScrollTop();
left += Window.getScrollLeft();
- contextMenu = new ContextMenuDetails(getKey(), left, top);
+ contextMenu = new ContextMenuDetails(this.getKey(), left,
+ top);
client.getContextMenu().showAt(this, left, top);
}
}
@@ -6501,11 +6490,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* The row to ensure is visible
*/
private void ensureRowIsVisible(VScrollTableRow row) {
- if (BrowserInfo.get().isTouchDevice()) {
- // Skip due to android devices that have broken scrolltop will may
- // get odd scrolling here.
- return;
- }
Util.scrollIntoViewVertically(row.getElement());
}