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

Reply via email to