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;
}
private class TooltipEventHandler implements MouseMoveHandler,
- KeyDownHandler, FocusHandler, BlurHandler, MouseDownHandler {
+ KeyDownHandler, FocusHandler, BlurHandler, MouseDownHandler,
+ MouseUpHandler, TouchStartHandler {
/**
* Current element hovered
*/
private boolean handledByFocus;
+ /**
+ * Indicates whether the tooltip is being called after a touch event.
+ */
+ private boolean touchInitiated = false;
+
/**
* Locate the tooltip for given element
*
@Override
public void onMouseMove(MouseMoveEvent mme) {
- handleShowHide(mme, false);
+ if (!touchInitiated) {
+ handleShowHide(mme, false);
+ }
+ }
+
+ @Override
+ public void onMouseUp(MouseUpEvent event) {
+ touchInitiated = false;
+ }
+
+ @Override
+ public void onTouchStart(TouchStartEvent te) {
+ touchInitiated = true;
}
@Override
Profiler.enter("VTooltip.connectHandlersToWidget");
widget.addDomHandler(tooltipEventHandler, MouseMoveEvent.getType());
widget.addDomHandler(tooltipEventHandler, MouseDownEvent.getType());
+ widget.addDomHandler(tooltipEventHandler, MouseUpEvent.getType());
widget.addDomHandler(tooltipEventHandler, KeyDownEvent.getType());
widget.addDomHandler(tooltipEventHandler, FocusEvent.getType());
widget.addDomHandler(tooltipEventHandler, BlurEvent.getType());
+ widget.addDomHandler(tooltipEventHandler, TouchStartEvent.getType());
Profiler.leave("VTooltip.connectHandlersToWidget");
}
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.Timer;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.*;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
public final class Slot extends SimplePanel {
private static final String ALIGN_CLASS_PREFIX = "v-align-";
+ private static final int TOUCH_ERROR_MESSAGE_HIDE_DELAY = 200;
private final VAbstractOrderedLayout layout;
private Element captionText;
private Icon icon;
private Element errorIcon;
+ private Element errorMessage;
private Element requiredIcon;
+ private HandlerRegistration focusRegistration;
+ private HandlerRegistration blurRegistration;
+ private boolean labelClicked = false;
+
private ElementResizeListener captionResizeListener;
private ElementResizeListener widgetResizeListener;
errorIcon.setClassName("v-errorindicator");
}
caption.appendChild(errorIcon);
- } else if (errorIcon != null) {
- errorIcon.removeFromParent();
- errorIcon = null;
+
+ if(BrowserInfo.get().isTouchDevice()) {
+ addFocusHandlerToWidget(error, widget);
+ addBlurHandlerToWidget(widget);
+ }
+
+ } else {
+ if (errorIcon != null) {
+ errorIcon.removeFromParent();
+ errorIcon = null;
+ }
+
+ if (errorMessage != null) {
+ removeErrorMessageAndHandlers();
+ }
}
if (caption != null) {
}
}
+ private void removeErrorMessageAndHandlers() {
+ errorMessage.removeFromParent();
+ errorMessage = null;
+
+ if (focusRegistration != null) {
+ focusRegistration.removeHandler();
+ focusRegistration = null;
+ }
+
+ if(blurRegistration != null) {
+ blurRegistration.removeHandler();
+ blurRegistration = null;
+ }
+ }
+
+ private void addFocusHandlerToWidget(final String error, Widget widget) {
+ focusRegistration = widget.addHandler(new FocusHandler() {
+ @Override
+ public void onFocus(FocusEvent event) {
+ if(labelClicked) {
+ labelClicked = false;
+ return;
+ }
+ if (errorMessage == null) {
+ errorMessage = DOM.createDiv();
+ errorMessage.setClassName("v-touch-error-message");
+ }
+ errorMessage.setInnerHTML(error);
+ captionWrap.appendChild(errorMessage);
+ }
+ }, FocusEvent.getType());
+ }
+
+ private void addBlurHandlerToWidget(final Widget widget) {
+ blurRegistration = widget.addHandler(new BlurHandler() {
+ @Override
+ public void onBlur(BlurEvent event) {
+ if(errorMessage != null) {
+ addClickHandlerToErrorMessage(widget);
+ }
+ scheduleErrorMessageHide(TOUCH_ERROR_MESSAGE_HIDE_DELAY);
+ }
+ }, BlurEvent.getType());
+ }
+
+ private void scheduleErrorMessageHide(int delay) {
+ //Delaying hiding to allow error message click handler
+ //do his job and return the focus back if error message was tapped
+ Timer hideTimer = new Timer() {
+ @Override
+ public void run() {
+ if(errorMessage != null) {
+ errorMessage.removeFromParent();
+ errorMessage = null;
+ }
+ }
+ };
+ hideTimer.schedule(delay);
+ }
+
+ private void addClickHandlerToErrorMessage(final Widget widget) {
+ Event.sinkEvents(errorMessage, Event.ONCLICK);
+ Event.setEventListener(errorMessage, new EventListener() {
+ @Override
+ public void onBrowserEvent(Event event) {
+ if(Event.ONCLICK == event.getTypeInt()) {
+ errorMessage.removeFromParent();
+ errorMessage = null;
+ labelClicked = true;
+ widget.getElement().focus();
+ }
+ }
+ });
+ }
+
/**
* Does the slot have a caption
*/
--- /dev/null
+package com.vaadin.tests.components;
+
+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;
+
+public class TouchDevicesTooltip extends AbstractTestUI {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ final Label errorLabel = new Label("No error");
+ addComponent(errorLabel);
+
+ TextField textField = new TextField("Value");
+ textField.setConverter(new StringToIntegerConverter());
+ textField.addValidator(new IntegerRangeValidator("incorrect value", 0, 100));
+ textField.setImmediate(true);
+ textField.setValue("-5");
+ addComponent(textField);
+
+ TextField textField2 = new TextField("Value2");
+ textField2.setConverter(new StringToIntegerConverter());
+ textField2.addValidator(new IntegerRangeValidator("incorrect value2", 0, 100));
+ textField2.setImmediate(true);
+ textField2.setValue("-5");
+ addComponent(textField2);
+ }
+
+ 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 15353;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Displaying error message in slot for touch devices";
+ }
+}