2012/07/06

Replacing Expand and Collapse Icons in Tree Control in TWaver

One of the advantages of TWaver is its “flexible and customizable function.”Let’s see an example: to customize the label of a Tree node.
  • The label of Tree and Network shows the name of the element as default. After setting Styles.TREE_LABEL, the label of Tree shows the value of Styles.TREE_LABEL. In this way, the labels of Tree and Network show different things.
  • In addition, you could even set Tree#labelFunction. For example, through the following code, the label of Node can show the name and the label of Link can show Styles.Tree_LABEL
tree.labelFunction = function(e:IElement):String{
    if(e is Link){
        return e.getStyle(Styles.TREE_LABEL);
    }else{
        return e.name;
    }

Since TWaver is so flexible, can it customize the expand and the collapse icons in the Tree control?
Let’s first see the most original way:
  1. Define 2 classes:

  2. [Embed(source="assets/plus.png")]
    [Bindable]
    public var plus:Class;

    [Embed(source="assets/minus.png")]
    [Bindable]
    public var minus:Class;  

  3. Set disclosureOpenIcon and disclosureClosedIcon of Tree as the classes just defined above.

  4. <tw:Tree id="tree2" width="100%" height="100%" disclosureOpenIcon="{minus}" disclosureClosedIcon="{plus}"/>

    See the effects as follows: 
Someone may say that it is not flexible enough, for with resource files included in SWF it has to be recompiled, packed and uploaded if we would like to change the icon. This problem is easy to solve if we use css to customize the style of components. (In fact, css has also to be compiled into swf and if you want to change the color and again you have to compile swf. So adobe does not take users’ actual requirements into consideration. Later on I will introduce to you the way to use css to customize styles without compiling swf if time permitted.) However, the style of disclosureOpenIcon and disclosureClosedIcon can’t be customized by css, which you could see from the following sound code of mx.controls.Tree

[Style(name="disclosureOpenIcon", type="Class", format="EmbeddedFile", inherit="no")]
[Style(name="disclosureClosedIcon", type="Class", format="EmbeddedFile", inherit="no")]
 
This means that the style of disclosureOpenIcon and disclosureClosedIcon is class. Can css load class? The answer is no. You could try if you don’t believe it:

<mx:Style>
    Tree {
        disclosureOpenIcon: "assets/minus.png";
        disclosureClosedIcon: "assets/plus.png";
    }
</mx:Style>

You will see the following words if you run the program:

TypeError: Error #1034: Type Coercion failed: cannot convert “assets/plus.png” to Class.
at mx.controls::Tree/initListData()[C:\autobuild\3.5.0\frameworks\projects\framework\src\mx\controls\Tree.as:2663]

Since that we can define two classes. But what class do the two classes belong to? Let’s analyze them:
  1. Since the icon is an image, why don’t we let it inherit the component Image?
  2. Since it is of class, mx will renew this class on and on. So the image should be cached. Since it is of class, mx will renew this class on and on. So the image should be cached.
  3. Image-loading is nonsynchronous, so we have to register this class after the image is loaded.
Then we have got the definition of the two classes.

package {
    import flash.display.Bitmap;
    import flash.display.Loader;

    import mx.controls.Image;

    public class disclosureOpenIcon extends Image {
        public static const loader:Loader = new Loader();

        public function disclosureOpenIcon() {
            this.source = new Bitmap(Bitmap(loader.content).bitmapData);
            this.width = loader.content.width;
            this.height = loader.content.height;
        }
    }
}

package {
    import flash.display.Bitmap;
    import flash.display.Loader;

    import mx.controls.Image;

    public class disclosureClosedIcon extends Image {
        public static const loader:Loader = new Loader();

        public function disclosureClosedIcon() {
            this.source = new Bitmap(Bitmap(loader.content).bitmapData);
            this.width = loader.content.width;
            this.height = loader.content.height;
        }
    }
}

The following is the code to customize the expand and the collapse icon in the Tree control in TWaver.

private static function registTreeDisclosureIcon(tree:UIComponent):void {
    registClassStyle(tree, disclosureOpenIcon, "assets/minus.png");
    registClassStyle(tree, disclosureClosedIcon, "assets/plus.png");
}

private static function registClassStyle(component:UIComponent, clazz:Class, value:String):void {
    if(clazz["loader"].content == null){
        clazz["loader"].contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
            component.setStyle(getQualifiedClassName(clazz), clazz);
        });
        clazz["loader"].load(new URLRequest(value), new LoaderContext(true));
    }
}

If you would like the global registry, you could refer to the following code:

private static function registTreeDisclosureIcon():void {
    var css:CSSStyleDeclaration = StyleManager.getStyleDeclaration("Tree");
    registClassStyle(css, disclosureOpenIcon, "assets/minus.png");
    registClassStyle(css, disclosureClosedIcon, "assets/plus.png");
}

private static function registClassStyle(css:CSSStyleDeclaration, clazz:Class, value:String):void {
    if(clazz["loader"].content == null){
        clazz["loader"].contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
            css.setStyle(getQualifiedClassName(clazz), clazz);
        });
        clazz["loader"].load(new URLRequest(value), new LoaderContext(true));
    }
}

At last, let’s see how FastTree which has get rid of mx.controls.Tree customizes the expand and the collapse icons in the Tree control in TWaver. For example, the following code has reversed the two icons in the Tree control.

tree4.expandIcon = "collapsed_icon";
tree4.collapseIcon = "expanded_icon";

No comments:

Post a Comment