diff options
-rw-r--r-- | client/src/com/vaadin/client/BrowserInfo.java | 3 | ||||
-rw-r--r-- | client/src/com/vaadin/client/VTooltip.java | 127 | ||||
-rw-r--r-- | client/src/com/vaadin/client/ui/VFormLayout.java | 6 | ||||
-rw-r--r-- | shared/src/com/vaadin/shared/VBrowserDetails.java | 11 | ||||
-rw-r--r-- | uitest/src/com/vaadin/tests/components/TouchDevicesTooltip.java | 66 |
5 files changed, 191 insertions, 22 deletions
diff --git a/client/src/com/vaadin/client/BrowserInfo.java b/client/src/com/vaadin/client/BrowserInfo.java index 3bc75a9a9b..01968a17a6 100644 --- a/client/src/com/vaadin/client/BrowserInfo.java +++ b/client/src/com/vaadin/client/BrowserInfo.java @@ -88,7 +88,8 @@ public class BrowserInfo { } else if (browserDetails.isIE()) { touchDevice = detectIETouchDevice(); } else { - touchDevice = detectTouchDevice(); + //PhantomJS pretends to be a touch device which breaks some UI tests + touchDevice = !browserDetails.isPhantomJS() && detectTouchDevice(); } } diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java index 453563370c..b9392e3941 100644 --- a/client/src/com/vaadin/client/VTooltip.java +++ b/client/src/com/vaadin/client/VTooltip.java @@ -20,17 +20,7 @@ import com.google.gwt.aria.client.RelevantValue; import com.google.gwt.aria.client.Roles; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Display; -import com.google.gwt.event.dom.client.BlurEvent; -import com.google.gwt.event.dom.client.BlurHandler; -import com.google.gwt.event.dom.client.DomEvent; -import com.google.gwt.event.dom.client.FocusEvent; -import com.google.gwt.event.dom.client.FocusHandler; -import com.google.gwt.event.dom.client.KeyDownEvent; -import com.google.gwt.event.dom.client.KeyDownHandler; -import com.google.gwt.event.dom.client.MouseDownEvent; -import com.google.gwt.event.dom.client.MouseDownHandler; -import com.google.gwt.event.dom.client.MouseMoveEvent; -import com.google.gwt.event.dom.client.MouseMoveHandler; +import com.google.gwt.event.dom.client.*; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Timer; @@ -70,6 +60,11 @@ public class VTooltip extends VOverlay { private int closeTimeout; /** + * Current element hovered + */ + private com.google.gwt.dom.client.Element currentElement = null; + + /** * Used to show tooltips; usually used via the singleton in * {@link ApplicationConnection}. NOTE that #setOwner(Widget)} should be * called after instantiating. @@ -179,8 +174,20 @@ public class VTooltip extends VOverlay { offsetWidth = getOffsetWidth(); offsetHeight = getOffsetHeight(); } - int x = getFinalX(offsetWidth); - int y = getFinalY(offsetHeight); + + int x = 0; + int y = 0; + if(BrowserInfo.get().isTouchDevice()) { + setMaxWidth(Window.getClientWidth()); + offsetWidth = getOffsetWidth(); + offsetHeight = getOffsetHeight(); + + x = getFinalTouchX(offsetWidth); + y = getFinalTouchY(offsetHeight); + } else { + x = getFinalX(offsetWidth); + y = getFinalY(offsetHeight); + } setPopupPosition(x, y); sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT); @@ -222,6 +229,40 @@ public class VTooltip extends VOverlay { } /** + * Return the final X-coordinate of the tooltip based on cursor + * position, size of the tooltip, size of the page and necessary + * margins. + * + * @param offsetWidth + * @return The final X-coordinate + */ + private int getFinalTouchX(int offsetWidth) { + int x = 0; + int widthNeeded = 10 + offsetWidth; + int roomLeft = currentElement != null ? + currentElement.getAbsoluteLeft() : EVENT_XY_POSITION_OUTSIDE; + int viewPortWidth = Window.getClientWidth(); + int roomRight = viewPortWidth - roomLeft; + if (roomRight > widthNeeded) { + x = roomLeft; + } else { + x = roomLeft - offsetWidth; + } + if (x + offsetWidth - Window.getScrollLeft() > viewPortWidth) { + x = viewPortWidth - offsetWidth + Window.getScrollLeft(); + } + + if (roomLeft != EVENT_XY_POSITION_OUTSIDE) { + // Do not allow x to be zero, for otherwise the tooltip + // does not close when the mouse is moved (see + // isTooltipOpen()). #15129 + int minX = Window.getScrollLeft(); + x = Math.max(x, minX); + } + return x; + } + + /** * Return the final Y-coordinate of the tooltip based on cursor * position, size of the tooltip, size of the page and necessary * margins. @@ -232,7 +273,7 @@ public class VTooltip extends VOverlay { */ private int getFinalY(int offsetHeight) { int y = 0; - int heightNeeded = 10 + MARGIN + offsetHeight; + int heightNeeded = 10 + offsetHeight; int roomAbove = tooltipEventMouseY; int roomBelow = Window.getClientHeight() - roomAbove; @@ -263,12 +304,58 @@ public class VTooltip extends VOverlay { } return y; } + + /** + * Return the final Y-coordinate of the tooltip based on cursor + * position, size of the tooltip, size of the page and necessary + * margins. + * + * @param offsetHeight + * @return The final y-coordinate + * + */ + private int getFinalTouchY(int offsetHeight) { + int y = 0; + int heightNeeded = 10 + offsetHeight; + int roomAbove = currentElement != null ? + currentElement.getAbsoluteTop() + currentElement.getOffsetHeight() + : EVENT_XY_POSITION_OUTSIDE; + int roomBelow = Window.getClientHeight() - roomAbove; + + if (roomBelow > heightNeeded) { + y = roomAbove; + } else { + y = roomAbove - offsetHeight - + (currentElement != null ? currentElement.getOffsetHeight() : 0); + } + + if (y + offsetHeight - Window.getScrollTop() > Window + .getClientHeight()) { + y = roomAbove - 5 - offsetHeight + + Window.getScrollTop(); + if (y - Window.getScrollTop() < 0) { + // tooltip does not fit on top of the mouse either, + // put it at the top of the screen + y = Window.getScrollTop(); + } + } + + if (roomAbove != EVENT_XY_POSITION_OUTSIDE) { + // Do not allow y to be zero, for otherwise the tooltip + // does not close when the mouse is moved (see + // isTooltipOpen()). #15129 + int minY = Window.getScrollTop(); + y = Math.max(y, minY); + } + return y; + } }); } else { hide(); } } + /** * For assistive tooltips to work correctly we must have the tooltip visible * and attached to the DOM well in advance. For this reason both isShowing @@ -391,11 +478,6 @@ public class VTooltip extends VOverlay { KeyDownHandler, FocusHandler, BlurHandler, MouseDownHandler { /** - * Current element hovered - */ - private com.google.gwt.dom.client.Element currentElement = null; - - /** * Marker for handling of tooltip through focus */ private boolean handledByFocus; @@ -455,7 +537,7 @@ public class VTooltip extends VOverlay { @Override public void onMouseDown(MouseDownEvent event) { - handleHideEvent(); + handleHideEvent(); } @Override @@ -524,6 +606,11 @@ public class VTooltip extends VOverlay { updatePosition(event, isFocused); // Schedule timer for showing the tooltip according to if it // was recently closed or not. + + if (BrowserInfo.get().isIOS()) { + element.focus(); + } + int timeout = justClosed ? getQuickOpenDelay() : getOpenDelay(); if (timeout == 0) { showTooltip(); diff --git a/client/src/com/vaadin/client/ui/VFormLayout.java b/client/src/com/vaadin/client/ui/VFormLayout.java index a2ea77d31c..3781305e52 100644 --- a/client/src/com/vaadin/client/ui/VFormLayout.java +++ b/client/src/com/vaadin/client/ui/VFormLayout.java @@ -361,7 +361,11 @@ public class VFormLayout extends SimplePanel { public ErrorFlag(ComponentConnector owner) { setStyleName(CLASSNAME); - sinkEvents(VTooltip.TOOLTIP_EVENTS); + + if(!BrowserInfo.get().isTouchDevice()) { + sinkEvents(VTooltip.TOOLTIP_EVENTS); + } + this.owner = owner; } diff --git a/shared/src/com/vaadin/shared/VBrowserDetails.java b/shared/src/com/vaadin/shared/VBrowserDetails.java index 6e45d33e16..561b6c76d0 100644 --- a/shared/src/com/vaadin/shared/VBrowserDetails.java +++ b/shared/src/com/vaadin/shared/VBrowserDetails.java @@ -41,6 +41,7 @@ public class VBrowserDetails implements Serializable { private boolean isFirefox = false; private boolean isOpera = false; private boolean isIE = false; + private boolean isPhantomJS = false; private boolean isWindowsPhone; private boolean isIPad; @@ -86,6 +87,7 @@ public class VBrowserDetails implements Serializable { isSafari = !isChrome && !isIE && userAgent.indexOf("safari") != -1; isFirefox = userAgent.indexOf(" firefox/") != -1; + isPhantomJS = userAgent.indexOf("phantomjs/") != -1; // chromeframe isChromeFrameCapable = userAgent.indexOf("chromeframe") != -1; @@ -371,6 +373,15 @@ public class VBrowserDetails implements Serializable { } /** + * Tests if the browser is PhantomJS. + * + * @return true if it is PhantomJS, false otherwise + */ + public boolean isPhantomJS() { + return isPhantomJS; + } + + /** * Returns the version of the browser engine. For WebKit this is an integer * e.g., 532.0. For gecko it is a float e.g., 1.8 or 1.9. * diff --git a/uitest/src/com/vaadin/tests/components/TouchDevicesTooltip.java b/uitest/src/com/vaadin/tests/components/TouchDevicesTooltip.java new file mode 100644 index 0000000000..ac4b48711e --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/TouchDevicesTooltip.java @@ -0,0 +1,66 @@ +package com.vaadin.tests.components; + +import com.vaadin.annotations.Viewport; +import com.vaadin.data.util.converter.StringToIntegerConverter; +import com.vaadin.data.validator.IntegerRangeValidator; +import com.vaadin.server.VaadinRequest; +import com.vaadin.ui.Label; +import com.vaadin.ui.TextField; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +@Viewport(value = "width=device-width,height=device-height") +public class TouchDevicesTooltip extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final Label errorLabel = new Label("No error"); + addComponent(errorLabel); + + for (int i = 0; i < 50; i++) { + createTextField(i); + } + } + + private void createTextField(int n) { + TextField textField = new TextField("Value" + n); + textField.setConverter(new StringToIntegerConverter()); + textField.addValidator(new IntegerRangeValidator(getErrorMessage(n), 0, 100)); + textField.setImmediate(true); + textField.setValue("-5"); + addComponent(textField); + } + + private String getErrorMessage(int n) { + if(n % 2 == 0) { + return "incorrect value" + n; + } else { + return "super long long long long long long long long long long long error message " + n; + } + } + + public static class Bean { + @NotNull + @Min(0) + private Integer value; + + public Integer getValue() { + return value; + } + + public void setValue(Integer value) { + this.value = value; + } + } + + @Override + protected Integer getTicketNumber() { + return 17150; + } + + @Override + public String getDescription() { + return "Unable to dismiss a tooltip on touch devices"; + } +}
\ No newline at end of file |