On Fri, Mar 6, 2009 at 2:50 PM, Alexandre Dube <ad...@mapgears.com> wrote: > Hi Eric, > > This looks flawless but the easiest way to know would be to test it. I > would be glad to test this. > > One thing to though is that a SelectFeature control can draw features > depending on its renderIntent value OR selectStyle value. So, instead of > storing the previous renderIntent, it could be the previous drawer ( i.e. > control ) and call feature._previousdrawer.drawFeature(feature) and the > previous control itself would draw the feature depending if it has a > selectStyle or renderIntent. > > Your solution looks simple and great. It has this concept : > > this == current drawer > feature._previousdrawer ( or previousrenderintent ) == the previous drawer > > With our 2 controls example that is enough. Plus, as long as we keep the > selectedFeature array in the layer objet, there's no need to know more than > "current" and "previous". If we have let's say ( silly ) 6 select features > at the same time, having all their own colors, a feature is > selected/highlighted and unselected/unhighlighted one at a time so "curent" > and "previous" drawer is enough. With that in mind, no need for a "stack" > of renderIntent or a "stack" of _previousDrawer. > > What do you think ?
I like it. See the attached eric-to-alexandre patch ;-) -- Eric
Index: tests/Control/SelectFeature.html =================================================================== --- tests/Control/SelectFeature.html (revision 9041) +++ tests/Control/SelectFeature.html (working copy) @@ -81,7 +81,7 @@ // test that onSelect gets called properly control.onSelect = function(feature) { feature.tested += 1; - t.eq(feature, features[feature.index], + t.ok(feature == features[feature.index], "onSelect called with proper feature (" + feature.index + ")"); t.eq(feature.tested, feature.test, "onSelect called only once for feature (" + feature.index + ")"); @@ -90,7 +90,7 @@ // test that onUnselect gets called properly control.onUnselect = function(feature) { feature.tested += 1; - t.eq(feature, features[feature.index], + t.ok(feature == features[feature.index], "onUnselect called with proper feature (" + feature.index + ")"); t.eq(feature.tested, feature.test, "onUnselect called only once for feature (" + feature.index + ")"); @@ -195,6 +195,166 @@ control.deactivate(); } + function test_highlighyOnly(t) { + t.plan(23); + + /* + * setup + */ + + var map, layer, ctrl1, ctrl2, _feature, feature, evt, _style; + + map = new OpenLayers.Map("map"); + layer = new OpenLayers.Layer.Vector("name", {isBaseLayer: true}); + map.addLayer(layer); + + ctrl1 = new OpenLayers.Control.SelectFeature(layer, { + highlightOnly: false, + hover: false + }); + map.addControl(ctrl1); + + ctrl2 = new OpenLayers.Control.SelectFeature(layer, { + highlightOnly: true, + hover: true + }); + map.addControl(ctrl2); + + ctrl2.activate(); + ctrl1.activate(); + + feature = new OpenLayers.Feature.Vector(); + feature.layer = layer; + + // override the layer's getFeatureFromEvent func so that it always + // returns the feature referenced to by _feature + layer.getFeatureFromEvent = function(evt) { return _feature; }; + + evt = {xy: new OpenLayers.Pixel(Math.random(), Math.random())}; + + map.zoomToMaxExtent(); + + /* + * tests + */ + + // with renderIntent + + ctrl1.renderIntent = "select"; + ctrl2.renderIntent = "temporary"; + + // mouse over feature, feature is drawn with "temporary" + _feature = feature; + evt.type = "mousemove"; + map.events.triggerEvent("mousemove", evt); + t.eq(feature.renderIntent, "temporary", + "feature drawn with expected render intent after \"mouseover\""); + t.ok(feature._lastHighlighter == ctrl2, + "feature._lastHighlighter properly set after \"mouseover\""); + t.eq(feature._prevHighlighter, undefined, + "feature._prevHighlighter properly set after \"mouseover\""); + + // click in feature, feature is drawn with "select" + _feature = feature; + evt.type = "click"; + map.events.triggerEvent("click", evt); + t.eq(feature.renderIntent, "select", + "feature drawn with expected render intent after \"clickin\""); + t.ok(feature._lastHighlighter == ctrl1, + "feature._lastHighlighter properly set after \"clickin\""); + t.ok(feature._prevHighlighter == ctrl2, + "feature._prevHighlighter properly set after \"clickin\""); + + // mouse out of feature, feature is still drawn with "select" + _feature = null; + evt.type = "mousemove"; + map.events.triggerEvent("mousemove", evt); + t.eq(feature.renderIntent, "select", + "feature drawn with expected render intent after \"mouseout\""); + t.ok(feature._lastHighlighter == ctrl1, + "feature._lastHighlighter properly set after \"nouseout\""); + t.ok(feature._prevHighlighter == ctrl2, + "feature._prevHighlighter properly set after \"mouseout\""); + + // mouse over feature again, feature is drawn with "temporary" + _feature = feature; + evt.type = "mousemove"; + map.events.triggerEvent("mousemove", evt); + t.eq(feature.renderIntent, "temporary", + "feature drawn with expected render intent after \"mouseover\""); + t.ok(feature._lastHighlighter == ctrl2, + "feature._lastHighlighter properly set after \"mouseover\""); + t.ok(feature._prevHighlighter == ctrl1, + "feature._prevHighlighter properly set after \"mouseover\""); + + // mouve out of feature again, feature is still drawn with "select" + _feature = null; + evt.type = "mousemove"; + map.events.triggerEvent("mousemove", evt); + t.eq(feature.renderIntent, "select", + "feature drawn with expected render intent after \"mouseout\""); + t.ok(feature._lastHighlighter == ctrl1, + "feature._lastHighlighter properly set after \"mouseout\""); + t.eq(feature._prevHighlighter, undefined, + "feature._prevHighlighter properly set after \"mouseout\""); + + // click out of feature, feature is drawn with "default" + _feature = null; + evt.type = "click"; + map.events.triggerEvent("click", evt); + t.eq(feature.renderIntent, "default", + "feature drawn with expected render intent after \"clickout\""); + t.eq(feature._lastHighlighter, undefined, + "feature._lastHighlighter properly set after \"clickout\""); + t.eq(feature._prevHighlighter, undefined, + "feature._prevHighlighter properly set after \"clickout\""); + + // with selectStyle + + ctrl1.selectStyle = OpenLayers.Feature.Vector.style["select"]; + ctrl2.selectStyle = OpenLayers.Feature.Vector.style["temporary"]; + + layer.renderer.drawFeature = function(f, s) { + var style = OpenLayers.Feature.Vector.style[_style]; + t.eq(s, style, "renderer drawFeature called with expected style obj (" + _style + ")"); + }; + + // mouse over feature, feature is drawn with "temporary" + _feature = feature; + _style = "temporary"; + evt.type = "mousemove"; + map.events.triggerEvent("mousemove", evt); + + // click in feature, feature is drawn with "select" + _feature = feature; + _style = "select"; + evt.type = "click"; + map.events.triggerEvent("click", evt); + + // mouse out of feature, feature is still drawn with "select" and + // the renderer drawFeature method should not be called + _feature = null; + evt.type = "mousemove"; + map.events.triggerEvent("mousemove", evt); + + // mouse over feature again, feature is drawn with "temporary" + _feature = feature; + _style = "temporary"; + evt.type = "mousemove"; + map.events.triggerEvent("mousemove", evt); + + // mouve out of feature again, feature is still drawn with "select" + _feature = null; + _style = "select"; + evt.type = "mousemove"; + map.events.triggerEvent("mousemove", evt); + + // click out of feature, feature is drawn with "default" + _feature = null; + _style = "default"; + evt.type = "click"; + map.events.triggerEvent("click", evt); + } </script> </head> <body> Index: lib/OpenLayers/Control/SelectFeature.js =================================================================== --- lib/OpenLayers/Control/SelectFeature.js (revision 9041) +++ lib/OpenLayers/Control/SelectFeature.js (working copy) @@ -18,8 +18,16 @@ * - <OpenLayers.Control> */ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { - /** + * Constant: EVENT_TYPES + * + * Supported event types: + * - *featurehighlighted* Triggered when a feature is highlighted + * - *featureunhighlighted* Triggered when a feature is unhighlighted + */ + EVENT_TYPES: ["featurehighlighted", "featureunhighlighted"], + + /** * Property: multipleKey * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets * the <multiple> property to true. Default is null. @@ -59,6 +67,14 @@ * ignores clicks and only listens to mouse moves. */ hover: false, + + /** + * APIProperty: highlightOnly + * {Boolean} If true do not actually select features (i.e. place them in the + * layer's selected features array) just highlight them. This property has + * no effect if hover is false. Defaults to false. + */ + highlightOnly: false, /** * APIProperty: box @@ -141,6 +157,11 @@ * options - {Object} */ initialize: function(layer, options) { + // concatenate events specific to vector with those from the base + this.EVENT_TYPES = + OpenLayers.Control.SelectFeature.prototype.EVENT_TYPES.concat( + OpenLayers.Control.prototype.EVENT_TYPES + ); OpenLayers.Control.prototype.initialize.apply(this, [options]); this.layer = layer; var callbacks = { @@ -300,9 +321,13 @@ * feature - {<OpenLayers.Feature.Vector>} */ overFeature: function(feature) { - if(this.hover && - (OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) == -1)) { - this.select(feature); + if(this.hover) { + if(this.highlightOnly) { + this.highlight(feature); + } else if(OpenLayers.Util.indexOf( + this.layer.selectedFeatures, feature) == -1) { + this.select(feature); + } } }, @@ -316,9 +341,49 @@ */ outFeature: function(feature) { if(this.hover) { - this.unselect(feature); + if(this.highlightOnly) { + if(feature._lastHighlighter === this) { + if(feature._prevHighlighter) { + feature._lastHighlighter = undefined; + feature._prevHighlighter.highlight(feature); + } else { + this.unhighlight(feature); + } + } + } else { + this.unselect(feature); + } } }, + + /** + * Method: highlight + * Redraw feature with the select style. + * + * Parameters: + * feature - {<OpenLayers.Feature.Vector>} + */ + highlight: function(feature) { + feature._prevHighlighter = feature._lastHighlighter; + feature._lastHighlighter = this; + var style = this.selectStyle || this.renderIntent; + this.layer.drawFeature(feature, style); + this.events.triggerEvent("featurehighlighted", {feature : feature}); + }, + + /** + * Method: unhighlight + * Redraw feature with the "default" style + * + * Parameters: + * feature - {<OpenLayers.Feature.Vector>} + */ + unhighlight: function(feature) { + feature._lastHighlighter = feature._prevHighlighter; + feature._prevHighlighter = undefined; + this.layer.drawFeature(feature, "default"); + this.events.triggerEvent("featureunhighlighted", {feature : feature}); + }, /** * Method: select @@ -336,10 +401,7 @@ }); if(cont !== false) { this.layer.selectedFeatures.push(feature); - - var selectStyle = this.selectStyle || this.renderIntent; - - this.layer.drawFeature(feature, selectStyle); + this.highlight(feature); this.layer.events.triggerEvent("featureselected", {feature: feature}); this.onSelect.call(this.scope, feature); } @@ -356,12 +418,12 @@ */ unselect: function(feature) { // Store feature style for restoration later - this.layer.drawFeature(feature, "default"); + this.unhighlight(feature); OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature); this.layer.events.triggerEvent("featureunselected", {feature: feature}); this.onUnselect.call(this.scope, feature); }, - + /** * Method: selectBox * Callback from the handlers.box set up when <box> selection is true Index: examples/highlight-feature.html =================================================================== --- examples/highlight-feature.html (revision 0) +++ examples/highlight-feature.html (revision 0) @@ -0,0 +1,123 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>SelectFeature Control on Layer.Vector</title> + <link rel="stylesheet" href="../theme/default/style.css" type="text/css" /> + <link rel="stylesheet" href="style.css" type="text/css" /> + <style type="text/css"> + #controlToggle li { + list-style: none; + } + </style> + <script src="../lib/OpenLayers.js"></script> + <script type="text/javascript"> + var map, drawControls; + OpenLayers.Feature.Vector.style['default']['strokeWidth'] = '2'; + function init(){ + map = new OpenLayers.Map('map'); + var wmsLayer = new OpenLayers.Layer.WMS( + "OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0", + {layers: 'basic'} + ); + + var vectors = new OpenLayers.Layer.Vector("Vector Layer"); + map.addLayers([wmsLayer, vectors]); + map.addControl(new OpenLayers.Control.LayerSwitcher()); + + drawControls = { + point: new OpenLayers.Control.DrawFeature( + vectors, OpenLayers.Handler.Point + ), + line: new OpenLayers.Control.DrawFeature( + vectors, OpenLayers.Handler.Path + ), + polygon: new OpenLayers.Control.DrawFeature( + vectors, OpenLayers.Handler.Polygon + ), + highlight: [ + new OpenLayers.Control.SelectFeature( + vectors, { + multiple: false, hover: true, highlightOnly: true, + toggleKey: "ctrlKey", // ctrl key removes from selection + multipleKey: "shiftKey" // shift key adds to selection + }), + new OpenLayers.Control.SelectFeature( + vectors, { + clickout: true, toggle: false, + multiple: false, hover: false, + toggleKey: "ctrlKey", // ctrl key removes from selection + multipleKey: "shiftKey", // shift key adds to selection + box: true + }) + ] + }; + + for(var key in drawControls) { + var controls = drawControls[key]; + if(!(controls instanceof Array)) { + controls = [controls]; + } + for(var i = 0; i < controls.length; i++) { + map.addControl(controls[i]); + } + } + map.setCenter(new OpenLayers.LonLat(0, 0), 3); + + } + + function toggleControl(element) { + for(key in drawControls) { + var controls = drawControls[key]; + if(!(controls instanceof Array)) { + controls = [controls]; + } + for(var i = 0; i < controls.length; i++) { + if(element.value == key && element.checked) { + controls[i].activate(); + } else { + controls[i].deactivate(); + } + } + } + } + </script> + </head> + <body onload="init()"> + <h1 id="title">OpenLayers Select Feature Example</h1> + <p id="shortdesc"> + Select a feature on hover or click with the Control.SelectFeature on a + vector layer. + </p> + <div id="map" class="smallmap"></div> + <ul id="controlToggle"> + <li> + <input type="radio" name="type" value="none" id="noneToggle" + onclick="toggleControl(this);" checked="checked" /> + <label for="noneToggle">navigate</label> + </li> + <li> + <input type="radio" name="type" value="point" id="pointToggle" + onclick="toggleControl(this);" /> + <label for="pointToggle">draw point</label> + </li> + <li> + <input type="radio" name="type" value="line" id="lineToggle" + onclick="toggleControl(this);" /> + <label for="lineToggle">draw line</label> + </li> + <li> + <input type="radio" name="type" value="polygon" id="polygonToggle" + onclick="toggleControl(this);" /> + <label for="polygonToggle">draw polygon</label> + </li> + <li> + <input type="radio" name="type" value="highlight" id="highlightToggle" + onclick="toggleControl(this);" /> + <label for="highlightToggle">highlight feature</label> + </li> + </ul> + <p>Use the shift key to select multiple features. Use the ctrl key to + toggle selection on features one at a time. Note: the "clickout" option has no + effect when "hover" is selected.</p> + </body> +</html>
_______________________________________________ Dev mailing list Dev@openlayers.org http://openlayers.org/mailman/listinfo/dev