summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/build.xml2
-rw-r--r--src/com/vaadin/terminal/gwt/client/ApplicationConnection.java101
-rw-r--r--src/com/vaadin/terminal/gwt/client/VDebugConsole.java21
-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/customlayout/VCustomLayout.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java20
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java20
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java25
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java4
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java6
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java6
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java5
-rw-r--r--src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java20
-rw-r--r--src/com/vaadin/terminal/gwt/server/RequestTimer.java80
-rw-r--r--tests/testbench/com/vaadin/tests/components/TouchScrollables.java267
-rw-r--r--tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.html42
-rw-r--r--tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.java81
-rw-r--r--tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.html32
-rw-r--r--tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.java58
20 files changed, 887 insertions, 202 deletions
diff --git a/build/build.xml b/build/build.xml
index 0aa379660d..573c799f7d 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -845,7 +845,7 @@
<property file="${gpg.passphrase.file}" />
<echo>Publishing ${output-dir}/WebContent/WEB-INF/lib/${lib-jar-name} to Maven repository</echo>
- <artifact:mvn>
+ <artifact:mvn failonerror="true">
<arg value="gpg:sign-and-deploy-file"/>
<!-- .. is a workaround as maven runs in the build directory -->
<sysproperty key="file" value="../${output-dir}/WebContent/WEB-INF/lib/${lib-jar-name}" />
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
index 2be35f053f..be6d578112 100644
--- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
+++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
@@ -256,10 +256,10 @@ public class ApplicationConnection {
/*-{
var ap = this;
var client = {};
- client.isActive = function() {
+ client.isActive = $entry(function() {
return ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::hasActiveRequest()()
|| ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::isExecutingDeferredCommands()();
- }
+ });
var vi = ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::getVersionInfo()();
if (vi) {
client.getVersionInfo = function() {
@@ -267,12 +267,21 @@ public class ApplicationConnection {
}
}
- client.getElementByPath = function(id) {
+ client.getProfilingData = $entry(function() {
+ var pd = [
+ ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::lastProcessingTime,
+ ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::totalProcessingTime
+ ];
+ pd = pd.concat(ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::testBenchServerStatus);
+ return pd;
+ });
+
+ client.getElementByPath = $entry(function(id) {
return componentLocator.@com.vaadin.terminal.gwt.client.ComponentLocator::getElementByPath(Ljava/lang/String;)(id);
- }
- client.getPathForElement = function(element) {
+ });
+ client.getPathForElement = $entry(function(element) {
return componentLocator.@com.vaadin.terminal.gwt.client.ComponentLocator::getPathForElement(Lcom/google/gwt/user/client/Element;)(element);
- }
+ });
$wnd.vaadin.clients[TTAppId] = client;
}-*/;
@@ -314,22 +323,22 @@ public class ApplicationConnection {
if ($wnd.vaadin.forceSync) {
oldSync = $wnd.vaadin.forceSync;
}
- $wnd.vaadin.forceSync = function() {
+ $wnd.vaadin.forceSync = $entry(function() {
if (oldSync) {
oldSync();
}
app.@com.vaadin.terminal.gwt.client.ApplicationConnection::sendPendingVariableChanges()();
- }
+ });
var oldForceLayout;
if ($wnd.vaadin.forceLayout) {
oldForceLayout = $wnd.vaadin.forceLayout;
}
- $wnd.vaadin.forceLayout = function() {
+ $wnd.vaadin.forceLayout = $entry(function() {
if (oldForceLayout) {
oldForceLayout();
}
app.@com.vaadin.terminal.gwt.client.ApplicationConnection::forceLayout()();
- }
+ });
}-*/;
/**
@@ -536,21 +545,22 @@ public class ApplicationConnection {
return;
case 503:
- // We'll assume msec instead of the usual seconds
- int delay = Integer.parseInt(response
- .getHeader("Retry-After"));
- VConsole.log("503, retrying in " + delay + "msec");
- (new Timer() {
- @Override
- public void run() {
- // TODO why? Here used to be "activeRequests--;"
- // but can't see why exactly
- hasActiveRequest = false;
- doUidlRequest(uri, payload, synchronous);
- }
- }).schedule(delay);
- return;
-
+ /*
+ * We'll assume msec instead of the usual seconds. If
+ * there's no Retry-After header, handle the error like
+ * a 500, as per RFC 2616 section 10.5.4.
+ */
+ String delay = response.getHeader("Retry-After");
+ if (delay != null) {
+ VConsole.log("503, retrying in " + delay + "msec");
+ (new Timer() {
+ @Override
+ public void run() {
+ doUidlRequest(uri, payload, synchronous);
+ }
+ }).schedule(Integer.parseInt(delay));
+ return;
+ }
}
if ((statusCode / 100) == 4) {
@@ -561,6 +571,13 @@ public class ApplicationConnection {
+ statusCode, statusCode);
endRequest();
return;
+ } else if ((statusCode / 100) == 5) {
+ // Something's wrong on the server, there's nothing the
+ // client can do except maybe try again.
+ showCommunicationError("Server error. Error code: "
+ + statusCode, statusCode);
+ endRequest();
+ return;
}
String contentType = response.getHeader("Content-Type");
@@ -663,6 +680,26 @@ public class ApplicationConnection {
}
int cssWaits = 0;
+
+ /**
+ * Holds the time spent rendering the last request
+ */
+ protected int lastProcessingTime;
+
+ /**
+ * Holds the total time spent rendering requests during the lifetime of the
+ * session.
+ */
+ protected int totalProcessingTime;
+
+ /**
+ * Holds the timing information from the server-side. How much time was
+ * spent servicing the last request and how much time has been spent
+ * servicing the session so far. These values are always one request behind,
+ * since they cannot be measured before the request is finished.
+ */
+ private ValueMap testBenchServerStatus;
+
static final int MAX_CSS_WAITS = 100;
protected void handleWhenCSSLoaded(final String jsonText,
@@ -999,6 +1036,12 @@ public class ApplicationConnection {
handleUIDLDuration.logDuration(
" * Handling type mappings from server completed", 10);
+ /*
+ * Hook for TestBench to get details about server status
+ */
+ if (json.containsKey("tbss")) {
+ testBenchServerStatus = json.getValueMap("tbss");
+ }
Command c = new Command() {
public void execute() {
@@ -1183,10 +1226,12 @@ public class ApplicationConnection {
// TODO build profiling for widget impl loading time
- final long prosessingTime = (new Date().getTime())
- - start.getTime();
+ lastProcessingTime = (int) ((new Date().getTime()) - start
+ .getTime());
+ totalProcessingTime += lastProcessingTime;
+
VConsole.log(" Processing time was "
- + String.valueOf(prosessingTime) + "ms for "
+ + String.valueOf(lastProcessingTime) + "ms for "
+ jsonText.length() + " characters of JSON");
VConsole.log("Referenced paintables: " + connectorMap.size());
diff --git a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java
index c2fa4f46bf..5eaf78f255 100644
--- a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java
+++ b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java
@@ -180,6 +180,9 @@ public class VDebugConsole extends VOverlay implements Console {
private static final String help = "Drag title=move, shift-drag=resize, doubleclick title=min/max."
+ "Use debug=quiet to log only to browser console.";
+ private static final int DEFAULT_WIDTH = 650;
+ private static final int DEFAULT_HEIGHT = 400;
+
public VDebugConsole() {
super(false, false);
getElement().getStyle().setOverflow(Overflow.HIDDEN);
@@ -301,10 +304,20 @@ public class VDebugConsole extends VOverlay implements Console {
height = Integer.parseInt(split[3]);
autoScrollValue = Boolean.valueOf(split[4]);
} else {
- width = 400;
- height = 150;
- top = Window.getClientHeight() - 160;
- left = Window.getClientWidth() - 410;
+ int windowHeight = Window.getClientHeight();
+ int windowWidth = Window.getClientWidth();
+ width = DEFAULT_WIDTH;
+ height = DEFAULT_HEIGHT;
+
+ if (height > windowHeight / 2) {
+ height = windowHeight / 2;
+ }
+ if (width > windowWidth / 2) {
+ width = windowWidth / 2;
+ }
+
+ top = windowHeight - (height + 10);
+ left = windowWidth - (width + 10);
}
setPixelSize(width, height);
setPopupPosition(left, top);
diff --git a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
index 6b22f3c9f3..533d6a78ae 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java
@@ -3,6 +3,8 @@
*/
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;
@@ -21,6 +23,7 @@ import com.google.gwt.user.client.Event;
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;
/**
* A scrollhandlers similar to {@link ScrollPanel}.
@@ -126,7 +129,11 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
* @return the vertical scroll position, in pixels
*/
public int getScrollPosition() {
- return getElement().getScrollTop();
+ if (getElement().getPropertyJSO("_vScrollTop") != null) {
+ return getElement().getPropertyInt("_vScrollTop");
+ } else {
+ return getElement().getScrollTop();
+ }
}
/**
@@ -146,7 +153,23 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
* the new vertical scroll position, in pixels
*/
public void setScrollPosition(int position) {
- getElement().setScrollTop(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");
}
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 3c08741de5..d4b59ed23a 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java
@@ -4,19 +4,22 @@
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;
/**
@@ -64,27 +67,34 @@ 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;
}
@@ -116,37 +126,8 @@ public class TouchScrollDelegate implements NativePreviewHandler {
public void onTouchStart(TouchStartEvent event) {
if (activeScrollDelegate == null && event.getTouches().length() == 1) {
-
- 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();
- }
+ NativeEvent nativeEvent = event.getNativeEvent();
+ doTouchStart(nativeEvent);
} else {
/*
* Touch scroll is currenly on (possibly bouncing). Ignore.
@@ -154,16 +135,39 @@ public class TouchScrollDelegate implements NativePreviewHandler {
}
}
- 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;
+ 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;
}
- }-*/;
+ return scrolledElement.getScrollTop();
+ }
private void onTransitionEnd() {
if (finalScrollTop < 0) {
@@ -175,7 +179,6 @@ public class TouchScrollDelegate implements NativePreviewHandler {
} else {
moveTransformationToScrolloffset();
}
- transitionOn = false;
}
private void animateToScrollPosition(int to, int from) {
@@ -184,7 +187,14 @@ public class TouchScrollDelegate implements NativePreviewHandler {
if (time <= 0) {
time = 1; // get animation and transition end event
}
- translateTo(time, -to + origScrollTop);
+ VConsole.log("Animate " + time + " " + from + " " + to);
+ int translateTo = -to + origScrollTop;
+ int fromY = -from + origScrollTop;
+ if (androidWithBrokenScrollTop) {
+ fromY -= origScrollTop;
+ translateTo -= origScrollTop;
+ }
+ translateTo(time, fromY, translateTo);
}
private int getAnimationTimeForDistance(int dist) {
@@ -200,16 +210,21 @@ public class TouchScrollDelegate implements NativePreviewHandler {
* scrolltop, causing onscroll event.
*/
private void moveTransformationToScrolloffset() {
- for (Element el : layers) {
- Style style = el.getStyle();
- style.setProperty("webkitTransitionProperty", "none");
- style.setProperty("webkitTransform", "translate3d(0,0,0)");
+ 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);
}
- scrolledElement.setScrollTop(finalScrollTop);
activeScrollDelegate = null;
handlerRegistration.removeHandler();
handlerRegistration = null;
-
}
/**
@@ -225,14 +240,7 @@ public class TouchScrollDelegate implements NativePreviewHandler {
if (el.isOrHasChild(target)
&& el.getScrollHeight() > el.getClientHeight()) {
scrolledElement = el;
- 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);
- }
- }
+ layers = getElements(scrolledElement);
return true;
}
@@ -240,10 +248,21 @@ 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) {
- Date date = new Date();
- long l = (date.getTime() - eventTimeStamps[0].getTime());
+ double l = (getTimeStamp() - eventTimeStamps[0]);
VConsole.log(l + " ms from start to move");
}
boolean handleMove = readPositionAndSpeed(event);
@@ -255,15 +274,15 @@ public class TouchScrollDelegate implements NativePreviewHandler {
int overscroll = (deltaScrollTop + origScrollTop)
- getMaxFinalY();
overscroll = overscroll / 2;
- if (overscroll > scrolledElement.getClientHeight() / 2) {
- overscroll = scrolledElement.getClientHeight() / 2;
+ if (overscroll > getMaxOverScroll()) {
+ overscroll = getMaxOverScroll();
}
deltaScrollTop = getMaxFinalY() + overscroll - origScrollTop;
} else if (finalPos < 0) {
// spring effect at the beginning
int overscroll = finalPos / 2;
- if (-overscroll > scrolledElement.getClientHeight() / 2) {
- overscroll = -scrolledElement.getClientHeight() / 2;
+ if (-overscroll > getMaxOverScroll()) {
+ overscroll = -getMaxOverScroll();
}
deltaScrollTop = overscroll - origScrollTop;
}
@@ -276,16 +295,20 @@ public class TouchScrollDelegate implements NativePreviewHandler {
private void quickSetScrollPosition(int deltaX, int deltaY) {
deltaScrollPos = deltaY;
- translateTo(0, -deltaScrollPos);
+ if (androidWithBrokenScrollTop) {
+ deltaY += origScrollTop;
+ translateTo(-deltaY);
+ } else {
+ translateTo(-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 Date[] eventTimeStamps = new Date[EVENTS_FOR_SPEED_CALC];
+ private double[] eventTimeStamps = new double[EVENTS_FOR_SPEED_CALC];
private int nextEvent = 0;
- private Date transitionStart;
- private Date transitionDuration;
+ private Animation momentum;
/**
*
@@ -293,12 +316,11 @@ 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] = now;
+ eventTimeStamps[eventIndx] = getTimeStamp();
yPositions[eventIndx] = lastClientY;
return isMovedSignificantly();
}
@@ -348,15 +370,11 @@ 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));
@@ -380,8 +398,13 @@ public class TouchScrollDelegate implements NativePreviewHandler {
return;
}
- int translateY = -finalY + origScrollTop;
- translateTo(duration, translateY);
+ int translateTo = -finalY + origScrollTop;
+ int fromY = -currentY + origScrollTop;
+ if (androidWithBrokenScrollTop) {
+ fromY -= origScrollTop;
+ translateTo -= origScrollTop;
+ }
+ translateTo(duration, fromY, translateTo);
}
private double calculateSpeed() {
@@ -392,47 +415,75 @@ public class TouchScrollDelegate implements NativePreviewHandler {
}
int idx = nextEvent % EVENTS_FOR_SPEED_CALC;
final int firstPos = yPositions[idx];
- final Date firstTs = eventTimeStamps[idx];
+ final double firstTs = eventTimeStamps[idx];
idx += EVENTS_FOR_SPEED_CALC;
idx--;
idx = idx % EVENTS_FOR_SPEED_CALC;
final int lastPos = yPositions[idx];
- final Date lastTs = eventTimeStamps[idx];
+ final double lastTs = eventTimeStamps[idx];
// speed as in change of scrolltop == -speedOfTouchPos
- return (firstPos - lastPos)
- / (double) (lastTs.getTime() - firstTs.getTime());
+ return (firstPos - lastPos) / (lastTs - firstTs);
}
/**
* Note positive scrolltop moves layer up, positive translate moves layer
* down.
- *
- * @param duration
- * @param translateY
*/
- private void translateTo(int duration, int translateY) {
+ private void translateTo(double translateY) {
for (Element el : layers) {
- 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 style = el.getStyle();
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 scrolledElement.getClientHeight() / 4;
+ return androidWithBrokenScrollTop ? 0 : scrolledElement
+ .getClientHeight() / 3;
}
private int getMaxFinalY() {
@@ -441,14 +492,18 @@ 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()) {
@@ -484,4 +539,15 @@ 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/customlayout/VCustomLayout.java b/src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java
index 5208d7cacf..b4194c40a6 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java
@@ -362,9 +362,9 @@ public class VCustomLayout extends ComplexPanel {
private native void publishResizedFunction(Element element)
/*-{
var self = this;
- element.notifyChildrenOfSizeChange = function() {
+ element.notifyChildrenOfSizeChange = $entry(function() {
self.@com.vaadin.terminal.gwt.client.ui.customlayout.VCustomLayout::notifyChildrenOfSizeChange()();
- };
+ });
}-*/;
/**
diff --git a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java
index 5cbfabbb11..d09b81e1e1 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java
@@ -508,9 +508,9 @@ public class VDragAndDropWrapper extends VCustomComponent implements
protected native void hookHtml5DragStart(Element el)
/*-{
var me = this;
- el.addEventListener("dragstart", function(ev) {
+ el.addEventListener("dragstart", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
+ }), false);
}-*/;
/**
@@ -522,21 +522,21 @@ public class VDragAndDropWrapper extends VCustomComponent implements
/*-{
var me = this;
- el.addEventListener("dragenter", function(ev) {
+ el.addEventListener("dragenter", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
+ }), false);
- el.addEventListener("dragleave", function(ev) {
+ el.addEventListener("dragleave", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
+ }), false);
- el.addEventListener("dragover", function(ev) {
+ el.addEventListener("dragover", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
+ }), false);
- el.addEventListener("drop", function(ev) {
+ el.addEventListener("drop", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- }, false);
+ }), false);
}-*/;
public boolean updateDropDetails(VDragEvent drag) {
diff --git a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java
index f819b0559a..bb511524e5 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java
@@ -39,9 +39,9 @@ public class VDragAndDropWrapperIE extends VDragAndDropWrapper {
/*-{
var me = this;
- el.attachEvent("ondragstart", function(ev) {
+ el.attachEvent("ondragstart", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- });
+ }));
}-*/;
@Override
@@ -49,21 +49,21 @@ public class VDragAndDropWrapperIE extends VDragAndDropWrapper {
/*-{
var me = this;
- el.attachEvent("ondragenter", function(ev) {
+ el.attachEvent("ondragenter", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- });
+ }));
- el.attachEvent("ondragleave", function(ev) {
+ el.attachEvent("ondragleave", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- });
+ }));
- el.attachEvent("ondragover", function(ev) {
+ el.attachEvent("ondragover", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- });
+ }));
- el.attachEvent("ondrop", function(ev) {
+ el.attachEvent("ondrop", $entry(function(ev) {
return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev);
- });
+ }));
}-*/;
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java
index 563ca04abe..c45c26c4ac 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java
@@ -18,6 +18,7 @@ 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;
@@ -545,6 +546,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
if (touchScrollDelegate == null) {
touchScrollDelegate = new TouchScrollDelegate(
scrollBodyPanel.getElement());
+ touchScrollDelegate.setScrollHandler(this);
}
return touchScrollDelegate;
@@ -3833,6 +3835,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
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)");
+ }
+ }
}
@@ -4370,7 +4380,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
public class VScrollTableRow extends Panel implements ActionOwner {
- private static final int TOUCHSCROLL_TIMEOUT = 70;
+ private static final int TOUCHSCROLL_TIMEOUT = 100;
private static final int DRAGMODE_MULTIROW = 2;
protected ArrayList<Widget> childWidgets = new ArrayList<Widget>();
private boolean selected = false;
@@ -5020,9 +5030,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
}
};
}
- contextTouchTimeout.cancel();
- contextTouchTimeout
- .schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
+ if (contextTouchTimeout != null) {
+ contextTouchTimeout.cancel();
+ contextTouchTimeout
+ .schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
+ }
}
break;
case Event.ONMOUSEDOWN:
@@ -6175,6 +6187,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* 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());
}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java
index b2141e06e5..7bd392b503 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java
@@ -234,9 +234,9 @@ public class VTextField extends TextBoxBase implements Field, ChangeHandler,
protected native void attachCutEventListener(Element el)
/*-{
var me = this;
- el.oncut = function() {
+ el.oncut = $entry(function() {
me.@com.vaadin.terminal.gwt.client.ui.textfield.VTextField::onCut()();
- };
+ });
}-*/;
protected native void detachCutEventListener(Element el)
diff --git a/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java b/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java
index d40f954cdc..484000b8d1 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java
@@ -34,9 +34,9 @@ public class VVideo extends VMediaBase {
private native void updateDimensionsWhenMetadataLoaded(Element el)
/*-{
var self = this;
- el.addEventListener('loadedmetadata', function(e) {
- $entry(self.@com.vaadin.terminal.gwt.client.ui.video.VVideo::updateElementDynamicSize(II)(el.videoWidth, el.videoHeight));
- }, false);
+ el.addEventListener('loadedmetadata', $entry(function(e) {
+ self.@com.vaadin.terminal.gwt.client.ui.video.VVideo::updateElementDynamicSize(II)(el.videoWidth, el.videoHeight);
+ }), false);
}-*/;
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
index 0d67086339..58d6a18592 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
@@ -530,6 +530,9 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
WrappedPortletResponse wrappedResponse = new WrappedPortletResponse(
response, getDeploymentConfiguration());
+ RequestTimer requestTimer = RequestTimer.get(wrappedRequest);
+ requestTimer.start(wrappedRequest);
+
RequestType requestType = getRequestType(request);
if (requestType == RequestType.UNKNOWN) {
@@ -719,6 +722,9 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
Root.setCurrentRoot(null);
Application.setCurrentApplication(null);
}
+
+ requestTimer.stop();
+ RequestTimer.set(wrappedRequest, requestTimer);
}
}
}
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
index 18cc3f97f4..799271b979 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
@@ -403,6 +403,9 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
AbstractApplicationServletWrapper servletWrapper = new AbstractApplicationServletWrapper(
this);
+ RequestTimer requestTimer = RequestTimer.get(request);
+ requestTimer.start(request);
+
RequestType requestType = getRequestType(request);
if (!ensureCookiesEnabled(requestType, request, response)) {
return;
@@ -539,6 +542,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
Application.setCurrentApplication(null);
}
+ requestTimer.stop();
+ RequestTimer.set(request, requestTimer);
}
}
diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
index a1a917130f..b780f66a23 100644
--- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
+++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
@@ -691,7 +691,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
outWriter.print(getSecurityKeyUIDL(request));
}
- writeUidlResponse(repaintAll, outWriter, root, analyzeLayouts);
+ writeUidlResponse(request, repaintAll, outWriter, root, analyzeLayouts);
closeJsonMessage(outWriter);
@@ -735,7 +735,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
@SuppressWarnings("unchecked")
- public void writeUidlResponse(boolean repaintAll,
+ public void writeUidlResponse(WrappedRequest request, boolean repaintAll,
final PrintWriter outWriter, Root root, boolean analyzeLayouts)
throws PaintException {
ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>();
@@ -1096,6 +1096,20 @@ public abstract class AbstractCommunicationManager implements Serializable {
if (dragAndDropService != null) {
dragAndDropService.printJSONResponse(outWriter);
}
+
+ writePerformanceDataForTestBench(request, outWriter);
+ }
+
+ /**
+ * Adds the performance timing data used by TestBench 3 to the UIDL
+ * response.
+ */
+ private void writePerformanceDataForTestBench(final WrappedRequest request,
+ final PrintWriter outWriter) {
+ Long totalTime = (Long) request.getAttribute("TOTAL");
+ Long lastRequestTime = (Long) request.getAttribute("LASTREQUEST");
+ outWriter.write(String.format(", \"tbss\":[%d, %d]", totalTime,
+ lastRequestTime));
}
private void legacyPaint(PaintTarget paintTarget,
@@ -2161,7 +2175,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
if (isXSRFEnabled(root.getApplication())) {
pWriter.print(getSecurityKeyUIDL(request));
}
- writeUidlResponse(true, pWriter, root, false);
+ writeUidlResponse(request, true, pWriter, root, false);
pWriter.print("}");
String initialUIDL = sWriter.toString();
logger.log(Level.FINE, "Initial UIDL:" + initialUIDL);
diff --git a/src/com/vaadin/terminal/gwt/server/RequestTimer.java b/src/com/vaadin/terminal/gwt/server/RequestTimer.java
new file mode 100644
index 0000000000..d47f444bef
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/server/RequestTimer.java
@@ -0,0 +1,80 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.server;
+
+import com.vaadin.terminal.WrappedRequest;
+
+/**
+ * Times the handling of requests and stores the information as an attribute in
+ * the request. The timing info is later passed on to the client in the UIDL and
+ * the client provides JavaScript API for accessing this data from e.g.
+ * TestBench.
+ *
+ * @author Jonatan Kronqvist / Vaadin Ltd
+ */
+public class RequestTimer {
+ public static final String SESSION_ATTR_ID = "REQUESTTIMER";
+
+ private long requestStartTime = 0;
+ private long totalSessionTime = 0;
+ private long lastRequestTime = -1;
+
+ /**
+ * Starts the timing of a request. This should be called before any
+ * processing of the request.
+ *
+ * @param request
+ * the request.
+ */
+ public void start(WrappedRequest request) {
+ requestStartTime = System.nanoTime();
+ request.setAttribute("TOTAL", totalSessionTime);
+ request.setAttribute("LASTREQUEST", lastRequestTime);
+ }
+
+ /**
+ * Stops the timing of a request. This should be called when all processing
+ * of a request has finished.
+ */
+ public void stop() {
+ // Measure and store the total handling time. This data can be
+ // used in TestBench 3 tests.
+ long time = (System.nanoTime() - requestStartTime) / 1000000;
+ lastRequestTime = time;
+ totalSessionTime += time;
+ }
+
+ /**
+ * Returns a valid request timer for the specified request. Timers are
+ * session bound.
+ *
+ * @param request
+ * the request for which to get a valid timer.
+ * @return a valid timer.
+ */
+ public static RequestTimer get(WrappedRequest request) {
+ RequestTimer timer = (RequestTimer) request
+ .getSessionAttribute(SESSION_ATTR_ID);
+ if (timer == null) {
+ timer = new RequestTimer();
+ }
+ return timer;
+ }
+
+ /**
+ * Associates the specified request timer with the specified request. Since
+ * {@link #get(RequestWrapper)} will, at one point or another, return a new
+ * instance, this method should be called to keep the request <-> timer
+ * association updated.
+ *
+ * @param request
+ * the request for which to set the timer.
+ * @param requestTimer
+ * the timer.
+ */
+ public static void set(WrappedRequest request, RequestTimer requestTimer) {
+ request.setSessionAttribute(RequestTimer.SESSION_ATTR_ID, requestTimer);
+ }
+}
diff --git a/tests/testbench/com/vaadin/tests/components/TouchScrollables.java b/tests/testbench/com/vaadin/tests/components/TouchScrollables.java
new file mode 100644
index 0000000000..636bddead8
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/TouchScrollables.java
@@ -0,0 +1,267 @@
+package com.vaadin.tests.components;
+
+import java.util.Collection;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.event.Action;
+import com.vaadin.event.Action.Handler;
+import com.vaadin.event.DataBoundTransferable;
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.event.dd.DropHandler;
+import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
+import com.vaadin.event.dd.acceptcriteria.SourceIs;
+import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
+import com.vaadin.tests.util.Person;
+import com.vaadin.tests.util.PersonContainer;
+import com.vaadin.tests.util.TestUtils;
+import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.AbstractSelect.AbstractSelectTargetDetails;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.CssLayout;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Panel;
+import com.vaadin.ui.Table;
+
+public class TouchScrollables extends TestBase {
+ java.util.Random r = new java.util.Random(1);
+
+ private HorizontalLayout testSelector;
+
+ @Override
+ public void setup() {
+ testSelector = new HorizontalLayout();
+ getLayout().addComponent(testSelector);
+
+ CssLayout cssLayout = new CssLayout();
+ final Table table = new Table();
+
+ Button button = new Button("Toggle lazyloading");
+ button.addListener(new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ if (table.getCacheRate() == 100) {
+ table.setCacheRate(2);
+ table.setPageLength(15);
+ } else {
+ table.setCacheRate(100);
+ table.setHeight("400px");
+ }
+ }
+ });
+ cssLayout.addComponent(button);
+
+ button = new Button("Toggle selectable");
+ button.addListener(new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ table.setSelectable(!table.isSelectable());
+ }
+ });
+ cssLayout.addComponent(button);
+
+ table.addContainerProperty("foo", String.class, "bar");
+ table.setRowHeaderMode(Table.ROW_HEADER_MODE_INDEX);
+ for (int i = 0; i < 1000; i++) {
+ table.addItem();
+ }
+ cssLayout.addComponent(table);
+ cssLayout.setCaption("Table");
+
+ addTest(cssLayout);
+
+ cssLayout = new CssLayout();
+ cssLayout.setCaption("Panel");
+
+ final Panel p = new Panel();
+ p.setHeight("400px");
+ p.setCaption("Panel");
+ Label l50 = null;
+ for (int i = 0; i < 100; i++) {
+ Label c = new Label("Label" + i);
+ p.addComponent(c);
+ if (i == 50) {
+ l50 = c;
+ }
+ }
+
+ final Label l = l50;
+ button = new Button("Scroll to label 50", new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ getLayout().getRoot().scrollIntoView(l);
+ }
+ });
+ cssLayout.addComponent(button);
+ button = new Button("Scroll to 100px", new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ p.setScrollTop(100);
+ }
+ });
+ cssLayout.addComponent(button);
+ cssLayout.addComponent(p);
+
+ addTest(cssLayout);
+
+ TestUtils
+ .injectCSS(
+ getLayout().getRoot(),
+ "body * {-webkit-user-select: none;} .v-table-row-drag-middle .v-table-cell-content {"
+ + " background-color: inherit ; border-bottom: 1px solid cyan;"
+ + "}"
+ + ".v-table-row-drag-middle .v-table-cell-wrapper {"
+ + " margin-bottom: -1px;" + "}" + ""
+
+ );
+
+ addDDSortableTable();
+
+ }
+
+ private void addDDSortableTable() {
+ final Table table;
+ table = new Table();
+ table.setCaption("DD sortable table with context menus");
+ // table.setWidth("100%");
+ table.setPageLength(10);
+ table.setRowHeaderMode(Table.ROW_HEADER_MODE_ID);
+ table.setSelectable(true);
+ table.setMultiSelect(true);
+
+ table.addActionHandler(new Handler() {
+
+ Action[] actions = new Action[] { new Action("FOO"),
+ new Action("BAR"), new Action("CAR") };
+
+ public Action[] getActions(Object target, Object sender) {
+ return actions;
+ }
+
+ public void handleAction(Action action, Object sender, Object target) {
+ getLayout().getRoot().showNotification(action.getCaption());
+
+ }
+ });
+
+ populateTable(table);
+
+ /*
+ * Make table rows draggable
+ */
+ table.setDragMode(Table.TableDragMode.ROW);
+
+ table.setDropHandler(new DropHandler() {
+ // accept only drags from this table
+ AcceptCriterion crit = new SourceIs(table);
+
+ public AcceptCriterion getAcceptCriterion() {
+ return crit;
+ }
+
+ public void drop(DragAndDropEvent dropEvent) {
+ AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dropEvent
+ .getTargetDetails();
+ DataBoundTransferable transferable = (DataBoundTransferable) dropEvent
+ .getTransferable();
+ Object itemIdOver = dropTargetData.getItemIdOver();
+ Object itemId = transferable.getItemId();
+ if (itemId == null || itemIdOver == null
+ || itemId.equals(itemIdOver)) {
+ return; // no move happened
+ }
+
+ // IndexedContainer goodies... (hint: don't use it in real apps)
+ IndexedContainer containerDataSource = (IndexedContainer) table
+ .getContainerDataSource();
+ int newIndex = containerDataSource.indexOfId(itemIdOver) - 1;
+ if (dropTargetData.getDropLocation() != VerticalDropLocation.TOP) {
+ newIndex++;
+ }
+ if (newIndex < 0) {
+ newIndex = 0;
+ }
+ Object idAfter = containerDataSource.getIdByIndex(newIndex);
+ Collection<?> selections = (Collection<?>) table.getValue();
+ if (selections != null && selections.contains(itemId)) {
+ // dragged a selected item, if multiple rows selected, drag
+ // them too (functionality similar to apple mail)
+ for (Object object : selections) {
+ moveAfter(containerDataSource, object, idAfter);
+ }
+
+ } else {
+ // move just the dragged row, not considering selection at
+ // all
+ moveAfter(containerDataSource, itemId, idAfter);
+ }
+
+ }
+
+ private void moveAfter(IndexedContainer containerDataSource,
+ Object itemId, Object idAfter) {
+ try {
+ IndexedContainer clone = null;
+ clone = (IndexedContainer) containerDataSource.clone();
+ containerDataSource.removeItem(itemId);
+ Item newItem = containerDataSource.addItemAfter(idAfter,
+ itemId);
+ Item item = clone.getItem(itemId);
+ for (Object propId : item.getItemPropertyIds()) {
+ newItem.getItemProperty(propId).setValue(
+ item.getItemProperty(propId).getValue());
+ }
+
+ // TODO Auto-generated method stub
+ } catch (CloneNotSupportedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+ });
+ addTest(table);
+ }
+
+ private void populateTable(Table table) {
+ table.addContainerProperty("Name", String.class, "");
+ table.addContainerProperty("Weight", Integer.class, 0);
+
+ PersonContainer testData = PersonContainer.createWithTestData();
+
+ for (int i = 0; i < 40; i++) {
+ Item addItem = table.addItem("Item" + i);
+ Person p = testData.getIdByIndex(i);
+ addItem.getItemProperty("Name").setValue(
+ p.getFirstName() + " " + p.getLastName());
+ addItem.getItemProperty("Weight").setValue(50 + r.nextInt(60));
+ }
+
+ }
+
+ private Component testComponent;
+
+ private void addTest(final AbstractComponent t) {
+ Button button = new Button(t.getCaption());
+ testSelector.addComponent(button);
+ button.addListener(new Button.ClickListener() {
+
+ public void buttonClick(ClickEvent event) {
+ if (testComponent != null) {
+ getLayout().removeComponent(testComponent);
+ }
+ testComponent = t;
+ getLayout().addComponent(t);
+ }
+ });
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Various components and setups suitable for testing scrolling on touch devices.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return null;
+ }
+}
diff --git a/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.html b/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.html
new file mode 100644
index 0000000000..ac06706aa5
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.html
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>TestCurrentPageFirstItem</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">TestCurrentPageFirstItem</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/TestCurrentPageFirstItem?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runTestCurrentPageFirstItem::/VHorizontalLayout[0]/ChildComponentContainer[4]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runTestCurrentPageFirstItem::/VHorizontalLayout[0]/ChildComponentContainer[5]/VButton[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runTestCurrentPageFirstItem::/VHorizontalLayout[0]/ChildComponentContainer[4]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>screenCapture</td>
+ <td></td>
+ <td></td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.java b/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.java
index 1748c27426..7fb096739a 100644
--- a/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.java
+++ b/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.java
@@ -1,60 +1,77 @@
package com.vaadin.tests.components.table;
-import com.vaadin.Application;
import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.tests.components.TestBase;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Root.LegacyWindow;
+import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Table;
import com.vaadin.ui.VerticalLayout;
-public class TestCurrentPageFirstItem extends Application.LegacyApplication
- implements ClickListener {
+public class TestCurrentPageFirstItem extends TestBase implements ClickListener {
private Button buttonIndex;
private Button buttonItem;
- private Table table;
+ private Table[] tables = new Table[4];
private int counter = 0;
IndexedContainer container = new IndexedContainer();
@Override
- public void init() {
- try {
- LegacyWindow main = new LegacyWindow("Table header Test");
- setMainWindow(main);
- main.setSizeFull();
- // setTheme("testtheme");
- VerticalLayout baseLayout = new VerticalLayout();
- main.setContent(baseLayout);
-
- table = new Table();
- container.addContainerProperty("row", String.class, "");
- table.setContainerDataSource(container);
- table.setWidth("100%");
- table.setPageLength(3);
- buttonIndex = new Button("Add row and select last index", this);
- buttonItem = new Button("Add row and select last item", this);
-
- baseLayout.addComponent(table);
- baseLayout.addComponent(buttonIndex);
- baseLayout.addComponent(buttonItem);
- } catch (Exception e) {
- e.printStackTrace();
+ public void setup() {
+ container.addContainerProperty("row", String.class, "");
+
+ HorizontalLayout baseLayout = new HorizontalLayout();
+ baseLayout.setHeight("115px");
+ getMainWindow().setContent(baseLayout);
+
+ for (int i = 0; i < tables.length; ++i) {
+ Table t = new Table();
+ t.setContainerDataSource(container);
+ t.setWidth("100px");
+ baseLayout.addComponent(t);
+ tables[i] = t;
}
+ tables[0].setSizeFull();
+ tables[0].setCaption("Full");
+ tables[1].setHeight("100px");
+ tables[1].setCaption("100px");
+ tables[2].setHeight("95%");
+ tables[2].setCaption("95%");
+ tables[3].setPageLength(3);
+ tables[3].setCaption("3 rows");
+
+ buttonIndex = new Button("Add row and select last index", this);
+ buttonItem = new Button("Add row and select last item", this);
+ baseLayout.addComponent(buttonIndex);
+ baseLayout.addComponent(buttonItem);
}
public void buttonClick(ClickEvent event) {
Item item = container.addItem(++counter);
item.getItemProperty("row").setValue(counter + "");
- table.select(counter);
- if (event.getButton() == buttonIndex) {
- table.setCurrentPageFirstItemIndex(((Container.Indexed) table
- .getContainerDataSource()).indexOfId(counter));
- } else {
- table.setCurrentPageFirstItemId(counter);
+ for (int i = 0; i < tables.length; ++i) {
+ Table t = tables[i];
+ t.select(counter);
+ if (event.getButton() == buttonIndex) {
+ t.setCurrentPageFirstItemIndex(((Container.Indexed) t
+ .getContainerDataSource()).indexOfId(counter));
+ } else {
+ t.setCurrentPageFirstItemId(counter);
+ }
}
}
+
+ @Override
+ protected String getDescription() {
+ return "Table height changes when using setCurrentPageFirstItemId";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 2864;
+ }
}
diff --git a/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.html b/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.html
new file mode 100644
index 0000000000..f5579c9875
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.html
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>ScrollbarsInNestedTabsheets</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">ScrollbarsInNestedTabsheets</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/ScrollbarsInNestedTabsheets?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runScrollbarsInNestedTabsheets::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTabsheet[0]/VTabsheetPanel[0]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>27,8</td>
+</tr>
+<tr>
+ <td>screenCapture</td>
+ <td></td>
+ <td>should-not-have-scrollbars</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.java b/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.java
new file mode 100644
index 0000000000..de250539ff
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.java
@@ -0,0 +1,58 @@
+package com.vaadin.tests.components.tabsheet;
+
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.DateField;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Layout;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+
+@SuppressWarnings("serial")
+public class ScrollbarsInNestedTabsheets extends TestBase {
+
+ @Override
+ public void setup() {
+ setTheme("chameleon");
+ final Label l = new Label("Select Sub Tab 2");
+ final TabSheet t = new TabSheet();
+ final TabSheet t2 = getTabSheet();
+ t.addTab(t2, "Main Tab");
+ addComponent(l);
+ addComponent(t);
+ }
+
+ private TabSheet getTabSheet() {
+ final TabSheet t = new TabSheet();
+ t.addTab(getDummyLayout1(), "Sub Tab 1");
+ t.addTab(getDummyLayout2(), "Sub Tab 2");
+
+ return t;
+ }
+
+ private Layout getDummyLayout1() {
+ final VerticalLayout l = new VerticalLayout();
+ l.addComponent(new DateField("Date"));
+
+ return l;
+ }
+
+ private Layout getDummyLayout2() {
+ final VerticalLayout l = new VerticalLayout();
+ l.addComponent(new DateField("Date"));
+ l.addComponent(new TextField("TextField"));
+
+ return l;
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Nested tabsheets show unwanted scrollbars with Chameleon theme when the inner tabsheet is resized";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 8625;
+ }
+
+} \ No newline at end of file