Skip to main content
Inspiring
May 7, 2011
Question

Deleting from a textFlow with out a full recomposition

  • May 7, 2011
  • 1 reply
  • 2900 views

I have a textflow in which new paragraphs are added to the bottom, sometimes at great speed. 

To keep the textFlow from becoming infinitely large I delete paragraphs from the top.

The visible lines of the TF in the container are at the bottom of the textFlow - where the new paragraphs are being added.

Each time a paragraphElement is deleted from the top - the textFlow recomposes entirely, which if I have set the maximum length to 1000 paragraphs for example - can take a long time.

I delete the top pargrapElement using textFlow.removeChildAt().

Because a paragraph implies a line break, there is actuall no need to recompose the entire textFlow - just the y position of the textFlowLines would need to be adjusted - because the width has not changed.

Sorry for the long exposition - here is the question:

Is there a way to remove a paragraphElement from the top of a textFlow - without forcing a recomposition of the entire textFlow up to the point which is visible in a container?

I imagine there could be a function like removeBlockElement() - it would only allow the deletion of ParagraphElements and DivElements - not SpanElements or InlineGraphicElements - because thoose would need to force a recomposition - given that they may not take up the full width or imply a lineBreak.

If I wanted to add this functionality - could someone suggest how I might implement it?

Thanks,

Josh

This topic has been closed for replies.

1 reply

Adobe Employee
May 9, 2011

Hi josh,

TLF does not recompose the whole textflow.

If there is only one visible line, when you scroll down, only the one visible line and few lines around it will be recomposed, which I think is for cacheing.

I debugged an example to prove what I said.

package {
    import flash.display.Sprite;
   
    import flashx.textLayout.container.ContainerController;
    import flashx.textLayout.elements.ParagraphElement;
    import flashx.textLayout.elements.SpanElement;
    import flashx.textLayout.elements.TextFlow;
    import flashx.textLayout.formats.TextLayoutFormat;

    /** Simplest possible "Hello, World" text example */
    public class as_helloWorld extends Sprite
    {
        public function as_helloWorld()
        {
            var textFlow:TextFlow = new TextFlow();
            var p:ParagraphElement = new ParagraphElement();
            var span:SpanElement = new SpanElement();
            span.text ="start";
            p.addChild(span);   
            var p1:ParagraphElement = new ParagraphElement();
            var span1:SpanElement = new SpanElement();
            span1.text = "Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,Hello, WorldHello,";
            p1.addChild(span1);
           
            textFlow.addChild(p);
            textFlow.addChild(p1);
           
            var cc:ContainerController = new ContainerController(this,100,12);

            textFlow.flowComposer.addController(cc);
            textFlow.flowComposer.updateAllControllers();
           
            var p2:ParagraphElement = new ParagraphElement();
            var span2:SpanElement = new SpanElement();
            span2.text = "I have a try";
            p2.addChild(span2);
            textFlow.addChild(p2);
            //cc.scrollToRange(266,266);/*266 is a character index in the end line. If you scroll to the end directly, the whole process will be acted, which means scroll line by line but you only see the final result. So you need set the index to scroll only one line, you can see the process clearly.*/
            textFlow.flowComposer.updateAllControllers();
        }
    }
}

please toggle your break points at (Our line number may not be the same. So I use this way to locate the break points)

the 1st line of composeParagraphElement() in BaseCompose.as (focus on variable *absStart*)

the 7th line of composeNextLine() in ComposeState.as (focus on variable *startCompose*)

Hope it can ease your worry~

josh_onAuthor
Inspiring
May 9, 2011

I don't see anything getting deleted in the example that you gave.

I am talking about a deletion at the top of a textFlow when the bottom of the textFlow is visible - this seems to force a recomposition.

If you delete something that breaks cleanly on a line (when there is just a single container - or if all the containers have the same width) from the top of the flow there is no reason (that I can see) to recalculate all the line breaks etc - just the y positions of the content would need to be adjusted.

I can understand that if you deleted something from the beginning of a textFlow - when you are displaying the bottom and have mulitple width containers, or the content that is being deleted ends half way through a line - then everything would need to be recalculated down to the point that is being displayed. 

I would like to have a long textFlow (say 700 paragraphs) that I can delete blocks (that break discretely at a line - e.g. ParagraphElements, or DivElements) from the top, while displaying the bottom of the flow - without the entire flow recomposing.

josh_onAuthor
Inspiring
May 9, 2011

Here is an example:

http://theyrule.net/test/delete_from_top/SingleContainerTest.swf

Click the red rectangle at the bottom to start.

It adds a new divElement to the bottom of the textFlow every frame.

The bottom of the flow is always displayed.

When it gets 500 divElements added - it starts deleting one divElement from the top every frame.

This causes the performance to drop dramatically - I assume it is recomposing.

I would like to be able to do this without losing performance - I can do it using multiple textFlows - one for each div - but that results in having to mantain a lot of code in order to select across textFlows.

This is the source:

------------------------------

package

{

import flash.display.Sprite;

import flash.events.Event;

import flash.events.MouseEvent;

import flash.system.System;

import flash.text.TextField;

import flash.text.TextFormat;

import flashx.textLayout.container.ContainerController;

import flashx.textLayout.container.ScrollPolicy;

import flashx.textLayout.edit.EditManager;

import flashx.textLayout.elements.DivElement;

import flashx.textLayout.elements.ParagraphElement;

import flashx.textLayout.elements.SpanElement;

import flashx.textLayout.elements.TextFlow;

import flashx.textLayout.formats.TextLayoutFormat;

import flashx.textLayout.formats.VerticalAlign;

import flashx.undo.UndoManager;

[SWF (width="500", height="700", backgroundColor="#FFFFFF")]

public class SingleContainerTest extends Sprite

{

protected var tf:TextFlow;

protected var em:EditManager;

protected var um:flashx.undo.UndoManager

protected var _bg:Sprite;

protected var _spr:Sprite;

protected var _cc:ContainerController

protected var _init_fmt:TextLayoutFormat;

protected var _btn:Sprite;

protected var _playing:Boolean = false;

protected var _count:int = 0;

protected var _graph:Sprite;

protected var _print_out:TextField;

protected var _last_time:Date = new Date();

protected var _last_five:Array = [];

public function SingleContainerTest()

{

var cw:Number = 200; // the container width

var ch:Number = 600;  // the container height

_bg = new Sprite();

_bg.graphics.lineStyle(.25, 0);

_bg.graphics.drawRect(0,0,cw,ch);

addChild(_bg);

_spr = new Sprite();

addChild(_spr);

_graph = new Sprite();

_graph.x = cw + 10;

_graph.y = 250;

addChild(_graph);

_print_out = new TextField();

var fmt:TextFormat = _print_out.defaultTextFormat;

fmt.font = "_sans";

_print_out.wordWrap = true;

_print_out.multiline = true;

_print_out.width = stage.stageWidth - (10 + _graph.x);

_print_out.x = _graph.x;

_print_out.y = _graph.y + 10;

addChild(_print_out);

//define TextFlow and manager objects

tf = new TextFlow();

um = new UndoManager();

em = new EditManager(um);

tf.interactionManager = em;  

//compose TextFlow to display

_cc = new ContainerController(_spr,cw,ch);

//_cc.verticalAlign = VerticalAlign.BOTTOM;

//_cc.verticalScrollPolicy = ScrollPolicy.ON;

tf.flowComposer.addController(_cc);

tf.flowComposer.updateAllControllers();

//make a button to add Inline Graphic elements

_btn = new Sprite();

_btn.graphics.beginFill(0xFF0000,1);

_btn.graphics.drawRect(0,0,120,30);

addChild(_btn);

_btn.addEventListener(MouseEvent.CLICK, btnClicked);

_btn.y = 600;

addMessage("hjfhsdg");

}

public function addMessage(msg:String):void {

//define elements to contain text

var d:DivElement = new DivElement();

var p:ParagraphElement = new ParagraphElement();

var s:SpanElement = new SpanElement();

s.text = msg;

//add these elements to the TextFlow

p.addChild(s);

d.addChild(p);

tf.addChild(d);

tf.flowComposer.updateAllControllers();

_cc.verticalScrollPosition = _cc.getContentBounds().height;

tf.flowComposer.updateAllControllers();

}

protected function btnClicked(e:MouseEvent):void {

_playing = !_playing;

removeEventListener(Event.ENTER_FRAME, onEnterFrame);

if(_playing){

addEventListener(Event.ENTER_FRAME, onEnterFrame);

}

}

protected function onEnterFrame(e:Event):void {

_count++;

if(_count > 500){

tf.removeChildAt(0);

}

addMessage("Message Number: " + _count + " " + randomString());

printOut()

}

protected function printOut():void {

var now:Date = new Date();

var tm:Number = (now.getTime() - _last_time.getTime());

_last_five.push(tm);

if(_last_five.length > 10) _last_five.shift();

var avg_tm:Number = 0;

for(var i:int = 0; i < _last_five.length; i++) avg_tm += _last_five;

avg_tm = Math.round(avg_tm/_last_five.length);

var elapsed_str:String = "message: \t\t\t"+_count

+ "\ntime: \t\t\t\t" + tm + "ms"

+ "\navg of last 10:\t\t" + avg_tm +"ms";

//trace(elapsed_str );

_print_out.text = elapsed_str;

_last_time = now;

drawGraph(tm);

}

protected function drawGraph(tm:Number):void {

if(_count % 5 == 0){

_graph.graphics.beginFill(0x0);

_graph.graphics.drawRect(_count/10,-Math.round(tm/10),1,1);

_graph.graphics.beginFill(0xFF0000);

_graph.graphics.drawRect(_count/10,-Math.round(System.totalMemory/1000000),1,1);

}

}

protected function randomString():String {

var chars:String = "abcdefghijklmnopqrstuvwzyz                    ";

var chars_len:Number = chars.length;

var random_str:String = "";

var num_chars:Number = Math.round(Math.random() * 100);

for (var i:int =0; i < num_chars; i++){

random_str = random_str + chars.charAt(Math.round(Math.random() * chars_len));

}

return random_str;

}

}

}