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.2KB

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