2012/07/26

To Realize the Vertical-text Layout in Twaver Flex

Recently, some clients have asked how to show the texts of the labels in Network in vertical direction. The first thing I think of is that we can insert a carriage return between every two characters which can be made by Network#labelFunction.

network.labelFunction = function (element:IElement):String {
    var name:String = element.name;
    if(element.getClient('vertical')) {
        var result:String = '';
        for(var i:int=0,n:int=name.length; i<n; i++) {
            result += name.charAt(i) + '\n';
        }
        result = result.substr(0, result.length-1);
        return result;
    } else {
        return name;
    }
};

Let’s test it with some codes:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:twaver="http://www.servasoftware.com/2009/twaver/flex"
                layout="absolute" width="100%" height="100%"
                creationComplete="init()" backgroundColor="#FFFFFF" >
    <mx:Script>
        <![CDATA[
            import twaver.Consts;
            import twaver.ElementBox;
            import twaver.IElement;
            import twaver.Node;
            import twaver.Styles;

            private var box:ElementBox = new ElementBox();

            private function init():void {
                network.labelFunction = function (element:IElement):String {
                    var name:String = element.name;
                    if(element.getClient('vertical')) {
                        var result:String = '';
                        for(var i:int=0,n:int=name.length; i<n; i++) {
                            result += name.charAt(i) + '\n';
                        }
                        result = result.substr(0, result.length-1);
                        return result;
                    } else {
                        return name;
                    }
                };

                var node1:Node = new Node();
                node1.location = new Point(100, 100);
                node1.setStyle(Styles.LABEL_POSITION, Consts.POSITION_LEFT_LEFT);
                node1.setClient('vertical', true);
                node1.name = '竖向文字Vertical Text';
                box.add(node1);

                network.elementBox = box;
            }
        ]]>
    </mx:Script>
    <twaver:Network id="network" width="100%" height="100%" />
</mx:Application>

You can see the effects:

The method mentioned above can easily show the texts vertically while the effect is not agreeable, because when Chinese characters and English words are mingled together the characters of English words are separated one by one. Is there a better solution? There must be, for we can easily show words from top to bottom by the means of Flash Text Engine (FTE) and Text Layout Framework (TLF) in Flex.
Let’s first see an example: set blockProgression of TextLayoutFormat as BlockProgression.RL.

package {
    import flash.display.Sprite;

    import flashx.textLayout.container.ContainerController;
    import flashx.textLayout.conversion.TextConverter;
    import flashx.textLayout.elements.TextFlow;
    import flashx.textLayout.formats.BlockProgression;
    import flashx.textLayout.formats.TextLayoutFormat;

    public class StaticHelloWorld extends Sprite {
        public function StaticHelloWorld() {
            var textLayoutFormat:TextLayoutFormat = new TextLayoutFormat();
            textLayoutFormat.lineHeight = 30;
            textLayoutFormat.locale = 'zh';
            textLayoutFormat.blockProgression = BlockProgression.RL;

            var text:String = "测试竖向文字,再看看English如何?";
            var textFlow:TextFlow = TextConverter.importToFlow(text, TextConverter.PLAIN_TEXT_FORMAT);
            textFlow.hostFormat = textLayoutFormat;
            textFlow.flowComposer.addController(new ContainerController(this, 25, 200));
            textFlow.flowComposer.updateAllControllers();
        }
    }
}

See the effects as follows:
Now the effects are better as the English characters are not separated one by one. Then let’s define an Attachment:

package {
    import flash.display.Sprite;
    import flash.text.engine.FontPosture;
    import flash.text.engine.FontWeight;

    import flashx.textLayout.container.ContainerController;
    import flashx.textLayout.elements.ParagraphElement;
    import flashx.textLayout.elements.SpanElement;
    import flashx.textLayout.elements.TextFlow;
    import flashx.textLayout.formats.BlockProgression;
    import flashx.textLayout.formats.TextDecoration;
    import flashx.textLayout.formats.TextLayoutFormat;

    import twaver.Styles;
    import twaver.network.ui.BasicAttachment;
    import twaver.network.ui.ElementUI;

    public class FTELabelAttachment extends BasicAttachment {

        private var textLayoutFormat:TextLayoutFormat = new TextLayoutFormat();

        public function FTELabelAttachment(elementUI:ElementUI, showInAttachmentCanvas:Boolean=false) {
            super(elementUI, showInAttachmentCanvas);

            this.textLayoutFormat.locale = 'zh';
            this.textLayoutFormat.blockProgression = BlockProgression.RL;
        }

        override public function updateProperties():void {
            super.updateProperties();

            this.textLayoutFormat.fontFamily = element.getStyle(Styles.LABEL_FONT);
            this.textLayoutFormat.color = element.getStyle(Styles.LABEL_COLOR);
            this.textLayoutFormat.fontSize = element.getStyle(Styles.LABEL_SIZE);
            this.textLayoutFormat.fontStyle = element.getStyle(Styles.LABEL_ITALIC) ? FontPosture.ITALIC : FontPosture.NORMAL;
            this.textLayoutFormat.fontWeight = element.getStyle(Styles.LABEL_BOLD) ? FontWeight.BOLD : FontWeight.NORMAL;
            this.textLayoutFormat.textDecoration = element.getStyle(Styles.LABEL_UNDERLINE ? TextDecoration.UNDERLINE : TextDecoration.NONE);

            var textFlow:TextFlow = new TextFlow();
            textFlow.hostFormat = this.textLayoutFormat;
            var p:ParagraphElement = new ParagraphElement();
            textFlow.addChild(p);
            var span:SpanElement = new SpanElement();
            span.text = network.getLabel(element);
            p.addChild(span);

            var fteLabel:Sprite = new Sprite();
            this.content = fteLabel;
            var containerController:ContainerController = new ContainerController(fteLabel, this.textLayoutFormat.fontSize, 1000);
            textFlow.flowComposer.addController(containerController);
            textFlow.flowComposer.updateAllControllers();
        }

        override public function get position():String {
            return element.getStyle(Styles.LABEL_POSITION);
        }

        override public function get xOffset():Number {
            return element.getStyle(Styles.LABEL_XOFFSET);
        }

        override public function get yOffset():Number {
            return element.getStyle(Styles.LABEL_YOFFSET);
        }

        override public function get padding():Number {
            return element.getStyle(Styles.LABEL_PADDING);
        }

        override public function get paddingLeft():Number {
            return element.getStyle(Styles.LABEL_PADDING_LEFT);
        }

        override public function get paddingRight():Number {
            return element.getStyle(Styles.LABEL_PADDING_RIGHT);
        }

        override public function get paddingTop():Number {
            return element.getStyle(Styles.LABEL_PADDING_TOP);
        }

        override public function get paddingBottom():Number {
            return element.getStyle(Styles.LABEL_PADDING_BOTTOM);
        }

        override public function get cornerRadius():Number {
            return element.getStyle(Styles.LABEL_CORNER_RADIUS);
        }

        override public function get pointerLength():Number {
            return element.getStyle(Styles.LABEL_POINTER_LENGTH);
        }

        override public function get pointerWidth():Number {
            return element.getStyle(Styles.LABEL_POINTER_WIDTH);
        }

        override public function get direction():String {
            return element.getStyle(Styles.LABEL_DIRECTION);
        }

        override public function get fill():Boolean {
            return element.getStyle(Styles.LABEL_FILL);
        }

        override public function get fillColor():Number {
            return element.getStyle(Styles.LABEL_FILL_COLOR);
        }

        override public function get fillAlpha():Number {
            return element.getStyle(Styles.LABEL_FILL_ALPHA);
        }

        override public function get gradient():String {
            return element.getStyle(Styles.LABEL_GRADIENT);
        }

        override public function get gradientColor():Number {
            return element.getStyle(Styles.LABEL_GRADIENT_COLOR);
        }

        override public function get gradientAlpha():Number {
            return element.getStyle(Styles.LABEL_GRADIENT_ALPHA);
        }

        override public function get contentXScale():Number {
            return element.getStyle(Styles.LABEL_CONTENT_XSCALE);
        }

        override public function get contentYScale():Number {
            return element.getStyle(Styles.LABEL_CONTENT_YSCALE);
        }

        override public function get outlineWidth():Number {
            return element.getStyle(Styles.LABEL_OUTLINE_WIDTH);
        }

        override public function get outlineColor():Number {
            return element.getStyle(Styles.LABEL_OUTLINE_COLOR);
        }

        override public function get outlineAlpha():Number {
            return element.getStyle(Styles.LABEL_OUTLINE_ALPHA);
        }
    }
}

Then, define Node and NodeUI and use this Attachment to replace LabelAttachment contained in TWaver.
Self-define Node:

package {
    import twaver.Node;

    public class FTELabelNode extends Node {
        public function FTELabelNode(id:Object=null) {
            super(id);
        }

        public override function get elementUIClass():Class {
            return FTELabelNodeUI;
        }
    }
}

Self-define NodeUI:

package {
    import twaver.Node;
    import twaver.network.Network;
    import twaver.network.ui.NodeUI;

    public class FTELabelNodeUI extends NodeUI {

        private var _labelAttachment:FTELabelAttachment = null;

        public function FTELabelNodeUI(network:Network, node:Node) {
            super(network, node);
        }

        override protected function checkLabelAttachment():void{
            var label:String = this.network.getLabel(element);
            if(label != null && label != ""){
                if(this._labelAttachment == null){
                    this._labelAttachment = new FTELabelAttachment(this, false);
                    this.addAttachment(this._labelAttachment);
                }
            }else{
                if(this._labelAttachment != null){
                    this.removeAttachment(this._labelAttachment);
                    this._labelAttachment = null;
                }
            }
        }
    }
}

Finally, let’s see the effect of another example:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:twaver="http://www.servasoftware.com/2009/twaver/flex"
                layout="absolute" width="100%" height="100%"
                creationComplete="init()" backgroundColor="#FFFFFF" >
    <mx:Script>
        <![CDATA[
            import twaver.Consts;
            import twaver.ElementBox;
            import twaver.IElement;
            import twaver.Node;
            import twaver.Styles;

            private var box:ElementBox = new ElementBox();

            private function init():void {
                network.labelFunction = function (element:IElement):String {
                    var name:String = element.name;
                    if(element.getClient('vertical')) {
                        var result:String = '';
                        for(var i:int=0,n:int=name.length; i<n; i++) {
                            result += name.charAt(i) + '\n';
                        }
                        result = result.substr(0, result.length-1);
                        return result;
                    } else {
                        return name;
                    }
                };

                var node1:Node = new Node();
                node1.location = new Point(100, 100);
                node1.setStyle(Styles.LABEL_POSITION, Consts.POSITION_LEFT_LEFT);
                node1.setClient('vertical', true);
                node1.name = '竖向文字Vertical Text';
                box.add(node1);

                var node2:Node = new FTELabelNode();
                node2.location = new Point(300, 100);
                node2.setStyle(Styles.LABEL_POSITION, Consts.POSITION_LEFT_LEFT);
                node2.name = '竖向文字Vertical Text';
                box.add(node2);

                network.elementBox = box;
            }

            private function changeFontSize():void {
                box.forEach(function (element:IElement):void {
                    element.setStyle(Styles.LABEL_SIZE, element.getStyle(Styles.LABEL_SIZE) + 2);
                });
            }
        ]]>
    </mx:Script>
    <mx:VBox width="100%" height="100%">
        <mx:HBox width="100%" height="20">
            <mx:Button label="Change Font Size" click="changeFontSize()"/>
        </mx:HBox>
        <twaver:Network id="network" width="100%" height="100%" />
    </mx:VBox>
</mx:Application>

Yeah. This is the very effect I want:
For more information on FTE and TLF, please refer to the official document of Adobe:
TextLayoutFormat
TextFlow
Textlayout

No comments:

Post a Comment