From 6ad22b8e6a381a57dce2a86742a2a6452b5a0a39 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 16 Dec 2007 04:42:14 +0000 Subject: [PATCH] adding a click handler for flexible cross-browser single and double click handling - add to a control and set click or dblclick callbacks - thanks for the review and pairing crschmidt (closes #1211) git-svn-id: http://svn.openlayers.org/trunk/openlayers@5438 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- examples/click-handler.html | 225 +++++++++++++++++++++++ lib/OpenLayers.js | 1 + lib/OpenLayers/Handler/Click.js | 239 ++++++++++++++++++++++++ tests/Handler/test_Click.html | 317 ++++++++++++++++++++++++++++++++ tests/list-tests.html | 1 + 5 files changed, 783 insertions(+) create mode 100644 examples/click-handler.html create mode 100644 lib/OpenLayers/Handler/Click.js create mode 100644 tests/Handler/test_Click.html diff --git a/examples/click-handler.html b/examples/click-handler.html new file mode 100644 index 0000000000..17c541d715 --- /dev/null +++ b/examples/click-handler.html @@ -0,0 +1,225 @@ + + + OpenLayers Click Handler Example + + + + + + + +

Click Handler Example

+
+ +
+
+ +

+ This example shows the use of the click handler. +

+ +
+

+ The click handler can be used to gain more flexibility over handling + click events. The handler can be constructed with options to handle + only single click events, to handle single and double-click events, + to ignore clicks that include a drag, and to stop propagation of + single and/or double-click events. A single click is a click that + is not followed by another click for more than 300ms. This delay + is configured with the delay property. +

+

+ The options to stop single and double clicks have to do with + stopping event propagation on the map events listener queue + (not stopping events from cascading to other elements). The + ability to stop an event from propagating has to do with the + order in which listeners are registered. With stopSingle or + stopDouble true, a click handler will stop propagation to all + listeners that were registered (or all handlers that were + activated) before the click handler was activated. So, for + example, activating a click handler with stopDouble true after + the navigation control is active will stop double-clicks from + zooming in. +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Controls with click handlers (toggle on/off to clear output)
single only
double only
both
single no drag
single with stop
double with stop
+
+ + diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index 1719e68f5d..4dbe4839a2 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -116,6 +116,7 @@ "OpenLayers/Feature/Vector.js", "OpenLayers/Feature/WFS.js", "OpenLayers/Handler.js", + "OpenLayers/Handler/Click.js", "OpenLayers/Handler/Point.js", "OpenLayers/Handler/Path.js", "OpenLayers/Handler/Polygon.js", diff --git a/lib/OpenLayers/Handler/Click.js b/lib/OpenLayers/Handler/Click.js new file mode 100644 index 0000000000..1ea2d1c498 --- /dev/null +++ b/lib/OpenLayers/Handler/Click.js @@ -0,0 +1,239 @@ +/* Copyright (c) 2006-2007 MetaCarta, Inc., published under the clear BSD license. + * See http://svn.openlayers.org/trunk/openlayers/license.txt + * for the full text of the license. */ + +/** + * @requires OpenLayers/Handler.js + * + * Class: OpenLayers.Handler.Click + * A handler for mouse clicks. The intention of this handler is to give + * controls more flexibility with handling clicks. Browsers trigger + * click events twice for a double-click. In addition, the mousedown, + * mousemove, mouseup sequence fires a click event. With this handler, + * controls can decide whether to ignore clicks associated with a double + * click. By setting a , controls can also ignore clicks + * that include a drag. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { + + /** + * APIProperty: delay + * {Number} Number of milliseconds between clicks before the event is + * considered a double-click. + */ + delay: 300, + + /** + * APIProperty: single + * {Boolean} Handle single clicks. Default is true. If false, clicks + * will not be reported. If true, single-clicks will be reported. + */ + single: true, + + /** + * APIProperty: double + * {Boolean} Handle double-clicks. Default is false. + */ + 'double': false, + + /** + * APIProperty: pixelTolerance + * {Number} Maximum number of pixels between mouseup and mousedown for an + * event to be considered a click. Default is null. If set to an + * integer value, clicks with a drag greater than the value will be + * ignored. This property can only be set when the handler is + * constructed. + */ + pixelTolerance: null, + + /** + * APIProperty: stopSingle + * {Boolean} Stop other listeners from being notified of clicks. Default + * is false. If true, any click listeners registered before this one + * will not be notified of *any* click event (associated with double + * or single clicks). + */ + stopSingle: false, + + /** + * APIProperty: stopDouble + * {Boolean} Stop other listeners from being notified of double-clicks. + * Default is false. If true, any click listeners registered before + * this one will not be notified of *any* double-click events. + * + * The one caveat with stopDouble is that given a map with two click + * handlers, one with stopDouble true and the other with stopSingle + * true, the stopSingle handler should be activated last to get + * uniform cross-browser performance. Since IE triggers one click + * with a dblclick and FF triggers two, if a stopSingle handler is + * activated first, all it gets in IE is a single click when the + * second handler stops propagation on the dblclick. + */ + stopDouble: false, + + /** + * Property: timerId + * {Number} The id of the timeout waiting to clear the . + */ + timerId: null, + + /** + * Property: down + * {} The pixel location of the last mousedown. + */ + down: null, + + /** + * Property: cachedEvent + * {Event} Since IE doesn't let us call window.setTimeout with extra + * arguments, we cache the last click event here. + */ + cachedEvent: null, + + /** + * Constructor: OpenLayers.Handler.Click + * Create a new click handler. + * + * Parameters: + * control - {} The control that is making use of + * this handler. If a handler is being used without a control, the + * handler's setMap method must be overridden to deal properly with + * the map. + * callbacks - {Object} An object with keys corresponding to callbacks + * that will be called by the handler. The callbacks should + * expect to recieve a single argument, the click event. + * Callbacks for 'click' and 'dblclick' are supported. + * options - {Object} Optional object whose properties will be set on the + * handler. + */ + initialize: function(control, callbacks, options) { + OpenLayers.Handler.prototype.initialize.apply(this, arguments); + // optionally register for mouseup and mousedown + if(this.pixelTolerance != null) { + this.mousedown = function(evt) { + this.down = evt.xy; + return true; + }; + } + }, + + /** + * Method: mousedown + * Handle mousedown. Only registered as a listener if pixelTolerance is + * a non-zero value at construction. + * + * Returns: + * {Boolean} Continue propagating this event. + */ + mousedown: null, + + /** + * Method: dblclick + * Handle dblclick. For a dblclick, we get two clicks in some browsers + * (FF) and one in others (IE). So we need to always register for + * dblclick to properly handle single clicks. + * + * Returns: + * {Boolean} Continue propagating this event. + */ + dblclick: function(evt) { + if(this.passesTolerance(evt)) { + if(this["double"]) { + this.callback('dblclick', [evt]); + } + this.clearTimer(); + } + return !this.stopDouble; + }, + + /** + * Method: click + * Handle click. + * + * Returns: + * {Boolean} Continue propagating this event. + */ + click: function(evt) { + if(this.passesTolerance(evt)) { + if(this.timerId != null) { + // already received a click + this.clearTimer(); + } else { + // set the timer, send evt only if single is true + var clickEvent = this.single ? evt : null; + this.timerId = window.setTimeout( + OpenLayers.Function.bind(this.delayedCall, this, clickEvent), + this.delay + ); + } + } + return !this.stopSingle; + }, + + /** + * Method: passesTolerance + * Determine whether the event is within the optional pixel tolerance. + * + * Returns: + * {Boolean} The click is within the pixel tolerance (if specified). + */ + passesTolerance: function(evt) { + var passes = true; + if(this.pixelTolerance) { + var dpx = Math.sqrt( + Math.pow(this.down.x - evt.xy.x, 2) + + Math.pow(this.down.y - evt.xy.y, 2) + ); + if(dpx > this.pixelTolerance) { + passes = false; + } + } + return passes; + }, + + /** + * Method: clearTimer + * Clear the timer and set to null. + */ + clearTimer: function() { + if(this.timerId != null) { + window.clearTimeout(this.timerId); + this.timerId = null; + } + }, + + /** + * Method: delayedCall + * Sets to null. And optionally triggers the click callback if + * is set. + */ + delayedCall: function(evt) { + this.timerId = null; + if(evt) { + this.callback('click', [evt]); + } + }, + + /** + * APIMethod: deactivate + * Deactivate the handler. + * + * Returns: + * {Boolean} The handler was successfully deactivated. + */ + deactivate: function() { + var deactivated = false; + if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { + this.clearTimer(); + this.down = null; + deactivated = true; + } + return deactivated; + }, + + CLASS_NAME: "OpenLayers.Handler.Click" +}); diff --git a/tests/Handler/test_Click.html b/tests/Handler/test_Click.html new file mode 100644 index 0000000000..74ebab93f5 --- /dev/null +++ b/tests/Handler/test_Click.html @@ -0,0 +1,317 @@ + + + + + + +
+ + diff --git a/tests/list-tests.html b/tests/list-tests.html index 5abbc687b7..852a196407 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -85,6 +85,7 @@
  • Control/test_Permalink.html
  • Control/test_Scale.html
  • test_Handler.html
  • +
  • Handler/test_Click.html
  • Handler/test_Feature.html
  • Handler/test_MouseWheel.html
  • Handler/test_Keyboard.html