views:

63

answers:

2

Reward: Has been claimed.

Overview: The code giving me the problem is deployed here: http://www.johnuckele.com/MastersOfTime.html

The problem I am running into is that a certain sequence of actions (I don't fully understand how or why) is causing my calls to invalidateDisplayList to fail to produce a subsequent call to updateDisplayList. What I know is that during this period, some other visual effects will fail to occur (such as changing the width of a component or the addition of a new child).

Example: The program below draws two columns of horizontal lines. The column on the left is drawn during commitProperties, the column on the right is drawn during updateDisplayList. A certain sequence of actions can cause the right column to stop updating.

To trigger this bug: First add a new item. Now hit the start button and a bar starts filling up. If you press the add row button, the right column and the filling bar both stop growing. The left column continues unfettered. The extra component won't appear until the last line of the if statement in TEComputeRow.tick() doesn't execute for a frame. Click on the stop button to halt the execution of the block inside the if statement in TEComputeRow.tick() and everything goes back to normal.

Question: What is going on here?

I can force it to behave by using validate now but it doesn't cure the problem, it merely covers it up for a frame. It also seems like a really sloppy hack. Is there a nicer way to deal with the loss of updateDisplayList than using validateNow? Are there any ways to accurately identify the state of the world?

MastersOfTime.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="absolute"
    backgroundColor="white"
    backgroundGradientAlphas="[1,1]"
    initialize="init()"
    enterFrame="tick()"
    creationComplete="addComputeArray()">
    <mx:Script>
        <![CDATA[
            import mx.containers.HBox;
            import mx.controls.Button;
            import mx.containers.VBox;
            import flash.utils.getTimer;
            private var global:int = 0;

            private function addComputeArray():void
            {
                var addButton:Button = new Button;
                addButton.label = "Add Row Item";
                addButton.addEventListener(MouseEvent.CLICK, addComputeBox);
                box.addChild(addButton);
            }

            private function addComputeBox(a:* = null):void
            {
                box.addChild(new TEComputeRow());
            }

            private function init():void
            {
                box.clipContent = false;
                box.graphics.lineStyle(1);
            }

            private function tick():void
            {
                global++;
                this.invalidateDisplayList();
                this.invalidateProperties();
                //this.validateNow();
            }
            protected override function commitProperties():void
            {
                super.commitProperties();
                box.graphics.moveTo(100, (global*3)%800);
                box.graphics.lineTo(200, (global*3)%800);
            }
            protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
            {
                super.updateDisplayList(unscaledWidth, unscaledHeight);
                box.graphics.moveTo(200, (global*3)%800);
                box.graphics.lineTo(300, (global*3)%800);
            }
        ]]>
    </mx:Script>
    <mx:VBox id="box"/>
</mx:Application>

TEComputeRow.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
    height="60"
    width="352"
    verticalGap="0"
    borderStyle="solid"
    enterFrame="tick()">
    <mx:Script>
        <![CDATA[
            public var doStuff:Boolean = false;
            private var parameter:Number = 0;

            private function tick(e:Event = null):void
            {
                var value:*;
                if(doStuff)
                {
                    parameter = parameter+1;

                    value = parameter;

                    fill.width = value;
                }
            }
        ]]>
    </mx:Script>
    <mx:Button label="turn on" click="{doStuff = true;}" height="20"/>
    <mx:Container id="fill" x="7" width="0" height="20" backgroundColor="0x8888AA"/>
    <mx:Button label="turn off" click="{doStuff = false;}" height="20"/>
</mx:VBox>
+2  A: 

Well for starters, you are abusing the Flex life-cycle horribly, and doing things you are not meant to do... Changing the width of the fill in the tick of the row kicks off another invalidation cycle is the one that immediately jumps out. If you drove things via the Timer rather than on enterFrame, you'd immediately be better off.

My guess is that you spend so much time per frame re-invalidating the properties (changing the width will invalidate properties), the player never fits in the updateDisplayList.

Read up on both the elastic race track, and Deepa's presentation on the Flex 3 lifecycle.

Gregor Kiddie
I'm going to elaborate of Gregor's stellar answer by saying that this is the perfect read for you:http://www.developmentarc.com/site/wp-content/uploads/pdfs/understanding_the_flex_3_lifecycle_v1.0.pdfYou need to view updateDisplayList() and commitProperties() as functions that get called from the systemManager when needed/flagged. A great example (that I think is in the article) is, what happens when you 10 visible = true; 20 visible = false; 30 goto 10; 500 times in a frame (excuse the spaghetti psuedo-code)? You'll be much much better off after having read the articles in this answer.
jeremy.mooer
I'm currently going through the suggested reading and should get a chance to try it out this upcoming week.
John Uckele
Gregor, the answer you gave helped me find the correct answer. Feel free to send me an E-Mail at [email protected]. Please leave a comment here at the same time for confirmation.
John Uckele
Don't worry about the reward dude. I doubt you want to be shipping it to Ireland ;)
Gregor Kiddie
A: 

The use of .width is what is triggering this problem. If I replace .width with .setActualSize the problem stops. These pieces of code travel through separate paths and .width and .height apparently have the capacity to skip part of the frame cycle (the updateDisplayList part).

John Uckele