On Fri, Mar 6, 2009 at 2:50 PM, Alexandre Dube <[email protected]> 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
[email protected]
http://openlayers.org/mailman/listinfo/dev