Simplifying and unhacking the ModifyFeature control

With two nested controls (DragFeature, SelectFeature), which - among
others - activated two OpenLayers.Handler.Feature instances, the
ModifyFeature control was fragile and hard to debug.

The issue that @iwillig, @bartvde and myself tried to track down was
that the two Feature handlers interfered with each other, making it hard
to select features on mobile devices, and causing points to jump during
dragging because of not updated last pixel positions.

With this refactoring, there are no more nested controls. All that's left
is a Drag handler. All tests pass. I had to remove one test that checked
for dragging of an unselected point while another was selected, because
now (and partially even before this change, thanks to the ugly drag
handler hack that is now removed) dragging a point will also select it,
saving the user an extra click.
This commit is contained in:
ahocevar
2013-03-19 23:00:10 +01:00
parent 7002ec49e7
commit d2a32d5421
2 changed files with 125 additions and 210 deletions

View File

@@ -4,8 +4,7 @@
* full text of the license. */ * full text of the license. */
/** /**
* @requires OpenLayers/Control/DragFeature.js * @requires OpenLayers/Handler/Drag.js
* @requires OpenLayers/Control/SelectFeature.js
* @requires OpenLayers/Handler/Keyboard.js * @requires OpenLayers/Handler/Keyboard.js
*/ */
@@ -88,18 +87,6 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
*/ */
virtualVertices: null, virtualVertices: null,
/**
* Property: selectControl
* {<OpenLayers.Control.SelectFeature>}
*/
selectControl: null,
/**
* Property: dragControl
* {<OpenLayers.Control.DragFeature>}
*/
dragControl: null,
/** /**
* Property: handlers * Property: handlers
* {Object} * {Object}
@@ -232,64 +219,50 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
if(!(OpenLayers.Util.isArray(this.deleteCodes))) { if(!(OpenLayers.Util.isArray(this.deleteCodes))) {
this.deleteCodes = [this.deleteCodes]; this.deleteCodes = [this.deleteCodes];
} }
var control = this;
// configure the select control // configure the drag handler
var selectOptions = { var dragCallbacks = {
geometryTypes: this.geometryTypes, down: function(pixel) {
clickout: this.clickout, this.vertex = null;
toggle: this.toggle, var feature = this.layer.getFeatureFromEvent(
onBeforeSelect: this.beforeSelectFeature, this.handlers.drag.evt);
onSelect: this.selectFeature, if (feature) {
onUnselect: this.unselectFeature, this.dragStart(feature);
scope: this } else if (this.feature && this.clickout) {
}; this.unselectFeature(this.feature);
if(this.standalone === false) { }
this.selectControl = new OpenLayers.Control.SelectFeature(
layer, selectOptions
);
}
// configure the drag control
var dragOptions = {
documentDrag: this.documentDrag,
geometryTypes: ["OpenLayers.Geometry.Point"],
onStart: function(feature, pixel) {
control.dragStart.apply(control, [feature, pixel]);
}, },
onDrag: function(feature, pixel) { move: function(pixel) {
control.dragVertex.apply(control, [feature, pixel]); delete this._unselect;
if (this.vertex) {
this.dragVertex(this.vertex, pixel);
}
}, },
onComplete: function(feature) { up: function() {
control.dragComplete.apply(control, [feature]); this.handlers.drag.stopDown = false;
if (this._unselect) {
this.unselectFeature(this._unselect);
delete this._unselect;
}
}, },
featureCallbacks: { done: function(pixel) {
over: function(feature) { if (this.vertex) {
/** this.dragComplete(this.vertex);
* In normal mode, the feature handler is set up to allow
* dragging of all points. In standalone mode, we only
* want to allow dragging of sketch vertices and virtual
* vertices - or, in the case of a modifiable point, the
* point itself.
*/
if(control.standalone !== true || feature._sketch ||
control.feature === feature) {
control.dragControl.overFeature.apply(
control.dragControl, [feature]);
}
} }
} }
}; };
this.dragControl = new OpenLayers.Control.DragFeature( var dragOptions = {
layer, dragOptions documentDrag: this.documentDrag,
); stopDown: false
};
// configure the keyboard handler // configure the keyboard handler
var keyboardOptions = { var keyboardOptions = {
keydown: this.handleKeypress keydown: this.handleKeypress
}; };
this.handlers = { this.handlers = {
keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions) keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions),
drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions)
}; };
}, },
@@ -299,8 +272,6 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
*/ */
destroy: function() { destroy: function() {
this.layer = null; this.layer = null;
this.standalone || this.selectControl.destroy();
this.dragControl.destroy();
OpenLayers.Control.prototype.destroy.apply(this, []); OpenLayers.Control.prototype.destroy.apply(this, []);
}, },
@@ -312,8 +283,8 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
* {Boolean} Successfully activated the control. * {Boolean} Successfully activated the control.
*/ */
activate: function() { activate: function() {
return ((this.standalone || this.selectControl.activate()) && return (this.handlers.keyboard.activate() &&
this.handlers.keyboard.activate() && this.handlers.drag.activate() &&
OpenLayers.Control.prototype.activate.apply(this, arguments)); OpenLayers.Control.prototype.activate.apply(this, arguments));
}, },
@@ -331,21 +302,12 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
this.layer.removeFeatures(this.vertices, {silent: true}); this.layer.removeFeatures(this.vertices, {silent: true});
this.layer.removeFeatures(this.virtualVertices, {silent: true}); this.layer.removeFeatures(this.virtualVertices, {silent: true});
this.vertices = []; this.vertices = [];
this.dragControl.deactivate(); this.handlers.drag.deactivate();
var feature = this.feature;
var valid = feature && feature.geometry && feature.layer;
if(this.standalone === false) {
if(valid) {
this.selectControl.unselect.apply(this.selectControl,
[feature]);
}
this.selectControl.deactivate();
} else {
if(valid) {
this.unselectFeature(feature);
}
}
this.handlers.keyboard.deactivate(); this.handlers.keyboard.deactivate();
var feature = this.feature;
if (feature && feature.geometry && feature.layer) {
this.unselectFeature(feature);
}
deactivated = true; deactivated = true;
} }
return deactivated; return deactivated;
@@ -375,11 +337,19 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
* feature - {<OpenLayers.Feature.Vector>} the selected feature. * feature - {<OpenLayers.Feature.Vector>} the selected feature.
*/ */
selectFeature: function(feature) { selectFeature: function(feature) {
if (this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes,
feature.geometry.CLASS_NAME) == -1) {
return;
}
if (!this.standalone || this.beforeSelectFeature(feature) !== false) { if (!this.standalone || this.beforeSelectFeature(feature) !== false) {
if (this.feature) {
this.unselectFeature(this.feature);
}
this.feature = feature; this.feature = feature;
this.layer.selectedFeatures.push(feature);
this.layer.drawFeature(feature, 'select');
this.modified = false; this.modified = false;
this.resetVertices(); this.resetVertices();
this.dragControl.activate();
this.onModificationStart(this.feature); this.onModificationStart(this.feature);
} }
// keep track of geometry modifications // keep track of geometry modifications
@@ -409,8 +379,9 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
this.layer.destroyFeatures([this.radiusHandle], {silent: true}); this.layer.destroyFeatures([this.radiusHandle], {silent: true});
delete this.radiusHandle; delete this.radiusHandle;
} }
this.layer.drawFeature(this.feature, 'default');
this.feature = null; this.feature = null;
this.dragControl.deactivate(); OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);
this.onModificationEnd(feature); this.onModificationEnd(feature);
this.layer.events.triggerEvent("afterfeaturemodified", { this.layer.events.triggerEvent("afterfeaturemodified", {
feature: feature, feature: feature,
@@ -419,63 +390,47 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
this.modified = false; this.modified = false;
}, },
/** /**
* Method: dragStart * Method: dragStart
* Called by the drag feature control with before a feature is dragged. * Called by the drag handler before a feature is dragged. This method is
* This method is used to differentiate between points and vertices * used to differentiate between points and vertices
* of higher order geometries. This respects the <geometryTypes> * of higher order geometries.
* property and forces a select of points when the drag control is
* already active (and stops events from propagating to the select
* control).
* *
* Parameters: * Parameters:
* feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be
* dragged. * dragged.
* pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.
*/ */
dragStart: function(feature, pixel) { dragStart: function(feature) {
// only change behavior if the feature is not in the vertices array var isPoint = feature.geometry.CLASS_NAME ==
if(feature != this.feature && !feature.geometry.parent && 'OpenLayers.Geometry.Point';
feature != this.dragHandle && feature != this.radiusHandle) { if (!this.standalone &&
if(this.standalone === false && this.feature) { ((!feature._sketch && isPoint) || !feature._sketch)) {
// unselect the currently selected feature if (this.toggle && this.feature === feature) {
this.selectControl.clickFeature.apply(this.selectControl, // mark feature for unselection
[this.feature]); this._unselect = feature;
}
// check any constraints on the geometry type
if(this.geometryTypes == null ||
OpenLayers.Util.indexOf(this.geometryTypes,
feature.geometry.CLASS_NAME) != -1) {
// select the point
this.standalone || this.selectControl.clickFeature.apply(
this.selectControl, [feature]);
/**
* TBD: These lines improve workflow by letting the user
* immediately start dragging after the mouse down.
* However, it is very ugly to be messing with controls
* and their handlers in this way. I'd like a better
* solution if the workflow change is necessary.
*/
// prepare the point for dragging
this.dragControl.overFeature.apply(this.dragControl,
[feature]);
this.dragControl.lastPixel = pixel;
this.dragControl.handlers.drag.started = true;
this.dragControl.handlers.drag.start = pixel;
this.dragControl.handlers.drag.last = pixel;
} }
this.selectFeature(feature);
}
if (feature._sketch || isPoint) {
// feature is a drag or virtual handle or point
this.vertex = feature;
this.handlers.drag.stopDown = true;
} }
}, },
/** /**
* Method: dragVertex * Method: dragVertex
* Called by the drag feature control with each drag move of a vertex. * Called by the drag handler with each drag move of a vertex.
* *
* Parameters: * Parameters:
* vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
* pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event. * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.
*/ */
dragVertex: function(vertex, pixel) { dragVertex: function(vertex, pixel) {
var pos = this.map.getLonLatFromViewPortPx(pixel);
var geom = vertex.geometry;
geom.move(pos.lon - geom.x, pos.lat - geom.y);
this.modified = true; this.modified = true;
/** /**
* Five cases: * Five cases:
@@ -487,9 +442,6 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
*/ */
if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
// dragging a simple point // dragging a simple point
if(this.feature != vertex) {
this.feature = vertex;
}
this.layer.events.triggerEvent("vertexmodified", { this.layer.events.triggerEvent("vertexmodified", {
vertex: vertex.geometry, vertex: vertex.geometry,
feature: this.feature, feature: this.feature,
@@ -526,7 +478,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
this.virtualVertices = []; this.virtualVertices = [];
} }
this.layer.drawFeature(this.feature, this.standalone ? undefined : this.layer.drawFeature(this.feature, this.standalone ? undefined :
this.selectControl.renderIntent); 'select');
} }
// keep the vertex on top so it gets the mouseout after dragging // keep the vertex on top so it gets the mouseout after dragging
// this should be removed in favor of an option to draw under or // this should be removed in favor of an option to draw under or
@@ -536,7 +488,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
/** /**
* Method: dragComplete * Method: dragComplete
* Called by the drag feature control when the feature dragging is complete. * Called by the drag handler when the feature dragging is complete.
* *
* Parameters: * Parameters:
* vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
@@ -572,16 +524,6 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
* Method: resetVertices * Method: resetVertices
*/ */
resetVertices: function() { resetVertices: function() {
// if coming from a drag complete we're about to destroy the vertex
// that was just dragged. For that reason, the drag feature control
// will never detect a mouse-out on that vertex, meaning that the drag
// handler won't be deactivated. This can cause errors because the drag
// feature control still has a feature to drag but that feature is
// destroyed. To prevent this, we call outFeature on the drag feature
// control if the control actually has a feature to drag.
if(this.dragControl.feature) {
this.dragControl.outFeature(this.dragControl.feature);
}
if(this.vertices.length > 0) { if(this.vertices.length > 0) {
this.layer.removeFeatures(this.vertices, {silent: true}); this.layer.removeFeatures(this.vertices, {silent: true});
this.vertices = []; this.vertices = [];
@@ -632,11 +574,10 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
// check for delete key // check for delete key
if(this.feature && if(this.feature &&
OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) { OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {
var vertex = this.dragControl.feature; var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt);
if(vertex && if (vertex &&
OpenLayers.Util.indexOf(this.vertices, vertex) != -1 && OpenLayers.Util.indexOf(this.vertices, vertex) != -1 &&
!this.dragControl.handlers.drag.dragging && !this.handlers.drag.dragging && vertex.geometry.parent) {
vertex.geometry.parent) {
// remove the vertex // remove the vertex
vertex.geometry.parent.removeComponent(vertex.geometry); vertex.geometry.parent.removeComponent(vertex.geometry);
this.layer.events.triggerEvent("vertexremoved", { this.layer.events.triggerEvent("vertexremoved", {
@@ -645,8 +586,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
pixel: evt.xy pixel: evt.xy
}); });
this.layer.drawFeature(this.feature, this.standalone ? this.layer.drawFeature(this.feature, this.standalone ?
undefined : undefined : 'select');
this.selectControl.renderIntent);
this.modified = true; this.modified = true;
this.resetVertices(); this.resetVertices();
this.setFeatureState(); this.setFeatureState();
@@ -800,8 +740,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
* map - {<OpenLayers.Map>} The control's map. * map - {<OpenLayers.Map>} The control's map.
*/ */
setMap: function(map) { setMap: function(map) {
this.standalone || this.selectControl.setMap(map); this.handlers.drag.setMap(map);
this.dragControl.setMap(map);
OpenLayers.Control.prototype.setMap.apply(this, arguments); OpenLayers.Control.prototype.setMap.apply(this, arguments);
}, },

View File

@@ -13,34 +13,27 @@
} }
}; };
var options = { var options = {
geometryTypes: "bar" documentDrag: true
}; };
var control = new OpenLayers.Control.ModifyFeature(layer, options); var control = new OpenLayers.Control.ModifyFeature(layer, options);
t.ok(control.layer == layer, t.ok(control.layer == layer,
"constructor sets layer correctly"); "constructor sets layer correctly");
t.eq(control.selectControl.geometryTypes, "bar", t.eq(control.handlers.drag.documentDrag, true,
"constructor sets options correctly on feature handler"); "constructor sets options correctly on drag handler");
t.eq(control.mode, OpenLayers.Control.ModifyFeature.RESHAPE, t.eq(control.mode, OpenLayers.Control.ModifyFeature.RESHAPE,
"constructor initializes modification mode correctly"); "constructor initializes modification mode correctly");
control.destroy(); control.destroy();
} }
function test_destroy(t) { function test_destroy(t) {
t.plan(2); t.plan(1);
var map = new OpenLayers.Map("map"); var map = new OpenLayers.Map("map");
var layer = new OpenLayers.Layer.Vector(); var layer = new OpenLayers.Layer.Vector();
map.addLayer(layer); map.addLayer(layer);
var control = new OpenLayers.Control.ModifyFeature(layer); var control = new OpenLayers.Control.ModifyFeature(layer);
control.selectControl.destroy = function() {
t.ok(true,
"control.destroy calls destroy on select control");
}
control.dragControl.destroy = function() {
t.ok(true,
"control.destroy calls destroy on feature handler");
}
control.destroy(); control.destroy();
t.eq(control.layer, null, "Layer reference removed on destroy.");
map.destroy(); map.destroy();
} }
@@ -51,11 +44,11 @@
map.addLayer(layer); map.addLayer(layer);
var control = new OpenLayers.Control.ModifyFeature(layer); var control = new OpenLayers.Control.ModifyFeature(layer);
map.addControl(control); map.addControl(control);
t.ok(!control.selectControl.active, t.ok(!control.handlers.drag.active,
"select control is not active prior to activating control"); "drag handler is not active prior to activating control");
control.activate(); control.activate();
t.ok(control.selectControl.active, t.ok(control.handlers.drag.active,
"select control is active after activating control"); "drag handler is active after activating control");
map.destroy(); map.destroy();
} }
@@ -99,7 +92,8 @@
); );
// mock up vertex deletion // mock up vertex deletion
control.dragControl.feature = point; var origGetFeatureFromEvent = layer.getFeatureFromEvent;
layer.getFeatureFromEvent = function() { return point; };
control.feature = poly; control.feature = poly;
// we cannot use selectFeature since the control is not part of a map // we cannot use selectFeature since the control is not part of a map
control._originalGeometry = poly.geometry.clone(); control._originalGeometry = poly.geometry.clone();
@@ -139,17 +133,18 @@
t.eq(control.feature.state, OpenLayers.State.UPDATE, "feature state set to update"); t.eq(control.feature.state, OpenLayers.State.UPDATE, "feature state set to update");
// now make sure nothing happens if the vertex is mid-drag // now make sure nothing happens if the vertex is mid-drag
control.dragControl.handlers.drag.dragging = true; control.handlers.drag.dragging = true;
control.handleKeypress({keyCode:delKey}); control.handleKeypress({keyCode:delKey});
// clean up // clean up
layer.getFeatureFromEvent = origGetFeatureFromEvent;
control.destroy(); control.destroy();
layer.destroy(); layer.destroy();
} }
function test_onUnSelect(t) { function test_onUnSelect(t) {
t.plan(6); t.plan(5);
var layer = new OpenLayers.Layer.Vector(); var layer = new OpenLayers.Layer.Vector();
var control = new OpenLayers.Control.ModifyFeature(layer); var control = new OpenLayers.Control.ModifyFeature(layer);
var fakeFeature = {'id':'myid'}; var fakeFeature = {'id':'myid'};
@@ -159,7 +154,6 @@
layer.events.on({"afterfeaturemodified": function(event) { layer.events.on({"afterfeaturemodified": function(event) {
t.eq(event.feature, fakeFeature, "afterfeaturemodified triggered"); t.eq(event.feature, fakeFeature, "afterfeaturemodified triggered");
}}); }});
control.dragControl.deactivate = function() { t.ok(true, "Deactivate called on drag control"); }
control.onModificationEnd = function (feature) { t.eq(feature.id, fakeFeature.id, "onModificationEnd got feature.") } control.onModificationEnd = function (feature) { t.eq(feature.id, fakeFeature.id, "onModificationEnd got feature.") }
layer.removeFeatures = function(verts) { layer.removeFeatures = function(verts) {
t.ok(verts == 'a', "Normal verts removed correctly"); t.ok(verts == 'a', "Normal verts removed correctly");
@@ -190,10 +184,6 @@
// If a feature is to be modified, control.selectFeature gets called. // If a feature is to be modified, control.selectFeature gets called.
// We want this test to fail if selectFeature gets called. // We want this test to fail if selectFeature gets called.
var modified = false; var modified = false;
var _ = OpenLayers.Control.ModifyFeature.prototype.selectFeature;
OpenLayers.Control.ModifyFeature.prototype.selectFeature = function() {
modified = true;
}
var control = new OpenLayers.Control.ModifyFeature(layer); var control = new OpenLayers.Control.ModifyFeature(layer);
map.addControl(control); map.addControl(control);
@@ -202,21 +192,19 @@
// register a listener that will stop feature modification // register a listener that will stop feature modification
layer.events.on({"beforefeaturemodified": function() {return false}}); layer.events.on({"beforefeaturemodified": function() {return false}});
// we can initiate feature modification by selecting a feature with // we can initiate feature modification by programmatically selecting
// the control's select feature control // a feature
control.selectControl.select(feature); control.selectFeature(feature);
if(modified) { if(modified) {
t.fail("selectFeature called, prepping feature for modification"); t.fail("selectFeature called, prepping feature for modification");
} else { } else {
t.ok(true, "the beforefeaturemodified listener stopped feature modification"); t.ok(true, "the beforefeaturemodified listener stopped feature modification");
} }
OpenLayers.Control.ModifyFeature.prototype.selectFeature = _;
} }
function test_selectFeature(t) { function test_selectFeature(t) {
t.plan(12); t.plan(9);
var map = new OpenLayers.Map('map'); var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.Vector("Vectors!", {isBaseLayer: true}); var layer = new OpenLayers.Layer.Vector("Vectors!", {isBaseLayer: true});
map.addLayer(layer); map.addLayer(layer);
@@ -228,7 +216,6 @@
t.ok(obj.feature == fakeFeature, "beforefeaturemodified triggered"); t.ok(obj.feature == fakeFeature, "beforefeaturemodified triggered");
}; };
layer.events.on({"beforefeaturemodified": callback}); layer.events.on({"beforefeaturemodified": callback});
control.dragControl.activate = function() { t.ok(true, "drag Control activated"); }
control.onModificationStart = function(feature) { t.eq(feature.id, fakeFeature.id, "On Modification Start called with correct feature."); } control.onModificationStart = function(feature) { t.eq(feature.id, fakeFeature.id, "On Modification Start called with correct feature."); }
// Start of testing // Start of testing
@@ -262,7 +249,7 @@
control.selectFeature(fakeFeature); control.selectFeature(fakeFeature);
control.vertices = ['a']; control.vertices = ['a'];
control.virtualVertices = ['b']; control.virtualVertices = [{destroy: function() {}}];
layer.addFeatures = function(features) {} layer.addFeatures = function(features) {}
@@ -283,7 +270,7 @@
} }
function test_resetVertices(t) { function test_resetVertices(t) {
t.plan(21); t.plan(20);
var layer = new OpenLayers.Layer.Vector(); var layer = new OpenLayers.Layer.Vector();
var control = new OpenLayers.Control.ModifyFeature(layer); var control = new OpenLayers.Control.ModifyFeature(layer);
var point = new OpenLayers.Geometry.Point(5,6); var point = new OpenLayers.Geometry.Point(5,6);
@@ -340,18 +327,6 @@
t.eq(control.vertices.length, 0, "No vertices when both resizing and reshaping (RESIZE|RESHAPE)"); t.eq(control.vertices.length, 0, "No vertices when both resizing and reshaping (RESIZE|RESHAPE)");
t.eq(control.virtualVertices.length, 0, "No virtual vertices when both resizing and reshaping (RESIZE|RESHAPE)"); t.eq(control.virtualVertices.length, 0, "No virtual vertices when both resizing and reshaping (RESIZE|RESHAPE)");
control.dragControl.feature = new OpenLayers.Feature.Vector(polygon);
control.dragControl.map = {};
control.dragControl.map.div = {};
control.dragControl.map.div.style = {};
control.dragControl.map.viewPortDiv = "foo";
control.dragControl.handlers.drag.deactivate = function() {
this.active = false;
}
control.resetVertices();
t.ok(!control.dragControl.handlers.drag.active, "resetVertices deactivates drag handler");
control.dragControl.map = null;
control.destroy(); control.destroy();
layer.destroy(); layer.destroy();
} }
@@ -512,17 +487,19 @@
var control = new OpenLayers.Control.ModifyFeature(layer); var control = new OpenLayers.Control.ModifyFeature(layer);
map.addControl(control); map.addControl(control);
control.selectControl.deactivate = function() { control.handlers.keyboard.deactivate = function() {
t.ok(true, t.ok(true,
"control.deactivate calls deactivate on select control"); "control.deactivate calls deactivate on keyboard handler");
} }
control.dragControl.deactivate = function() { control.handlers.drag.deactivate = function() {
t.ok(true, t.ok(true,
"control.deactivate calls deactivate on drag control"); "control.deactivate calls deactivate on drag handler");
} }
control.active = true; control.active = true;
control.deactivate(); control.deactivate();
control.handlers.keyboard.deactivate = OpenLayers.Handler.Keyboard.prototype.deactivate;
control.handlers.drag.deactivate = OpenLayers.Handler.Drag.prototype.deactivate;
map.destroy(); map.destroy();
} }
@@ -609,14 +586,17 @@
layer.events.on({"featuremodified": function(event) { layer.events.on({"featuremodified": function(event) {
t.eq(event.feature.id, poly.id, "featuremodified triggered"); t.eq(event.feature.id, poly.id, "featuremodified triggered");
}}); }});
control.onModification = function(feature) { control.onModification = function(feature) {
t.eq(feature.id, poly.id, t.eq(feature.id, poly.id,
"onModification called with the right feature on vertex delete"); "onModification called with the right feature on vertex delete");
}; };
point.geometry.parent = poly.geometry; point.geometry.parent = poly.geometry;
control.dragControl.feature = point; origGetFeatureFromEvent = layer.getFeatureFromEvent;
layer.getFeatureFromEvent = function() { return point; };
control.handleKeypress({keyCode:46}); control.handleKeypress({keyCode:46});
layer.drawFeature = oldDraw; layer.drawFeature = oldDraw;
layer.getFeatureFromEvent = origGetFeatureFromEvent;
map.destroy(); map.destroy();
} }
@@ -694,7 +674,7 @@
function test_standalone(t) { function test_standalone(t) {
t.plan(18); t.plan(17);
var map = new OpenLayers.Map("map"); var map = new OpenLayers.Map("map");
var layer = new OpenLayers.Layer.Vector(); var layer = new OpenLayers.Layer.Vector();
@@ -733,7 +713,6 @@
// activate control // activate control
control.activate(); control.activate();
t.eq(control.active, true, "[activate] control activated"); t.eq(control.active, true, "[activate] control activated");
t.eq(control.selectControl, null, "[activate] no select control");
// manually select feature for editing // manually select feature for editing
control.selectFeature(f1); control.selectFeature(f1);
@@ -761,22 +740,19 @@
t.ok(log[0].feature === f2, "[deactivate] correct feature"); t.ok(log[0].feature === f2, "[deactivate] correct feature");
t.eq(log[0].modified, false, "[deactivate] feature not actually modified"); t.eq(log[0].modified, false, "[deactivate] feature not actually modified");
// reactivate control and select a point feature to see if we can drag
// another point feature;
control.activate();
control.selectFeature(f3);
control.dragControl.handlers.feature.triggerCallback("mousemove", "in", [f4]);
t.eq(control.dragControl.handlers.drag.active, false, "cannot drag unselected feature f4");
control.dragControl.handlers.feature.triggerCallback("mousemove", "in", [f3]);
t.eq(control.dragControl.handlers.drag.active, true, "can drag selected feature f3");
// select the polygon feature to make sure that we can drag vertices and // select the polygon feature to make sure that we can drag vertices and
// virtual vertices // virtual vertices
control.selectFeature(f2); control.selectFeature(f2);
control.dragControl.handlers.feature.triggerCallback("mousemove", "in", [control.vertices[0]]); var origGetFeatureFromEvent = layer.getFeatureFromEvent;
t.eq(control.dragControl.handlers.drag.active, true, "can drag vertex of feature f2"); layer.getFeatureFromEvent = function() { return control.vertices[0]; };
control.dragControl.handlers.feature.triggerCallback("mousemove", "in", [control.virtualVertices[0]]); control.handlers.drag.callbacks.down.call(control, new OpenLayers.Pixel(0,0));
t.eq(control.dragControl.handlers.drag.active, true, "can drag virtual vertex of feature f2"); t.ok(control.vertex === control.vertices[0], "can drag vertex of feature f2");
t.ok(control.feature === f2, "dragging a vertex does not change the selected feature");
layer.getFeatureFromEvent = function() { return control.virtualVertices[0]; };
control.handlers.drag.callbacks.down.call(control, new OpenLayers.Pixel(0,0));
t.ok(control.vertex === control.virtualVertices[0], "can drag virtual vertex of feature f2");
t.ok(control.feature === f2, "dragging a vertex does not change the selected feature");
layer.getFeatureFromEvent = origGetFeatureFromEvent;
control.deactivate(); control.deactivate();
map.destroy(); map.destroy();