Reworking click and pinch handling to get better behavior on multi-touch devices. r=ahocevar +testing from others (closes #3133)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@11695 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
@@ -102,11 +102,15 @@
|
||||
|
||||
var callbackMap;
|
||||
function callbackSetup(log, options) {
|
||||
callbackMap = new OpenLayers.Map('map', {controls: []});
|
||||
|
||||
var control = {
|
||||
map: callbackMap
|
||||
};
|
||||
callbackMap = new OpenLayers.Map({
|
||||
div: "map",
|
||||
controls: [], // no controls here because these tests use a custom setTimeout and we only want setTimeout calls from a single handler
|
||||
layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
|
||||
center: new OpenLayers.LonLat(0, 0),
|
||||
zoom: 1
|
||||
});
|
||||
var control = new OpenLayers.Control();
|
||||
callbackMap.addControl(control);
|
||||
|
||||
var callbacks = {
|
||||
"click": function(evt) {
|
||||
@@ -119,6 +123,7 @@
|
||||
var handler = new OpenLayers.Handler.Click(control, callbacks, options);
|
||||
handler.activate();
|
||||
|
||||
|
||||
var timers = {};
|
||||
window._setTimeout = window.setTimeout;
|
||||
window.setTimeout = function(func, delay) {
|
||||
@@ -270,13 +275,140 @@
|
||||
|
||||
handler.map.events.triggerEvent("mouseup", up);
|
||||
handler.map.events.triggerEvent("click", up);
|
||||
|
||||
t.eq(log.length, 0, "nothing logged - event outside tolerance");
|
||||
|
||||
callbackTeardown();
|
||||
|
||||
}
|
||||
|
||||
function test_callbacks_within_dblclickTolerance(t) {
|
||||
t.plan(6);
|
||||
|
||||
var log = [];
|
||||
var handler = callbackSetup(log, {single: false, "double": true, dblclickTolerance: 8});
|
||||
|
||||
var first = {
|
||||
xy: px(0, 0)
|
||||
};
|
||||
var second = {
|
||||
xy: px(0, 5)
|
||||
};
|
||||
|
||||
handler.map.events.triggerEvent("mousedown", first);
|
||||
handler.map.events.triggerEvent("mouseup", first);
|
||||
handler.map.events.triggerEvent("click", first);
|
||||
t.eq(log.length, 1, "one item logged");
|
||||
t.eq(log[0] && log[0].method, "setTimeout", "setTimeout called");
|
||||
|
||||
handler.map.events.triggerEvent("mousedown", second);
|
||||
handler.map.events.triggerEvent("mouseup", second);
|
||||
handler.map.events.triggerEvent("click", second);
|
||||
t.eq(log.length, 2, "two events logged");
|
||||
t.eq(log[1] && log[1].method, "clearTimeout", "clearTimeout called");
|
||||
|
||||
handler.map.events.triggerEvent("dblclick", second);
|
||||
t.eq(log.length, 3, "three items logged");
|
||||
t.eq(log[2] && log[2].callback, "dblclick", "dblclick callback called");
|
||||
|
||||
callbackTeardown();
|
||||
}
|
||||
|
||||
function test_callbacks_outside_dblclickTolerance(t) {
|
||||
t.plan(5);
|
||||
|
||||
var log = [];
|
||||
// default dblclickTolerance is 13
|
||||
var handler = callbackSetup(log, {single: false, "double": true});
|
||||
|
||||
var first = {
|
||||
xy: px(0, 0)
|
||||
};
|
||||
var second = {
|
||||
xy: px(13.5, 0)
|
||||
};
|
||||
|
||||
handler.map.events.triggerEvent("mousedown", first);
|
||||
handler.map.events.triggerEvent("mouseup", first);
|
||||
handler.map.events.triggerEvent("click", first);
|
||||
t.eq(log.length, 1, "one item logged");
|
||||
t.eq(log[0] && log[0].method, "setTimeout", "setTimeout called");
|
||||
|
||||
handler.map.events.triggerEvent("mousedown", second);
|
||||
handler.map.events.triggerEvent("mouseup", second);
|
||||
handler.map.events.triggerEvent("click", second);
|
||||
t.eq(log.length, 2, "two items logged");
|
||||
t.eq(log[1] && log[1].method, "clearTimeout", "clearTimeout called");
|
||||
|
||||
handler.map.events.triggerEvent("dblclick", second);
|
||||
t.eq(log.length, 2, "still two items logged - dblclick callback is not called");
|
||||
|
||||
callbackTeardown();
|
||||
}
|
||||
|
||||
function test_callbacks_multitouch_single(t) {
|
||||
|
||||
t.plan(2);
|
||||
|
||||
var log = [];
|
||||
|
||||
var callbacks = {
|
||||
click: function(evt) {
|
||||
log.push({callback: "click", type: evt.type});
|
||||
},
|
||||
dblclick: function(evt) {
|
||||
log.push({callback: "dblclick", type: evt.type});
|
||||
}
|
||||
};
|
||||
|
||||
var map = new OpenLayers.Map("map");
|
||||
var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
|
||||
map.addLayer(layer);
|
||||
map.setCenter(new OpenLayers.LonLat(0, 0), 1);
|
||||
var control = new OpenLayers.Control();
|
||||
map.addControl(control);
|
||||
var handler = new OpenLayers.Handler.Click(
|
||||
control, callbacks,
|
||||
{"double": true, single: true, pixelTolerance: 2}
|
||||
);
|
||||
|
||||
// we override here so we don't have to wait for the timeout
|
||||
handler.queuePotentialClick = function(evt) {
|
||||
log.push({potential: true, evt: evt});
|
||||
OpenLayers.Handler.Click.prototype.queuePotentialClick.call(this, evt);
|
||||
}
|
||||
|
||||
handler.activate();
|
||||
|
||||
function handle(o) {
|
||||
var touches = [];
|
||||
if (("x0" in o) && ("y0" in o)) {
|
||||
touches.push({
|
||||
clientX: o.x0, clientY: o.y0
|
||||
});
|
||||
}
|
||||
if (("x1" in o) && ("y1" in o)) {
|
||||
touches.push({
|
||||
clientX: o.x1, clientY: o.y1
|
||||
});
|
||||
}
|
||||
handler.map.events.handleBrowserEvent({
|
||||
type: o.type, touches: touches
|
||||
});
|
||||
}
|
||||
|
||||
// a typical multitouch sequence goes like this:
|
||||
// touchstart, touchstart, touchend, touchend
|
||||
handle({type: "touchstart", x0: 10, y0: 10});
|
||||
handle({type: "touchstart", x0: 10, y0: 10, x1: 30, y1: 15});
|
||||
handle({type: "touchend"});
|
||||
handle({type: "touchend"});
|
||||
|
||||
t.eq(log.length, 1, "one item logged");
|
||||
t.eq(log[0] && log[0].potential, true, "click in queue - no dblclick called");
|
||||
|
||||
map.destroy();
|
||||
}
|
||||
|
||||
function test_Handler_Click_deactivate(t) {
|
||||
t.plan(4);
|
||||
var control = {
|
||||
@@ -395,83 +527,124 @@
|
||||
});
|
||||
}
|
||||
|
||||
function test_touch_ignoresimulatedclick(t) {
|
||||
t.plan(2);
|
||||
|
||||
// set up
|
||||
function test_touch_within_dblclickTolerance(t) {
|
||||
t.plan(4);
|
||||
|
||||
var log;
|
||||
|
||||
var map = new OpenLayers.Map('map');
|
||||
var control = {map: map};
|
||||
|
||||
var callbacks = {
|
||||
'dblclick': function(e) {
|
||||
log.dblclick = {x: e.xy.x, y: e.xy.y,
|
||||
lastTouches: e.lastTouches};
|
||||
click: function(evt) {
|
||||
log.push({callback: "click", type: evt.type});
|
||||
},
|
||||
dblclick: function(evt) {
|
||||
log.push({callback: "dblclick", type: evt.type});
|
||||
}
|
||||
};
|
||||
|
||||
var map = new OpenLayers.Map("map");
|
||||
var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
|
||||
map.addLayer(layer);
|
||||
map.setCenter(new OpenLayers.LonLat(0, 0), 1);
|
||||
var control = new OpenLayers.Control();
|
||||
map.addControl(control);
|
||||
var handler = new OpenLayers.Handler.Click(
|
||||
control, callbacks,
|
||||
{'double': true, pixelTolerance: null});
|
||||
control, callbacks,
|
||||
{"double": true, single: true, pixelTolerance: 2}
|
||||
);
|
||||
handler.activate();
|
||||
|
||||
function handle(type, x, y) {
|
||||
map.events.handleBrowserEvent({
|
||||
type: type,
|
||||
touches: [
|
||||
{clientX: x, clientY: y}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// test
|
||||
log = [];
|
||||
// sequence of two clicks on a touch device
|
||||
// click 1
|
||||
handle("touchstart", 10, 10);
|
||||
handle("touchend", 11, 10);
|
||||
handle("mousemove", 11, 10);
|
||||
handle("mousedown", 10, 10);
|
||||
handle("mouseup", 11, 10);
|
||||
handle("click", 11, 10);
|
||||
// click 2
|
||||
handle("touchstart", 12, 10);
|
||||
handle("touchend", 12, 10);
|
||||
handle("mousedown", 12, 10);
|
||||
handle("mouseup", 12, 10);
|
||||
handle("click", 12, 10);
|
||||
|
||||
log = {};
|
||||
handler.touchstart({xy: px(1, 1), touches: ["foo"]});
|
||||
handler.touchend({});
|
||||
handler.touchstart({xy: px(1, 1), touches: ["foo"]});
|
||||
handler.touchend({type: "click"});
|
||||
|
||||
t.eq(!!handler.down.touches, true, "Handler down touches property should be truthy");
|
||||
|
||||
t.ok(log.dblclick == undefined, "dblclick callback not called with simulated click");
|
||||
t.eq(log.length, 1, "one callback called");
|
||||
t.eq(log[0] && log[0].callback, "dblclick", "click callback called");
|
||||
t.eq(log[0] && log[0].type, "touchend", "click callback called with touchend event");
|
||||
t.ok(!handler.timerId, "handler doesn't have a timerId waiting for click")
|
||||
|
||||
// tear down
|
||||
map.destroy();
|
||||
}
|
||||
|
||||
function test_touch_dblclick(t) {
|
||||
t.plan(5);
|
||||
|
||||
// set up
|
||||
function test_touch_outside_dblclickTolerance(t) {
|
||||
t.plan(2);
|
||||
|
||||
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};
|
||||
click: function(evt) {
|
||||
log.push({callback: "click", type: evt.type});
|
||||
},
|
||||
'dblclick': function(e) {
|
||||
log.dblclick = {x: e.xy.x, y: e.xy.y,
|
||||
lastTouches: e.lastTouches};
|
||||
dblclick: function(evt) {
|
||||
log.push({callback: "dblclick", type: evt.type});
|
||||
}
|
||||
};
|
||||
|
||||
var map = new OpenLayers.Map("map");
|
||||
var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
|
||||
map.addLayer(layer);
|
||||
map.setCenter(new OpenLayers.LonLat(0, 0), 1);
|
||||
var control = new OpenLayers.Control();
|
||||
map.addControl(control);
|
||||
var handler = new OpenLayers.Handler.Click(
|
||||
control, callbacks,
|
||||
{'double': true, pixelTolerance: null});
|
||||
control, callbacks,
|
||||
{"double": true, single: true, pixelTolerance: 2, dblclickTolerance: 8}
|
||||
);
|
||||
handler.activate();
|
||||
|
||||
function handle(type, x, y) {
|
||||
var touches = [];
|
||||
if (x !== undefined && y !== undefined) {
|
||||
touches.push({
|
||||
clientX: x, clientY: y
|
||||
});
|
||||
}
|
||||
map.events.handleBrowserEvent({
|
||||
type: type, touches: touches
|
||||
});
|
||||
}
|
||||
|
||||
// test
|
||||
log = [];
|
||||
// sequence of two clicks on a touch device
|
||||
// click 1
|
||||
handle("touchstart", 10, 10);
|
||||
handle("touchend");
|
||||
handle("mousemove", 11, 10);
|
||||
handle("mousedown", 10, 10);
|
||||
handle("mouseup", 11, 10);
|
||||
handle("click", 11, 10);
|
||||
// click 2
|
||||
handle("touchstart", 20, 10);
|
||||
handle("touchend");
|
||||
handle("mousedown", 20, 10);
|
||||
handle("mouseup", 20, 10);
|
||||
handle("click", 20, 10);
|
||||
|
||||
log = {};
|
||||
handler.touchstart({xy: px(1, 1), touches: [{clientX:0, clientY:10}]});
|
||||
handler.touchend({});
|
||||
handler.touchstart({xy: px(1, 1), touches: [{clientX:0, clientY:10}]});
|
||||
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.ok(log.dblclick.lastTouches, "evt.lastTouches on evt");
|
||||
}
|
||||
t.eq(log.length, 0, "no callbacks called");
|
||||
t.ok(!handler.timerId, "handler doesn't have a timerId waiting for click")
|
||||
|
||||
// tear down
|
||||
map.destroy();
|
||||
|
||||
Reference in New Issue
Block a user