Browse Source

Displaying tooltip on touch devices underneath the field (#17150)

Change-Id: I7381a6212b824f9dafc5fe7359b0e791f15c57b2
tags/7.4.4
Alexey Fansky 9 years ago
parent
commit
2d24d34b19

+ 2
- 1
client/src/com/vaadin/client/BrowserInfo.java View File

@@ -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();
}
}


+ 107
- 20
client/src/com/vaadin/client/VTooltip.java View File

@@ -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;
@@ -69,6 +59,11 @@ public class VTooltip extends VOverlay {
private int quickOpenTimeout;
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
@@ -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);
@@ -221,6 +228,40 @@ public class VTooltip extends VOverlay {
return x;
}

/**
* 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
@@ -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
@@ -390,11 +477,6 @@ public class VTooltip extends VOverlay {
private class TooltipEventHandler implements MouseMoveHandler,
KeyDownHandler, FocusHandler, BlurHandler, MouseDownHandler {

/**
* Current element hovered
*/
private com.google.gwt.dom.client.Element currentElement = null;

/**
* Marker for handling of tooltip through focus
*/
@@ -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();

+ 5
- 1
client/src/com/vaadin/client/ui/VFormLayout.java View File

@@ -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;
}


+ 11
- 0
shared/src/com/vaadin/shared/VBrowserDetails.java View File

@@ -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;
@@ -370,6 +372,15 @@ public class VBrowserDetails implements Serializable {
return isIE;
}

/**
* 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.

+ 66
- 0
uitest/src/com/vaadin/tests/components/TouchDevicesTooltip.java View File

@@ -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";
}
}

Loading…
Cancel
Save