diff --git a/examples/highlight-feature.html b/examples/highlight-feature.html new file mode 100644 index 0000000000..cf7869c77f --- /dev/null +++ b/examples/highlight-feature.html @@ -0,0 +1,80 @@ + + + SelectFeature Control for Select and Highlight + + + + + + + + +

OpenLayers Select and Highlight Feature Example

+

+ Select features on click, highlight features on hover. +

+
+

Select features by clicking on them. Just highlight features by hovering over + them.

+ + diff --git a/lib/OpenLayers/Control/SelectFeature.js b/lib/OpenLayers/Control/SelectFeature.js index 67232a0342..774a2afac4 100644 --- a/lib/OpenLayers/Control/SelectFeature.js +++ b/lib/OpenLayers/Control/SelectFeature.js @@ -19,6 +19,16 @@ * - */ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { + + /** + * Constant: EVENT_TYPES + * + * Supported event types: + * - *beforefeaturehighlighted* Triggered before a feature is highlighted + * - *featurehighlighted* Triggered when a feature is highlighted + * - *featureunhighlighted* Triggered when a feature is unhighlighted + */ + EVENT_TYPES: ["beforefeaturehighlighted", "featurehighlighted", "featureunhighlighted"], /** * Property: multipleKey @@ -60,6 +70,14 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { * 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 @@ -150,6 +168,11 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { * options - {Object} */ initialize: function(layers, options) { + // concatenate events specific to this control 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]); if(!(layers instanceof Array)) { layers = [layers]; @@ -331,9 +354,14 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { * feature - {} */ overFeature: function(feature) { - if(this.hover && - (OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) == -1)) { - this.select(feature); + var layer = feature.layer; + if(this.hover) { + if(this.highlightOnly) { + this.highlight(feature); + } else if(OpenLayers.Util.indexOf( + layer.selectedFeatures, feature) == -1) { + this.select(feature); + } } }, @@ -347,9 +375,68 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { */ outFeature: function(feature) { if(this.hover) { - this.unselect(feature); + if(this.highlightOnly) { + // we do nothing if we're not the last highlighter of the + // feature + if(feature._lastHighlighter == this.id) { + // if another select control had highlighted the feature before + // we did it ourself then we use that control to highlight the + // feature as it was before we highlighted it, else we just + // unhighlight it + if(feature._prevHighlighter && + feature._prevHighlighter != this.id) { + delete feature._lastHighlighter; + var control = this.map.getControl( + feature._prevHighlighter); + if(control) { + control.highlight(feature); + } + } else { + this.unhighlight(feature); + } + } + } else { + this.unselect(feature); + } } }, + + /** + * Method: highlight + * Redraw feature with the select style. + * + * Parameters: + * feature - {} + */ + highlight: function(feature) { + var layer = feature.layer; + var cont = this.events.triggerEvent("beforefeaturehighlighted", { + feature : feature + }); + if(cont !== false) { + feature._prevHighlighter = feature._lastHighlighter; + feature._lastHighlighter = this.id; + var style = this.selectStyle || this.renderIntent; + layer.drawFeature(feature, style); + this.events.triggerEvent("featurehighlighted", {feature : feature}); + } + }, + + /** + * Method: unhighlight + * Redraw feature with the "default" style + * + * Parameters: + * feature - {} + */ + unhighlight: function(feature) { + var layer = feature.layer; + feature._lastHighlighter = feature._prevHighlighter; + delete feature._prevHighlighter; + layer.drawFeature(feature, feature.style || feature.layer.style || + "default"); + this.events.triggerEvent("featureunhighlighted", {feature : feature}); + }, /** * Method: select @@ -369,10 +456,7 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { if(cont !== false) { layer.selectedFeatures.push(feature); this.layerData = {}; - - var selectStyle = this.selectStyle || this.renderIntent; - - layer.drawFeature(feature, selectStyle); + this.highlight(feature); layer.events.triggerEvent("featureselected", {feature: feature}); this.onSelect.call(this.scope, feature); } @@ -390,7 +474,7 @@ OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { unselect: function(feature) { var layer = feature.layer; // Store feature style for restoration later - layer.drawFeature(feature, "default"); + this.unhighlight(feature); OpenLayers.Util.removeItem(layer.selectedFeatures, feature); layer.events.triggerEvent("featureunselected", {feature: feature}); this.onUnselect.call(this.scope, feature); diff --git a/tests/Control/SelectFeature.html b/tests/Control/SelectFeature.html index e6adc0ed2c..53b2ce04a3 100644 --- a/tests/Control/SelectFeature.html +++ b/tests/Control/SelectFeature.html @@ -206,6 +206,167 @@ 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.eq(feature._lastHighlighter, ctrl2.id, + "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.eq(feature._lastHighlighter, ctrl1.id, + "feature._lastHighlighter properly set after \"clickin\""); + t.eq(feature._prevHighlighter, ctrl2.id, + "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.eq(feature._lastHighlighter, ctrl1.id, + "feature._lastHighlighter properly set after \"nouseout\""); + t.ok(feature._prevHighlighter, ctrl2.id, + "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.eq(feature._lastHighlighter, ctrl2.id, + "feature._lastHighlighter properly set after \"mouseover\""); + t.eq(feature._prevHighlighter, ctrl1.id, + "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.eq(feature._lastHighlighter, ctrl1.id, + "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); + } +