[Commits] r1294 - in core/trunk/geoext: lib/GeoExt/widgets/tree tests/lib/GeoExt/widgets/tree

commits at geoext.org commits at geoext.org
Wed Jul 29 11:38:05 CEST 2009


Author: ahocevar
Date: 2009-07-29 11:38:05 +0200 (Wed, 29 Jul 2009)
New Revision: 1294

Modified:
   core/trunk/geoext/lib/GeoExt/widgets/tree/LayerNode.js
   core/trunk/geoext/tests/lib/GeoExt/widgets/tree/LayerNode.html
Log:
enforce one layer visible when checkedGroup is set. r=elemoine (closes #109)


Modified: core/trunk/geoext/lib/GeoExt/widgets/tree/LayerNode.js
===================================================================
--- core/trunk/geoext/lib/GeoExt/widgets/tree/LayerNode.js	2009-07-29 08:52:37 UTC (rev 1293)
+++ core/trunk/geoext/lib/GeoExt/widgets/tree/LayerNode.js	2009-07-29 09:38:05 UTC (rev 1294)
@@ -34,22 +34,25 @@
         if (a.checked === undefined) {
             a.checked = this.node.layer.getVisibility();
         }
-        GeoExt.tree.LayerNodeUI.superclass.render.call(this, bulkRender);
+        GeoExt.tree.LayerNodeUI.superclass.render.apply(this, arguments);
+        var cb = this.checkbox;
         if (a.radioGroup && this.radio === null) {
-            this.radio = Ext.DomHelper.insertAfter(this.checkbox,
+            this.radio = Ext.DomHelper.insertAfter(cb,
                 ['<input type="radio" class="gx-tree-layer-radio" name="',
                 a.radioGroup, '_radio"></input>'].join(""));
         }
         if(a.checkedGroup) {
             // replace the checkbox with a radio button
-            var radio = Ext.DomHelper.insertAfter(this.checkbox,
+            var radio = Ext.DomHelper.insertAfter(cb,
                 ['<input type="radio" name="', a.checkedGroup,
-                '_checkbox" class="', this.checkbox.className, 
-                this.checkbox.checked ? '" checked="checked"' : '',
+                '_checkbox" class="', cb.className,
+                cb.checked ? '" checked="checked"' : '',
                 '"></input>'].join(""));
-            Ext.get(this.checkbox).remove();
+            radio.defaultChecked = cb.defaultChecked;
+            Ext.get(cb).remove();
             this.checkbox = radio;
         }
+        this.enforceOneVisible();
     },
     
     /** private: method[onClick]
@@ -59,34 +62,85 @@
         if (e.getTarget('.gx-tree-layer-radio', 1)) {
             this.fireEvent("radiochange", this.node);
         } else if(e.getTarget('.x-tree-node-cb', 1)) {
-            GeoExt.tree.LayerNodeUI.superclass.onCheckChange.call(this);
+            this.onCheckChange();
         } else {
-            GeoExt.tree.LayerNodeUI.superclass.onClick.call(this, e);
+            GeoExt.tree.LayerNodeUI.superclass.onClick.apply(this, arguments);
         }
     },
     
     /** private: method[toggleCheck]
-     *  :param value: ``Boolean``
+     * :param value: ``Boolean``
      */
     toggleCheck: function(value) {
-        GeoExt.tree.LayerNodeUI.superclass.toggleCheck.call(this, value);
-        var node = this.node;
-        var layer = this.node.layer;
-        node.visibilityChanging = true;
-        if(this.checkbox && (layer.getVisibility() != this.isChecked())) {
-            layer.setVisibility(this.isChecked());
+        if(!this._visibilityChanging) {
+            this._visibilityChanging = true;
+            
+            // make sure we do not hide the checked layer from a checkedGroup
+            value = (value === undefined ? !this.isChecked() : value) ||
+                    (this.isChecked() && !!this.node.attributes.checkedGroup);
+            GeoExt.tree.LayerNodeUI.superclass.toggleCheck.call(this, value);
+            
+            this.enforceOneVisible();
+
+            delete this._visibilityChanging;
         }
-        node.visibilityChanging = false;
     },
     
+    /** private: method[enforceOneVisible]
+     * 
+     *  Makes sure that only one layer is visible if checkedGroup is set.
+     *  This can only work when ``layer.setVisibility()`` does not trigger
+     *  ``this.toggleCheck()``. If it does, ``this._visibilityChanging`` has
+     *  to be set to true before calling this method.
+     */
+    enforceOneVisible: function() {
+        var attributes = this.node.attributes;
+        var group = attributes.checkedGroup;
+        if(group) {
+            var layer = this.node.layer;
+            var checkedNodes = this.node.getOwnerTree().getChecked();
+            var checkedCount = 0;
+            // enforce "not more than one visible"
+            Ext.each(checkedNodes, function(n){
+                var ui = n.getUI();
+                var l = n.layer
+                if(!n.hidden && n.attributes.checkedGroup === group) {
+                    checkedCount++;
+                    if(l != layer && attributes.checked) {
+                        // toggleCheck won't be called (_visibilityChanging
+                        // set to true when we are called from toggleCheck(),
+                        // and layer visibility handler is not yet set when we
+                        // are called from render()), so we synchronize the
+                        // button state manually
+                        ui.checkbox.defaultChecked = false;
+                        ui.checkbox.checked = false;
+                        l.setVisibility(false);
+                    }
+                }
+            });
+            // enforce "at least one visible"
+            if(checkedCount === 0 && attributes.checked == false) {
+                var ui = this.node.getUI();
+                // toggleCheck won't be called (_visibilityChanging set to
+                // true when we are called from toggleCheck(), and layer
+                // visibility handler is not yet set when we are called from
+                // render()), so we synchronize the button state manually
+                ui.checkbox.defaultChecked = true;
+                ui.checkbox.checked = true;
+                layer.setVisibility(true);
+            }
+        }
+    },
+    
     /** private: method[destroy]
      */
     destroy: function() {
         delete this.radio;
-        GeoExt.tree.LayerNodeUI.superclass.destroy.call(this);
+        GeoExt.tree.LayerNodeUI.superclass.destroy.apply(this, arguments);
     }
 });
 
+
 /** api: (define)
  *  module = GeoExt.tree
  *  class = LayerNode
@@ -156,21 +210,15 @@
      */
     childNodeType: null,
     
-    /** private: property[visibilityChanging]
-     * {Boolean} private property indicating layer visibility being changed
-     *     by this node in order to prevent visibilitychanged events bouncing
-     *     back and forth
-     */
-    visibilityChanging: false,
-    
     /** private: method[constructor]
      *  Private constructor override.
      */
     constructor: function(config) {
         config.leaf = config.leaf || !config.children;
         
-        config.iconCls = typeof config.iconCls == "undefined" &&
-            !config.children ? "layer-icon" : config.iconCls;
+        if(!config.iconCls && !config.children) {
+            config.iconCls = "gx-tree-layer-icon";
+        }
         
         this.defaultUI = this.defaultUI || GeoExt.tree.LayerNodeUI;
         this.addEvents(
@@ -214,6 +262,13 @@
             
             if(layer) {
                 this.layer = layer;
+                // no DD and radio buttons for base layers
+                if(layer.isBaseLayer) {
+                    this.draggable = false;
+                    Ext.applyIf(this.attributes, {
+                        checkedGroup: "gx_baselayer"
+                    });
+                }
                 if(!this.text) {
                     this.text = layer.name;
                 }
@@ -232,7 +287,7 @@
                 this.addStoreEventHandlers(layer);
             }            
         }
-        GeoExt.tree.LayerNode.superclass.render.call(this, bulkRender);
+        GeoExt.tree.LayerNode.superclass.render.apply(this, arguments);
     },
     
     /** private: method[addVisibilityHandlers]
@@ -254,10 +309,7 @@
      *  handler for visibilitychanged events on the layer
      */
     onLayerVisibilityChanged: function() {
-        if(!this.visibilityChanging &&
-                this.attributes.checked != this.layer.getVisibility()) {
-            this.getUI().toggleCheck(this.layer.getVisibility());
-        }
+        this.getUI().toggleCheck(this.layer.getVisibility());
     },
     
     /** private: method[onCheckChange]
@@ -267,10 +319,14 @@
      *  handler for checkchange events 
      */
     onCheckChange: function(node, checked) {
-        if (checked && this.layer.isBaseLayer && this.layer.map) {
-            this.layer.map.setBaseLayer(this.layer);
+        if(checked != this.layer.getVisibility()) {
+            var layer = this.layer;
+            if(checked && layer.isBaseLayer && layer.map) {
+                layer.map.setBaseLayer(layer);
+            } else {
+                layer.setVisibility(checked);
+            }
         }
-        this.layer.setVisibility(checked);
     },
     
     /** private: method[addStoreEventHandlers]
@@ -299,12 +355,13 @@
             l = records[i].get("layer");
             if(this.layer == l) {
                 this.getUI().show();
+                break;
             } else if (this.layer == l.name) {
                 // layer is a string, which means the node has not yet
                 // been rendered because the layer was not found. But
                 // now we have the layer and can render.
-                this.render(bulkRender);
-                return;
+                this.render();
+                break;
             }
         }
     },
@@ -369,7 +426,7 @@
         delete this.layerStore;
         this.un("checkchange", this.onCheckChange, this);
 
-        GeoExt.tree.LayerNode.superclass.destroy.call(this);
+        GeoExt.tree.LayerNode.superclass.destroy.apply(this, arguments);
     }
 });
 

Modified: core/trunk/geoext/tests/lib/GeoExt/widgets/tree/LayerNode.html
===================================================================
--- core/trunk/geoext/tests/lib/GeoExt/widgets/tree/LayerNode.html	2009-07-29 08:52:37 UTC (rev 1293)
+++ core/trunk/geoext/tests/lib/GeoExt/widgets/tree/LayerNode.html	2009-07-29 09:38:05 UTC (rev 1294)
@@ -26,7 +26,7 @@
         
         function test_render(t) {
             
-            t.plan(8);
+            t.plan(9);
             
             var layer = new OpenLayers.Layer("foo");
             
@@ -64,16 +64,93 @@
                 node.ui.onClick({getTarget: function() {return true}});
                 
                 t.eq(node.ui.checkbox.type, "radio", "checkbox rendered as radio button when checkedGroup is configured");
-                t.eq(node.ui.checkbox.name, "check_checkbox", "option group name set correclty according to checkedGroup");
+                t.eq(node.ui.checkbox.name, "check_checkbox", "option group name set correctly according to checkedGroup");
                 
+                layer.setVisibility(false);
+                t.eq(layer.visibility, true, "unchecking a layer with checkedGroup has no effect");
+                
+                delete node.attributes.checkedGroup;
                 node.ui.toggleCheck();
-                t.eq(layer.visibility, false, "unchecking node hides layer");
+                t.eq(layer.visibility, false, "unchecking a layer without checkedGroup hides the layer");
             });
 
             mapPanel.render("map");
             
+            mapPanel.destroy();
         }
+        
+        function test_enforceOneVisible(t) {
+            t.plan(8);
 
+            var layers = [
+                new OpenLayers.Layer("foo"),
+                new OpenLayers.Layer("bar")
+            ];
+            var mapPanel = new GeoExt.MapPanel({
+                layers: layers,
+                allOverlays: true
+            });
+            var root = new GeoExt.tree.LayerContainer({
+                loader: {
+                    baseAttrs: {checkedGroup: "group"}
+                },
+                expanded: true
+            });
+            var panel = new Ext.tree.TreePanel({
+                renderTo: "tree",
+                root: root
+            });
+            mapPanel.render("map");
+
+            // two overlay layers in the same checkedGroup: only one can be visible
+            var nodes = panel.getRootNode().childNodes;
+            t.eq(nodes[0].layer.getVisibility(), false, "Layer on top is hidden");
+            t.eq(nodes[1].layer.getVisibility(), true, "Layer on bottom is visible");
+            
+            delete root.loader.baseAttrs.checkedGroup;
+            mapPanel.map.allOverlays = false;
+            
+            // without a custom checkedGroup, base layers get the gx_baselayer group assigned
+            mapPanel.layers.on("add", function(){
+                t.eq(nodes[0].attributes.checkedGroup, "gx_baselayer", "gx_baselayer checkedGroup set for base layer");
+            }, this, {single: true});
+            mapPanel.map.addLayer(new OpenLayers.Layer("foo1", {isBaseLayer: true}));
+                        
+            root.loader.baseAttrs.checkedGroup = "another_group";
+            
+            // a custom checkedGroup will override the gx_baselayer default
+            mapPanel.layers.on("add", function() {
+                t.eq(nodes[0].attributes.checkedGroup, "another_group", "custom checkedGroup set for base layer");
+            }, this, {single: true});
+            mapPanel.map.addLayer(new OpenLayers.Layer("bar", {isBaseLayer: true}));
+            
+            // overlays also get the custom checkedGroup assigned
+            mapPanel.layers.on("add", function() {
+                t.eq(nodes[0].attributes.checkedGroup, "another_group", "custom checkedGroup set for overlay");
+                // the another_group baselayer from above is invisible (the gx_baselayer one is visible)
+                t.eq(nodes[0].layer.getVisibility(), true, "overlay in checkedGroup visible because no other layer in group is visible");
+                // now making it visible
+                nodes[1].layer.setVisibility(true);
+                // and the overlay in the same checkedGroup gets hidden
+                t.eq(nodes[0].layer.getVisibility(), false, "overlay in checkedGroup now hidden because base layer in group is visible");
+            }, this, {single: true});
+            mapPanel.map.addLayer(new OpenLayers.Layer("foo2", {isBaseLayer: false}));
+            
+            // remove all layers except one visible and one invisible layer
+            mapPanel.map.removeLayer(nodes[0].layer);
+            mapPanel.map.removeLayer(nodes[0].layer);
+            mapPanel.map.removeLayer(nodes[0].layer);
+            // now there is only one layer in the another_group
+            // "bar" (invisible) is now in the group at position [0], "foo" (visible) at [1]
+            mapPanel.layers.on("remove", function() {
+                t.eq(nodes[0].layer.getVisibility(), true, "Previously invisible layer was made visible because the visible layer has been removed")
+            }, this, {single: true});
+            // removing "foo", so "bar" will have to be made visible
+            mapPanel.map.removeLayer(nodes[1].layer);
+            
+            mapPanel.destroy();
+        }
+
         function test_changelayername(t) {
             t.plan(2);
 



More information about the Commits mailing list