William
William

Reputation: 386

How do I insert a SpanElement in a TextFlow?

I want the ability to select some text in a textarea and then replace the selected text with a SpanElement. I cannot use a TextLayoutFormat because it doesn't have an "id" property.

How do I insert a span in a specific position in a textarea?

Upvotes: 1

Views: 2519

Answers (2)

Sami
Sami

Reputation: 31

I wanted to contribute some code, since I created mine from the one here and also because there is almost no examples or documentation about the "splitAtPosition" method.

This code will insert a FlowElement(flowElement) at any position(index) in a textflow(text flow) - remember to run textflow.flowComposer.updateAllControllers(); after running the function:

private function insertFlowElementInTextFlowAt(textflow:TextFlow,index:int,flowElement:FlowElement):void{

    var part2:TextFlow = textflow.splitAtPosition(index) as TextFlow;
    var part2_firstPara:ParagraphElement = part2.getChildAt(0) as ParagraphElement;     
    var textflow_lastPara:ParagraphElement = textflow.getChildAt(textflow.numChildren -1) as ParagraphElement;  



    if (textflow.textLength == index){

        part2_firstPara.addChildAt(0,flowElement);
        textflow.replaceChildren(textflow.numChildren,textflow.numChildren,getFlowElementChildren(part2,0))

    }else if(index < textflow.textLength){

        textflow_lastPara.addChild(flowElement);
        textflow_lastPara.replaceChildren( textflow_lastPara.numChildren, textflow_lastPara.numChildren, getFlowElementChildren(part2_firstPara));
        textflow.replaceChildren(textflow.numChildren,textflow.numChildren,getFlowElementChildren(part2,1))

    }



}

private function getFlowElementChildren(p:FlowGroupElement,start:int=0):Array
{
    var kids:Array = new Array();
    for (var i:int = start; i<p.numChildren; i++){
        kids.push( p.getChildAt(i) );
    }
    return kids;
}

Upvotes: 3

Sunil D.
Sunil D.

Reputation: 18193

There are several ways to go about doing this. Depending on the structure of your starting text, you can do a simple approach to substitute the text, or you might need a more generic approach that can handle many scenarios.

Two ideas, one simple, and one that approaches the generic solution. Would be curious to see other approaches, I'm always learning new things about TLF :)

Export the TLF text to a String, do your replace, reimport the String into a new TLF TextFlow.

  1. Get the TextFlow from the textFlow property of a Spark TextArea/TextInput
  2. Use TextFlow.getText(0, -1, "<br />") to get the String representation
  3. Do the substitution Re-import the flow: Have a look at TextFlowUtil.importFromString
  4. Apply the text flow from the above step back to the TextInput or TextArea's textFlow property.

This approach is probably not the most efficient. Note you are also likely to loose any styles applied to the text. Take a look at the TextConverter class, which you can also use to import text into TLF w/a text format. In particular, TextConvert.importToFlow().

Use the TextFlow API's to get at the FlowElement's that contain your text.

Remove the elements that contain the original text to be replaced, append the replacement text, append the ending text. More efficient than the above. Note, if you know the exact structure of your text, you could make this much simpler...

[Edit]

Here's an example that does both:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx"
               width="800" height="600" xmlns:local="*">

    <fx:Script>
        <![CDATA[
            import flashx.textLayout.edit.ISelectionManager;
            import flashx.textLayout.elements.FlowElement;
            import flashx.textLayout.elements.TextFlow;

            import spark.utils.TextFlowUtil;

            private var flow:TextFlow;
            private var mgr:ISelectionManager;
            private var startIndex:int;
            private var endIndex:int;

            private function init():void
            {
                flow = texty.textFlow;
                mgr = flow.interactionManager;
                if (mgr.hasSelection())
                {
                    // determine start/end indexes of the selection
                    if (mgr.anchorPosition < mgr.activePosition)
                    {
                        startIndex = mgr.anchorPosition;
                        endIndex = mgr.activePosition;
                    }
                    else
                    {
                        startIndex = mgr.activePosition;
                        endIndex = mgr.anchorPosition;
                    }
                }
                else
                {
                    startIndex = 0;
                    endIndex = 0;
                }
            }

            private function replaceByImportText():void
            {
                init();
                if (mgr.hasSelection())
                {
                    var copy:String = flow.getText(0,-1,"<br/>");
                    // replace the text
                    copy = copy.substring(0,startIndex) + "*** You just replaced me ***" + copy.substring(endIndex);
                    // reimport - expensive (makes a new TextFlow) and probably looses some fidelity (styles, etc)
                    texty.textFlow = TextFlowUtil.importFromString(copy);
                }
            }

            private function replaceBySplittingFlowElements():void
            {
                init();
                if (mgr.hasSelection())
                {
                    // each call to splitAtPosition leaves the text before startIndex in the original FlowElement
                    // and returns a new FlowElement (TextFlow's in this case) w/the remaining text
                    var textToBeReplaced:FlowElement = flow.splitAtPosition(startIndex);
                    // chops out whatever was between start/end indexes
                    var remainingTextToAddBack:TextFlow = textToBeReplaced.splitAtPosition(endIndex - startIndex) as TextFlow;
                    // insert replacment text
                    var span:SpanElement = new SpanElement();
                    span.text = "Hi, I'm a replacement String.";
                    // assumes last element is a paragraph but it could be a div :)
                    var lastParagraph:ParagraphElement = flow.getChildAt(flow.numChildren -1) as ParagraphElement;
                    if (lastParagraph)
                    {
                        lastParagraph.addChild(span);
                        // merge the last paragraph w/the 1st paragraph of remaining text
                        var firstP:ParagraphElement = remainingTextToAddBack.getChildAt(0) as ParagraphElement;
                        if (firstP)
                        {
                            lastParagraph.replaceChildren( lastParagraph.numChildren, lastParagraph.numChildren, getParagraphChildren(firstP) );
                        }

                    }

                }
            }

            private function getParagraphChildren(p:ParagraphElement):Array
            {
                var kids:Array =[];
                var numKids:int = p.numChildren;
                for (var i:int = 0; i<numKids; i++)
                {
                    kids.push( p.getChildAt(i) );
                }
                return kids;
            }

        ]]>
    </fx:Script>

    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>

    <s:HGroup>
        <s:Button label="Replace by importing text" click="replaceByImportText()" />
        <s:Button label="Replace by using FlowElements" click="replaceBySplittingFlowElements()"/>
    </s:HGroup>


    <s:TextArea id="texty" top="40" left="40" right="40" bottom="40">
        <s:p>
            <s:span>
                <s:text>
                    Bacon ipsum dolor sit amet turducken pancetta short ribs tail anim pig.
                    In pastrami in, ball tip flank shankle beef ribs spare ribs deserunt pancetta esse cupidatat aliquip venison pork chop.
                    Pork loin commodo corned beef ullamco culpa dolore occaecat, capicola adipisicing ribeye bresaola sunt est. 
                    Commodo labore culpa ut, sausage ad meatloaf adipisicing.
                </s:text>
            </s:span>
            <s:br/>
        </s:p>

        <s:p>
            <s:span>
                <s:text>
                    Quis qui aliqua enim, rump jerky aute bresaola aliquip pig speck short loin.
                    Non ea eiusmod shoulder, consequat enim ribeye sed. Meatloaf tenderloin pork loin reprehenderit.
                    Enim rump eiusmod, tri-tip capicola in do frankfurter dolore.
                    Culpa elit meatball pariatur turducken, leberkas excepteur irure in pork belly shank consequat.
                    Sint biltong t-bone veniam shankle. Consectetur irure et minim.
                </s:text>
            </s:span>
            <s:br/>
        </s:p>

        <s:p>
            <s:span>
                <s:text>
                    Excepteur laborum non corned beef, est dolore pastrami jowl id.
                    Leberkas short ribs ham, tempor id sed esse. Officia eiusmod pork frankfurter leberkas cow, nisi qui filet mignon mollit swine bacon veniam.
                    Nostrud deserunt nulla ground round, shankle sausage aliqua ut frankfurter culpa.
                    Veniam hamburger spare ribs, ullamco dolor labore salami capicola short loin swine.
                </s:text>
            </s:span>
        </s:p>

    </s:TextArea>
</s:Application>

Upvotes: 4

Related Questions