Problems with invalidate and callLater in AS3 - solved

When extending UIComponent I’ve come across the problem where invalidating with callLater=true does not schedule a draw operation on the next frame. Debugging this it became apparent that it is not possible to schedule a callLater from a method that is itself fired off by the callLaterDispatcher, since callLater checks to see if the variable inCallLaterPhase is true (which it is while callLater methods are being executed) and if so does not add the method.

To overcome this I’ve overridden callLater and replaced callLaterDispatcher:


        override protected function callLater(fn:Function):void {
            callLaterMethods[fn] = true;
            if (stage != null) {
                stage.addEventListener(Event.RENDER,callLaterDispatcher,false,0,true);
                stage.invalidate();                
            } else {
                addEventListener(Event.ADDED_TO_STAGE,callLaterDispatcher,false,0,true);
            }
        }
        
        private function callLaterDispatcher(event:Event):void {
            if (event.type == Event.ADDED_TO_STAGE) {
                removeEventListener(Event.ADDED_TO_STAGE,callLaterDispatcher);
                // now we can listen for render event:
                stage.addEventListener(Event.RENDER,callLaterDispatcher,false,0,true);
                stage.invalidate();
                
                return;
            } else {
                event.target.removeEventListener(Event.RENDER,callLaterDispatcher);
                if (stage == null) {
                    // received render, but the stage is not available, so we will listen for addedToStage again:
                    addEventListener(Event.ADDED_TO_STAGE,callLaterDispatcher,false,0,true);
                    return;
                }
            }
            var methods:Dictionary = new Dictionary();
            for (var copyMethod:Object in callLaterMethods) {
                methods[copyMethod] = callLaterMethods[copyMethod];
                delete(callLaterMethods[copyMethod]);
            }
            for (var method:Object in methods) {
                method();
            }
        }