You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractClickEventHandler.java 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.ui;
  17. import java.util.logging.Logger;
  18. import com.google.gwt.dom.client.Element;
  19. import com.google.gwt.dom.client.NativeEvent;
  20. import com.google.gwt.event.dom.client.ContextMenuEvent;
  21. import com.google.gwt.event.dom.client.ContextMenuHandler;
  22. import com.google.gwt.event.dom.client.DomEvent;
  23. import com.google.gwt.event.dom.client.DoubleClickEvent;
  24. import com.google.gwt.event.dom.client.DoubleClickHandler;
  25. import com.google.gwt.event.dom.client.MouseDownEvent;
  26. import com.google.gwt.event.dom.client.MouseDownHandler;
  27. import com.google.gwt.event.dom.client.MouseUpEvent;
  28. import com.google.gwt.event.dom.client.MouseUpHandler;
  29. import com.google.gwt.event.shared.EventHandler;
  30. import com.google.gwt.event.shared.HandlerRegistration;
  31. import com.google.gwt.user.client.Event;
  32. import com.google.gwt.user.client.Event.NativePreviewHandler;
  33. import com.vaadin.client.ComponentConnector;
  34. import com.vaadin.client.WidgetUtil;
  35. public abstract class AbstractClickEventHandler implements MouseDownHandler,
  36. MouseUpHandler, DoubleClickHandler, ContextMenuHandler {
  37. private HandlerRegistration mouseDownHandlerRegistration;
  38. private HandlerRegistration mouseUpHandlerRegistration;
  39. private HandlerRegistration doubleClickHandlerRegistration;
  40. private HandlerRegistration contextMenuHandlerRegistration;
  41. protected ComponentConnector connector;
  42. private String clickEventIdentifier;
  43. /**
  44. * The element where the last mouse down event was registered.
  45. */
  46. private Element lastMouseDownTarget;
  47. /**
  48. * Set to true by {@link #mouseUpPreviewHandler} if it gets a mouseup at the
  49. * same element as {@link #lastMouseDownTarget}.
  50. */
  51. private boolean mouseUpPreviewMatched = false;
  52. private HandlerRegistration mouseUpEventPreviewRegistration;
  53. /**
  54. * Previews events after a mousedown to detect where the following mouseup
  55. * hits.
  56. */
  57. private final NativePreviewHandler mouseUpPreviewHandler = event -> {
  58. if (event.getTypeInt() == Event.ONMOUSEUP) {
  59. mouseUpEventPreviewRegistration.removeHandler();
  60. // Event's reported target not always correct if event
  61. // capture is in use
  62. Element elementUnderMouse = WidgetUtil
  63. .getElementUnderMouse(event.getNativeEvent());
  64. if (lastMouseDownTarget != null
  65. && elementUnderMouse == lastMouseDownTarget) {
  66. mouseUpPreviewMatched = true;
  67. } else {
  68. getLogger().info("Ignoring mouseup from " + elementUnderMouse
  69. + " when mousedown was on " + lastMouseDownTarget);
  70. }
  71. }
  72. };
  73. public AbstractClickEventHandler(ComponentConnector connector,
  74. String clickEventIdentifier) {
  75. this.connector = connector;
  76. this.clickEventIdentifier = clickEventIdentifier;
  77. }
  78. public void handleEventHandlerRegistration() {
  79. // Handle registering/unregistering of click handler depending on if
  80. // server side listeners have been added or removed.
  81. if (hasEventListener()) {
  82. if (mouseDownHandlerRegistration == null) {
  83. mouseDownHandlerRegistration = registerHandler(this,
  84. MouseDownEvent.getType());
  85. mouseUpHandlerRegistration = registerHandler(this,
  86. MouseUpEvent.getType());
  87. doubleClickHandlerRegistration = registerHandler(this,
  88. DoubleClickEvent.getType());
  89. contextMenuHandlerRegistration = registerHandler(this,
  90. ContextMenuEvent.getType());
  91. }
  92. } else {
  93. if (mouseDownHandlerRegistration != null) {
  94. // Remove existing handlers
  95. mouseDownHandlerRegistration.removeHandler();
  96. mouseUpHandlerRegistration.removeHandler();
  97. doubleClickHandlerRegistration.removeHandler();
  98. contextMenuHandlerRegistration.removeHandler();
  99. mouseDownHandlerRegistration = null;
  100. mouseUpHandlerRegistration = null;
  101. doubleClickHandlerRegistration = null;
  102. contextMenuHandlerRegistration = null;
  103. }
  104. }
  105. }
  106. /**
  107. * Registers the given handler to the widget so that the necessary events
  108. * are passed to this {@link ClickEventHandler}.
  109. * <p>
  110. * By default registers the handler with the connector root widget.
  111. * </p>
  112. *
  113. * @param <H>
  114. * @param handler
  115. * The handler to register
  116. * @param type
  117. * The type of the handler.
  118. * @return A reference for the registration of the handler.
  119. */
  120. protected <H extends EventHandler> HandlerRegistration registerHandler(
  121. final H handler, DomEvent.Type<H> type) {
  122. return connector.getWidget().addDomHandler(handler, type);
  123. }
  124. /**
  125. * Checks if there is a server side event listener registered for clicks.
  126. *
  127. * @return true if there is a server side event listener registered, false
  128. * otherwise
  129. */
  130. public boolean hasEventListener() {
  131. return connector.hasEventListener(clickEventIdentifier);
  132. }
  133. /**
  134. * Event handler for context menu. Prevents the browser context menu from
  135. * popping up if there is a listener for right clicks.
  136. */
  137. @Override
  138. public void onContextMenu(ContextMenuEvent event) {
  139. if (hasEventListener() && shouldFireEvent(event)) {
  140. // Prevent showing the browser's context menu when there is a right
  141. // click listener.
  142. event.preventDefault();
  143. }
  144. }
  145. @Override
  146. public void onMouseDown(MouseDownEvent event) {
  147. /*
  148. * When getting a mousedown event, we must detect where the
  149. * corresponding mouseup event if it's on a different part of the page.
  150. */
  151. lastMouseDownTarget = WidgetUtil
  152. .getElementUnderMouse(event.getNativeEvent());
  153. mouseUpPreviewMatched = false;
  154. mouseUpEventPreviewRegistration = Event
  155. .addNativePreviewHandler(mouseUpPreviewHandler);
  156. }
  157. @Override
  158. public void onMouseUp(MouseUpEvent event) {
  159. /*
  160. * Only fire a click if the mouseup hits the same element as the
  161. * corresponding mousedown. This is first checked in the event preview
  162. * but we can't fire the even there as the event might get canceled
  163. * before it gets here.
  164. */
  165. if (hasEventListener() && mouseUpPreviewMatched
  166. && lastMouseDownTarget != null
  167. && WidgetUtil.getElementUnderMouse(
  168. event.getNativeEvent()) == lastMouseDownTarget
  169. && shouldFireEvent(event)) {
  170. // "Click" with left, right or middle button
  171. fireClick(event.getNativeEvent());
  172. }
  173. mouseUpPreviewMatched = false;
  174. lastMouseDownTarget = null;
  175. }
  176. /**
  177. * Sends the click event based on the given native event.
  178. *
  179. * @param event
  180. * The native event that caused this click event
  181. */
  182. protected abstract void fireClick(NativeEvent event);
  183. /**
  184. * Called before firing a click event. Allows sub classes to decide if this
  185. * in an event that should cause an event or not.
  186. *
  187. * @param event
  188. * The user event
  189. * @return true if the event should be fired, false otherwise
  190. */
  191. protected boolean shouldFireEvent(DomEvent<?> event) {
  192. return true;
  193. }
  194. /**
  195. * Event handler for double clicks. Used to fire double click events. Note
  196. * that browsers typically fail to prevent the second click event so a
  197. * double click will result in two click events and one double click event.
  198. */
  199. @Override
  200. public void onDoubleClick(DoubleClickEvent event) {
  201. if (hasEventListener() && shouldFireEvent(event)) {
  202. fireClick(event.getNativeEvent());
  203. }
  204. }
  205. /**
  206. * Click event calculates and returns coordinates relative to the element
  207. * returned by this method. Default implementation uses the root element of
  208. * the widget. Override to provide a different relative element.
  209. *
  210. * @return The Element used for calculating relative coordinates for a click
  211. * or null if no relative coordinates can be calculated.
  212. */
  213. protected com.google.gwt.user.client.Element getRelativeToElement() {
  214. return connector.getWidget().getElement();
  215. }
  216. private static Logger getLogger() {
  217. return Logger.getLogger(AbstractClickEventHandler.class.getName());
  218. }
  219. }