| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package spark.components |
| { |
| import flash.events.Event; |
| import mx.core.IUIComponent; |
| import mx.core.IVisualElement; |
| import mx.events.FlexEvent; |
| import mx.states.State; |
| import spark.components.supportClasses.SkinnableComponent; |
| |
| [SkinState("rotatingState")] |
| [SkinState("notRotatingState")] |
| |
| public class MobileBusyIndicator extends SkinnableComponent |
| { |
| private var effectiveVisibility:Boolean = false; |
| private var effectiveVisibilityChanged:Boolean = true; |
| |
| public function MobileBusyIndicator() |
| { |
| super(); |
| // Listen to added to stage and removed from stage. |
| // Start rotating when we are on the stage and stop |
| // when we are removed from the stage. |
| addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler); |
| addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler); |
| states = [ |
| new State({name:"notRotatingState"}), |
| new State({name:"rotatingState"}) |
| ]; |
| } |
| |
| override protected function getCurrentSkinState():String |
| { |
| return currentState; |
| } |
| |
| private function addedToStageHandler(event:Event):void |
| { |
| // Check our visibility here since we haven't added |
| // visibility listeners yet. |
| computeEffectiveVisibility(); |
| |
| if (canRotate()) |
| currentState = "rotatingState"; |
| |
| addVisibilityListeners(); |
| invalidateSkinState(); |
| } |
| |
| private function removedFromStageHandler(event:Event):void |
| { |
| currentState = "notRotatingState"; |
| |
| removeVisibilityListeners(); |
| invalidateSkinState(); |
| } |
| |
| private function computeEffectiveVisibility():void |
| { |
| |
| // Check our design layer first. |
| if (designLayer && !designLayer.effectiveVisibility) |
| { |
| effectiveVisibility = false; |
| return; |
| } |
| |
| // Start out with true visibility and enablement |
| // then loop up parent-chain to see if any of them are false. |
| effectiveVisibility = true; |
| var current:IVisualElement = this; |
| |
| while (current) |
| { |
| if (!current.visible) |
| { |
| if (!(current is IUIComponent) || !IUIComponent(current).isPopUp) |
| { |
| // Treat all pop ups as if they were visible. This is to |
| // fix a bug where the BusyIndicator does not spin when it |
| // is inside modal popup. The problem is in we do not get |
| // an event when the modal window is made visible in |
| // PopUpManagerImpl.fadeInEffectEndHandler(). When the modal |
| // window is made visible, setVisible() is passed "true" so |
| // as to not send an event. When do get events when the |
| // non-modal windows are popped up. Only modal windows are |
| // a problem. |
| // The downside of this fix is BusyIndicator components that are |
| // inside of hidden, non-modal, popup windows will paint themselves |
| // on a timer. |
| effectiveVisibility = false; |
| break; |
| } |
| } |
| |
| current = current.parent as IVisualElement; |
| } |
| } |
| |
| /** |
| * The BusyIndicator can be rotated if it is both on the display list and |
| * visible. |
| * |
| * @returns true if the BusyIndicator can be rotated, false otherwise. |
| */ |
| private function canRotate():Boolean |
| { |
| if (effectiveVisibility && stage != null) |
| return true; |
| |
| return false; |
| } |
| |
| |
| /** |
| * @private |
| * Add event listeners for SHOW and HIDE on all the ancestors up the parent chain. |
| * Adding weak event listeners just to be safe. |
| */ |
| private function addVisibilityListeners():void |
| { |
| var current:IVisualElement = this.parent as IVisualElement; |
| while (current) |
| { |
| // add visibility listeners to the parent |
| current.addEventListener(FlexEvent.HIDE, visibilityChangedHandler, false, 0, true); |
| current.addEventListener(FlexEvent.SHOW, visibilityChangedHandler, false, 0, true); |
| |
| current = current.parent as IVisualElement; |
| } |
| } |
| |
| /** |
| * @private |
| * Remove event listeners for SHOW and HIDE on all the ancestors up the parent chain. |
| */ |
| private function removeVisibilityListeners():void |
| { |
| var current:IVisualElement = this; |
| while (current) |
| { |
| current.removeEventListener(FlexEvent.HIDE, visibilityChangedHandler, false); |
| current.removeEventListener(FlexEvent.SHOW, visibilityChangedHandler, false); |
| |
| current = current.parent as IVisualElement; |
| } |
| } |
| |
| /** |
| * @private |
| * Event call back whenever the visibility of us or one of our ancestors |
| * changes |
| */ |
| private function visibilityChangedHandler(event:FlexEvent):void |
| { |
| effectiveVisibilityChanged = true; |
| invalidateProperties(); |
| } |
| |
| |
| } |
| } |