From 05fc5806e7946223e057ad7458a18dadceb0566f Mon Sep 17 00:00:00 2001 From: Alexey Fansky Date: Fri, 6 Feb 2015 16:25:44 -0800 Subject: [PATCH] Displaying tooltip in slot for touch devices (#15353) Change-Id: Ia2fce4dbfc205b44622557017afff19c4a2ef7df --- client/src/com/vaadin/client/VTooltip.java | 36 +++--- .../vaadin/client/ui/orderedlayout/Slot.java | 108 +++++++++++++++++- .../tests/components/TouchDevicesTooltip.java | 57 +++++++++ 3 files changed, 182 insertions(+), 19 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/TouchDevicesTooltip.java diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java index 453563370c..a9406935dc 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; @@ -388,7 +378,8 @@ public class VTooltip extends VOverlay { } private class TooltipEventHandler implements MouseMoveHandler, - KeyDownHandler, FocusHandler, BlurHandler, MouseDownHandler { + KeyDownHandler, FocusHandler, BlurHandler, MouseDownHandler, + MouseUpHandler, TouchStartHandler { /** * Current element hovered @@ -400,6 +391,11 @@ public class VTooltip extends VOverlay { */ private boolean handledByFocus; + /** + * Indicates whether the tooltip is being called after a touch event. + */ + private boolean touchInitiated = false; + /** * Locate the tooltip for given element * @@ -450,7 +446,19 @@ public class VTooltip extends VOverlay { @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 @@ -550,9 +558,11 @@ public class VTooltip extends VOverlay { 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"); } diff --git a/client/src/com/vaadin/client/ui/orderedlayout/Slot.java b/client/src/com/vaadin/client/ui/orderedlayout/Slot.java index b97cf73989..616667c367 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/Slot.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/Slot.java @@ -22,9 +22,12 @@ import com.google.gwt.aria.client.Roles; 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; @@ -44,6 +47,7 @@ import com.vaadin.shared.ui.AlignmentInfo; 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; @@ -55,8 +59,13 @@ public final class Slot extends SimplePanel { 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; @@ -582,9 +591,21 @@ public final class Slot extends SimplePanel { 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) { @@ -651,6 +672,81 @@ public final class Slot extends SimplePanel { } } + 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 */ 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..1a3b4cdda5 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/TouchDevicesTooltip.java @@ -0,0 +1,57 @@ +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"; + } +} -- 2.39.5