From 7a8f3917c9f907f6ce727d7ff71d7cb82692e19e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 21 Feb 2011 16:04:34 +0000 Subject: [PATCH] make click handler work with touch events (closes #2996) git-svn-id: http://svn.openlayers.org/trunk/openlayers@11201 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- lib/OpenLayers/Handler/Click.js | 80 +++++++++++++++++++++++------ tests/Handler/Click.html | 89 +++++++++++++++++++++++++++++++-- 2 files changed, 152 insertions(+), 17 deletions(-) diff --git a/lib/OpenLayers/Handler/Click.js b/lib/OpenLayers/Handler/Click.js index 2bf5e33292..aa1b8f7867 100644 --- a/lib/OpenLayers/Handler/Click.js +++ b/lib/OpenLayers/Handler/Click.js @@ -22,7 +22,6 @@ * - */ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { - /** * APIProperty: delay * {Number} Number of milliseconds between clicks before the event is @@ -89,7 +88,14 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { * {} The pixel location of the last mousedown. */ down: null, - + + /** + * Property: last + * {} The pixel for the last touchmove. This is + * used to + */ + last: null, + /** * Property: rightclickTimerId * {Number} The id of the right mouse timeout waiting to clear the @@ -118,7 +124,7 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { // optionally register for mouseup and mousedown if(this.pixelTolerance != null) { this.mousedown = function(evt) { - this.down = evt.xy; + this.down = evt; return true; }; } @@ -134,6 +140,19 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { */ mousedown: null, + /** + * Method: touchstart + * Handle touchstart. + * + * Returns: + * {Boolean} Continue propagating this event. + */ + touchstart: function(evt) { + this.down = evt; + this.last = null; + return true; + }, + /** * Method: mouseup * Handle mouseup. Installed to support collection of right mouse events. @@ -141,16 +160,16 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { * Returns: * {Boolean} Continue propagating this event. */ - mouseup: function (evt) { + mouseup: function (evt) { var propagate = true; // Collect right mouse clicks from the mouseup // IE - ignores the second right click in mousedown so using // mouseup instead - if (this.checkModifiers(evt) && - this.control.handleRightClicks && - OpenLayers.Event.isRightClick(evt)) { - propagate = this.rightclick(evt); + if(this.checkModifiers(evt) && + this.control.handleRightClicks && + OpenLayers.Event.isRightClick(evt)) { + propagate = this.rightclick(evt); } return propagate; @@ -216,7 +235,10 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { * {Boolean} Continue propagating this event. */ dblclick: function(evt) { - if(this.passesTolerance(evt)) { + // for touch devices trigger dblclick only for + // "one finger" touch + if(this.passesTolerance(evt) && + (!evt.lastTouches || evt.lastTouches.length == 1)) { if(this["double"]) { this.callback('dblclick', [evt]); } @@ -225,6 +247,30 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { return !this.stopDouble; }, + /** + * Method: touchmove + * Store position of last move, because touchend event can have + * an empty "touches" property. + */ + touchmove: function(evt) { + this.last = evt; + }, + + /** + * Method: touchend + * Correctly set event xy property, and add lastTouches to have + * touches property from last touchstart or touchmove + */ + touchend: function(evt) { + if(!evt) { + return false; + } + var last = this.last || this.down; + evt.xy = last.xy; + evt.lastTouches = last.touches; + return evt.xy ? this.click(evt) : false; + }, + /** * Method: click * Handle click. @@ -236,7 +282,12 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { if(this.passesTolerance(evt)) { if(this.timerId != null) { // already received a click - this.clearTimer(); + if(evt.lastTouches) { + // touch device - we may trigger dblclick + this.dblclick(evt); + } else { + this.clearTimer(); + } } else { // set the timer, send evt only if single is true //use a clone of the event object because it will no longer @@ -246,7 +297,7 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { this.timerId = window.setTimeout( OpenLayers.Function.bind(this.delayedCall, this, clickEvent), this.delay - ); + ); } } return !this.stopSingle; @@ -265,10 +316,10 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { */ passesTolerance: function(evt) { var passes = true; - if(this.pixelTolerance != null && this.down) { + if(this.pixelTolerance != null && this.down && this.down.xy) { var dpx = Math.sqrt( - Math.pow(this.down.x - evt.xy.x, 2) + - Math.pow(this.down.y - evt.xy.y, 2) + Math.pow(this.down.xy.x - evt.xy.x, 2) + + Math.pow(this.down.xy.y - evt.xy.y, 2) ); if(dpx > this.pixelTolerance) { passes = false; @@ -316,6 +367,7 @@ OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { this.clearTimer(); this.down = null; + this.last = null; deactivated = true; } return deactivated; diff --git a/tests/Handler/Click.html b/tests/Handler/Click.html index 8156c99ede..930add8c22 100644 --- a/tests/Handler/Click.html +++ b/tests/Handler/Click.html @@ -43,7 +43,7 @@ } function test_Handler_Click_events(t) { - t.plan(50); + t.plan(80); var map = new OpenLayers.Map('map'); var control = { @@ -72,7 +72,7 @@ // list below events that should be handled (events) and those // that should not be handled (nonevents) by the handler - var events = ["click", "dblclick", "mousedown", "mouseup", "rightclick"]; + var events = ["click", "dblclick", "mousedown", "mouseup", "rightclick", "touchstart", "touchmove", "touchend"]; var nonevents = ["mousemove", "resize", "focus", "blur"]; var handler = new OpenLayers.Handler.Click(control); // set browser event like properties on the handler @@ -82,7 +82,7 @@ handler.activate(); // different listeners registered for pixelTolerance option - var events = ["click", "dblclick", "mousedown", "mouseup", "rightclick"]; + var events = ["click", "dblclick", "mousedown", "mouseup", "rightclick", "touchstart", "touchmove", "touchend"]; var nonevents = ["mousemove", "resize", "focus", "blur"]; var handler = new OpenLayers.Handler.Click(control, {}, { pixelTolerance: 2 @@ -290,6 +290,89 @@ OpenLayers.Event.isRightClick = temp; } + function test_touch_click(t) { + t.plan(4); + + // set up + + var log; + + var map = new OpenLayers.Map('map'); + var control = {map: map}; + + var callbacks = { + 'click': function(e) { + log = {x: e.xy.x, y: e.xy.y, + lastTouches: e.lastTouches}; + } + }; + + var handler = new OpenLayers.Handler.Click( + control, callbacks, + {'single': true, pixelTolerance: null}); + + // test + + log = null; + handler.touchstart({xy: {x: 1, y: 1}, touches: ["foo"]}); + handler.touchend({}); + + t.delay_call(1, function() { + t.ok(log != null, "click callback called"); + if(log != null) { + t.eq(log.x, 1, "evt.xy.x as expected"); + t.eq(log.y, 1, "evt.xy.y as expected"); + t.eq(log.lastTouches, ["foo"], "evt.lastTouches as expected"); + } + // tear down + map.destroy(); + }); + } + + function test_touch_dblclick(t) { + t.plan(5); + + // set up + + var log; + + var map = new OpenLayers.Map('map'); + var control = {map: map}; + + var callbacks = { + 'click': function(e) { + log.click = {x: e.xy.x, y: e.xy.y, + lastTouches: e.lastTouches}; + }, + 'dblclick': function(e) { + log.dblclick = {x: e.xy.x, y: e.xy.y, + lastTouches: e.lastTouches}; + } + }; + + var handler = new OpenLayers.Handler.Click( + control, callbacks, + {'double': true, pixelTolerance: null}); + + // test + + log = {}; + handler.touchstart({xy: {x: 1, y: 1}, touches: ["foo"]}); + handler.touchend({}); + handler.touchstart({xy: {x: 1, y: 1}, touches: ["foo"]}); + handler.touchend({}); + + t.eq(log.click, undefined, "click callback not called"); + t.ok(log.dblclick != undefined, "dblclick callback called"); + if(log.dblclick != undefined) { + t.eq(log.dblclick.x, 1, "evt.xy.x as expected"); + t.eq(log.dblclick.y, 1, "evt.xy.y as expected"); + t.eq(log.dblclick.lastTouches, ["foo"], "evt.lastTouches as expected"); + } + + // tear down + map.destroy(); + }