From 49774f6ab549571966dd95e0d8e42fa2be8ea8ed Mon Sep 17 00:00:00 2001 From: pgiraud Date: Fri, 3 Apr 2009 10:28:41 +0000 Subject: [PATCH] SelectFeature now takes a hilightOnly config options, that way user can hover features without actually selecting them, combining two differents controls will allow to separate hover and click events, patches from ahocevar, elemoine and pgiraud, reviewers are ahocevar, elemoine and pgiraud git-svn-id: http://svn.openlayers.org/trunk/openlayers@9176 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- examples/highlight-feature.html | 80 ++++++++++++ lib/OpenLayers/Control/SelectFeature.js | 102 +++++++++++++-- tests/Control/SelectFeature.html | 161 ++++++++++++++++++++++++ 3 files changed, 334 insertions(+), 9 deletions(-) create mode 100644 examples/highlight-feature.html 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); + } +