diff --git a/examples/mobile-drawing.js b/examples/mobile-drawing.js index 869880e30d..d598ff0a17 100644 --- a/examples/mobile-drawing.js +++ b/examples/mobile-drawing.js @@ -5,7 +5,13 @@ function init() { styleMap: new OpenLayers.StyleMap({ temporary: OpenLayers.Util.applyDefaults({ pointRadius: 16 - }, OpenLayers.Feature.Vector.style.temporary) + }, OpenLayers.Feature.Vector.style.temporary), + 'default': OpenLayers.Util.applyDefaults({ + pointRadius: 16 + }, OpenLayers.Feature.Vector.style['default']), + select: OpenLayers.Util.applyDefaults({ + pointRadius: 16 + }, OpenLayers.Feature.Vector.style.select) }) }); diff --git a/lib/OpenLayers/Control/ModifyFeature.js b/lib/OpenLayers/Control/ModifyFeature.js index 8882ae3065..6b0c37ce7b 100644 --- a/lib/OpenLayers/Control/ModifyFeature.js +++ b/lib/OpenLayers/Control/ModifyFeature.js @@ -4,8 +4,7 @@ * full text of the license. */ /** - * @requires OpenLayers/Control/DragFeature.js - * @requires OpenLayers/Control/SelectFeature.js + * @requires OpenLayers/Handler/Drag.js * @requires OpenLayers/Handler/Keyboard.js */ @@ -50,7 +49,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { * Default is true. */ toggle: true, - + /** * APIProperty: standalone * {Boolean} Set to true to create a control without SelectFeature @@ -67,20 +66,26 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { * {} */ layer: null, - + /** * Property: feature * {} Feature currently available for modification. */ feature: null, - + + /** + * Property: vertex + * {} Vertex currently being modified. + */ + vertex: null, + /** * Property: vertices * {Array()} Verticies currently available * for dragging. */ vertices: null, - + /** * Property: virtualVertices * {Array()} Virtual vertices in the middle @@ -88,24 +93,12 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { */ virtualVertices: null, - /** - * Property: selectControl - * {} - */ - selectControl: null, - - /** - * Property: dragControl - * {} - */ - dragControl: null, - /** * Property: handlers * {Object} */ handlers: null, - + /** * APIProperty: deleteCodes * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable @@ -120,7 +113,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { * {Object} A symbolizer to be used for virtual vertices. */ virtualStyle: null, - + /** * APIProperty: vertexRenderIntent * {String} The renderIntent to use for vertices. If no is @@ -232,64 +225,50 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { if(!(OpenLayers.Util.isArray(this.deleteCodes))) { this.deleteCodes = [this.deleteCodes]; } - var control = this; - - // configure the select control - var selectOptions = { - geometryTypes: this.geometryTypes, - clickout: this.clickout, - toggle: this.toggle, - onBeforeSelect: this.beforeSelectFeature, - onSelect: this.selectFeature, - onUnselect: this.unselectFeature, - scope: this - }; - 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]); + + // configure the drag handler + var dragCallbacks = { + down: function(pixel) { + this.vertex = null; + var feature = this.layer.getFeatureFromEvent( + this.handlers.drag.evt); + if (feature) { + this.dragStart(feature); + } else if (this.feature && this.clickout) { + this.unselectFeature(this.feature); + } }, - onDrag: function(feature, pixel) { - control.dragVertex.apply(control, [feature, pixel]); + move: function(pixel) { + delete this._unselect; + if (this.vertex) { + this.dragVertex(this.vertex, pixel); + } }, - onComplete: function(feature) { - control.dragComplete.apply(control, [feature]); + up: function() { + this.handlers.drag.stopDown = false; + if (this._unselect) { + this.unselectFeature(this._unselect); + delete this._unselect; + } }, - featureCallbacks: { - over: function(feature) { - /** - * 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]); - } + done: function(pixel) { + if (this.vertex) { + this.dragComplete(this.vertex); } } }; - this.dragControl = new OpenLayers.Control.DragFeature( - layer, dragOptions - ); + var dragOptions = { + documentDrag: this.documentDrag, + stopDown: false + }; // configure the keyboard handler var keyboardOptions = { keydown: this.handleKeypress }; 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 +278,6 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { */ destroy: function() { this.layer = null; - this.standalone || this.selectControl.destroy(); - this.dragControl.destroy(); OpenLayers.Control.prototype.destroy.apply(this, []); }, @@ -312,8 +289,8 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { * {Boolean} Successfully activated the control. */ activate: function() { - return ((this.standalone || this.selectControl.activate()) && - this.handlers.keyboard.activate() && + return (this.handlers.keyboard.activate() && + this.handlers.drag.activate() && OpenLayers.Control.prototype.activate.apply(this, arguments)); }, @@ -331,26 +308,17 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { this.layer.removeFeatures(this.vertices, {silent: true}); this.layer.removeFeatures(this.virtualVertices, {silent: true}); this.vertices = []; - this.dragControl.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.drag.deactivate(); this.handlers.keyboard.deactivate(); + var feature = this.feature; + if (feature && feature.geometry && feature.layer) { + this.unselectFeature(feature); + } deactivated = true; } return deactivated; }, - + /** * Method: beforeSelectFeature * Called before a feature is selected. @@ -375,11 +343,19 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { * feature - {} the selected 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.feature) { + this.unselectFeature(this.feature); + } this.feature = feature; + this.layer.selectedFeatures.push(feature); + this.layer.drawFeature(feature, 'select'); this.modified = false; this.resetVertices(); - this.dragControl.activate(); this.onModificationStart(this.feature); } // keep track of geometry modifications @@ -409,8 +385,9 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { this.layer.destroyFeatures([this.radiusHandle], {silent: true}); delete this.radiusHandle; } + this.layer.drawFeature(this.feature, 'default'); this.feature = null; - this.dragControl.deactivate(); + OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature); this.onModificationEnd(feature); this.layer.events.triggerEvent("afterfeaturemodified", { feature: feature, @@ -418,64 +395,48 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { }); this.modified = false; }, - + + /** * Method: dragStart - * Called by the drag feature control with before a feature is dragged. - * This method is used to differentiate between points and vertices - * of higher order geometries. This respects the - * property and forces a select of points when the drag control is - * already active (and stops events from propagating to the select - * control). + * Called by the drag handler before a feature is dragged. This method is + * used to differentiate between points and vertices + * of higher order geometries. * * Parameters: * feature - {} The point or vertex about to be * dragged. - * pixel - {} Pixel location of the mouse event. */ - dragStart: function(feature, pixel) { - // only change behavior if the feature is not in the vertices array - if(feature != this.feature && !feature.geometry.parent && - feature != this.dragHandle && feature != this.radiusHandle) { - if(this.standalone === false && this.feature) { - // unselect the currently selected feature - this.selectControl.clickFeature.apply(this.selectControl, - [this.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; + dragStart: function(feature) { + var isPoint = feature.geometry.CLASS_NAME == + 'OpenLayers.Geometry.Point'; + if (!this.standalone && + ((!feature._sketch && isPoint) || !feature._sketch)) { + if (this.toggle && this.feature === feature) { + // mark feature for unselection + this._unselect = feature; } + 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 - * 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: * vertex - {} The vertex being dragged. * pixel - {} Pixel location of the mouse event. */ 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; /** * Five cases: @@ -487,9 +448,6 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { */ if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { // dragging a simple point - if(this.feature != vertex) { - this.feature = vertex; - } this.layer.events.triggerEvent("vertexmodified", { vertex: vertex.geometry, feature: this.feature, @@ -526,7 +484,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { this.virtualVertices = []; } this.layer.drawFeature(this.feature, this.standalone ? undefined : - this.selectControl.renderIntent); + 'select'); } // 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 @@ -536,7 +494,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { /** * 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: * vertex - {} The vertex being dragged. @@ -572,16 +530,6 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { * Method: resetVertices */ 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) { this.layer.removeFeatures(this.vertices, {silent: true}); this.vertices = []; @@ -632,11 +580,10 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { // check for delete key if(this.feature && OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) { - var vertex = this.dragControl.feature; - if(vertex && - OpenLayers.Util.indexOf(this.vertices, vertex) != -1 && - !this.dragControl.handlers.drag.dragging && - vertex.geometry.parent) { + var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt); + if (vertex && + OpenLayers.Util.indexOf(this.vertices, vertex) != -1 && + !this.handlers.drag.dragging && vertex.geometry.parent) { // remove the vertex vertex.geometry.parent.removeComponent(vertex.geometry); this.layer.events.triggerEvent("vertexremoved", { @@ -645,8 +592,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { pixel: evt.xy }); this.layer.drawFeature(this.feature, this.standalone ? - undefined : - this.selectControl.renderIntent); + undefined : 'select'); this.modified = true; this.resetVertices(); this.setFeatureState(); @@ -800,8 +746,7 @@ OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { * map - {} The control's map. */ setMap: function(map) { - this.standalone || this.selectControl.setMap(map); - this.dragControl.setMap(map); + this.handlers.drag.setMap(map); OpenLayers.Control.prototype.setMap.apply(this, arguments); }, diff --git a/tests/Control/ModifyFeature.html b/tests/Control/ModifyFeature.html index 51a10ac184..91a9430340 100644 --- a/tests/Control/ModifyFeature.html +++ b/tests/Control/ModifyFeature.html @@ -13,34 +13,27 @@ } }; var options = { - geometryTypes: "bar" + documentDrag: true }; var control = new OpenLayers.Control.ModifyFeature(layer, options); t.ok(control.layer == layer, "constructor sets layer correctly"); - t.eq(control.selectControl.geometryTypes, "bar", - "constructor sets options correctly on feature handler"); + t.eq(control.handlers.drag.documentDrag, true, + "constructor sets options correctly on drag handler"); t.eq(control.mode, OpenLayers.Control.ModifyFeature.RESHAPE, "constructor initializes modification mode correctly"); control.destroy(); } function test_destroy(t) { - t.plan(2); + t.plan(1); var map = new OpenLayers.Map("map"); var layer = new OpenLayers.Layer.Vector(); map.addLayer(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(); + t.eq(control.layer, null, "Layer reference removed on destroy."); map.destroy(); } @@ -51,11 +44,11 @@ map.addLayer(layer); var control = new OpenLayers.Control.ModifyFeature(layer); map.addControl(control); - t.ok(!control.selectControl.active, - "select control is not active prior to activating control"); + t.ok(!control.handlers.drag.active, + "drag handler is not active prior to activating control"); control.activate(); - t.ok(control.selectControl.active, - "select control is active after activating control"); + t.ok(control.handlers.drag.active, + "drag handler is active after activating control"); map.destroy(); } @@ -99,7 +92,8 @@ ); // mock up vertex deletion - control.dragControl.feature = point; + var origGetFeatureFromEvent = layer.getFeatureFromEvent; + layer.getFeatureFromEvent = function() { return point; }; control.feature = poly; // we cannot use selectFeature since the control is not part of a map control._originalGeometry = poly.geometry.clone(); @@ -139,17 +133,18 @@ t.eq(control.feature.state, OpenLayers.State.UPDATE, "feature state set to update"); // 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}); // clean up + layer.getFeatureFromEvent = origGetFeatureFromEvent; control.destroy(); layer.destroy(); } function test_onUnSelect(t) { - t.plan(6); + t.plan(5); var layer = new OpenLayers.Layer.Vector(); var control = new OpenLayers.Control.ModifyFeature(layer); var fakeFeature = {'id':'myid'}; @@ -159,7 +154,6 @@ layer.events.on({"afterfeaturemodified": function(event) { 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.") } layer.removeFeatures = function(verts) { t.ok(verts == 'a', "Normal verts removed correctly"); @@ -190,10 +184,6 @@ // If a feature is to be modified, control.selectFeature gets called. // We want this test to fail if selectFeature gets called. var modified = false; - var _ = OpenLayers.Control.ModifyFeature.prototype.selectFeature; - OpenLayers.Control.ModifyFeature.prototype.selectFeature = function() { - modified = true; - } var control = new OpenLayers.Control.ModifyFeature(layer); map.addControl(control); @@ -202,21 +192,19 @@ // register a listener that will stop feature modification layer.events.on({"beforefeaturemodified": function() {return false}}); - // we can initiate feature modification by selecting a feature with - // the control's select feature control - control.selectControl.select(feature); + // we can initiate feature modification by programmatically selecting + // a feature + control.selectFeature(feature); if(modified) { t.fail("selectFeature called, prepping feature for modification"); } else { t.ok(true, "the beforefeaturemodified listener stopped feature modification"); } - - OpenLayers.Control.ModifyFeature.prototype.selectFeature = _; } function test_selectFeature(t) { - t.plan(12); + t.plan(9); var map = new OpenLayers.Map('map'); var layer = new OpenLayers.Layer.Vector("Vectors!", {isBaseLayer: true}); map.addLayer(layer); @@ -228,7 +216,6 @@ t.ok(obj.feature == fakeFeature, "beforefeaturemodified triggered"); }; 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."); } // Start of testing @@ -262,7 +249,7 @@ control.selectFeature(fakeFeature); control.vertices = ['a']; - control.virtualVertices = ['b']; + control.virtualVertices = [{destroy: function() {}}]; layer.addFeatures = function(features) {} @@ -283,7 +270,7 @@ } function test_resetVertices(t) { - t.plan(21); + t.plan(20); var layer = new OpenLayers.Layer.Vector(); var control = new OpenLayers.Control.ModifyFeature(layer); 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.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(); layer.destroy(); } @@ -512,17 +487,19 @@ var control = new OpenLayers.Control.ModifyFeature(layer); map.addControl(control); - control.selectControl.deactivate = function() { + control.handlers.keyboard.deactivate = function() { 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, - "control.deactivate calls deactivate on drag control"); + "control.deactivate calls deactivate on drag handler"); } control.active = true; control.deactivate(); + control.handlers.keyboard.deactivate = OpenLayers.Handler.Keyboard.prototype.deactivate; + control.handlers.drag.deactivate = OpenLayers.Handler.Drag.prototype.deactivate; map.destroy(); } @@ -609,14 +586,17 @@ layer.events.on({"featuremodified": function(event) { t.eq(event.feature.id, poly.id, "featuremodified triggered"); }}); + control.onModification = function(feature) { t.eq(feature.id, poly.id, "onModification called with the right feature on vertex delete"); }; point.geometry.parent = poly.geometry; - control.dragControl.feature = point; + origGetFeatureFromEvent = layer.getFeatureFromEvent; + layer.getFeatureFromEvent = function() { return point; }; control.handleKeypress({keyCode:46}); layer.drawFeature = oldDraw; + layer.getFeatureFromEvent = origGetFeatureFromEvent; map.destroy(); } @@ -694,7 +674,7 @@ function test_standalone(t) { - t.plan(18); + t.plan(17); var map = new OpenLayers.Map("map"); var layer = new OpenLayers.Layer.Vector(); @@ -733,7 +713,6 @@ // activate control control.activate(); t.eq(control.active, true, "[activate] control activated"); - t.eq(control.selectControl, null, "[activate] no select control"); // manually select feature for editing control.selectFeature(f1); @@ -761,22 +740,19 @@ t.ok(log[0].feature === f2, "[deactivate] correct feature"); 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 // virtual vertices control.selectFeature(f2); - control.dragControl.handlers.feature.triggerCallback("mousemove", "in", [control.vertices[0]]); - t.eq(control.dragControl.handlers.drag.active, true, "can drag vertex of feature f2"); - control.dragControl.handlers.feature.triggerCallback("mousemove", "in", [control.virtualVertices[0]]); - t.eq(control.dragControl.handlers.drag.active, true, "can drag virtual vertex of feature f2"); + var origGetFeatureFromEvent = layer.getFeatureFromEvent; + layer.getFeatureFromEvent = function() { return control.vertices[0]; }; + control.handlers.drag.callbacks.down.call(control, new OpenLayers.Pixel(0,0)); + 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(); map.destroy();