blob: bbba22c655a69fa29d9f5e788f1c3bba223c8c69 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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 flash.events.EventDispatcher;
import flash.net.registerClassAlias;
import mx.core.IVisualElement;
import mx.core.UIComponent;
import mx.core.mx_internal;
import mx.effects.IEffect;
import mx.effects.Parallel;
import mx.events.EffectEvent;
import mx.events.FlexEvent;
import mx.events.PropertyChangeEvent;
import mx.managers.LayoutManager;
import spark.components.supportClasses.NavigationStack;
import spark.components.supportClasses.ViewDescriptor;
import spark.components.supportClasses.ViewNavigatorAction;
import spark.components.supportClasses.ViewNavigatorBase;
import spark.components.supportClasses.ViewReturnObject;
import spark.core.ContainerDestructionPolicy;
import spark.effects.Animate;
import spark.effects.animation.Animation;
import spark.effects.animation.MotionPath;
import spark.effects.animation.SimpleMotionPath;
import spark.events.ViewNavigatorEvent;
import spark.layouts.supportClasses.LayoutBase;
import spark.transitions.SlideViewTransition;
import spark.transitions.ViewTransitionBase;
import spark.transitions.ViewTransitionDirection;
use namespace mx_internal;
[DefaultProperty("navigationStack")]
//--------------------------------------
// SkinStates
//--------------------------------------
/**
* The state used when the navigator is in portrait orientation.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
[SkinState("portrait")]
/**
* The state used when the navigator is in landscape orientation.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
[SkinState("landscape")]
/**
* The state used when the navigator is in portrait orientation
* and the navigator controls are overlaid on top.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
[SkinState("portraitAndOverlay")]
/**
* The state used when the navigator is in landscape orientation
* and the navigator controls are overlaid on top.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
[SkinState("landscapeAndOverlay")]
//--------------------------------------
// Other metadata
//--------------------------------------
[IconFile("ViewNavigator.png")]
/**
* The ViewNavigator component is a container that consists of a collection of
* View objects, where only the top most view is visible and active.
* Use the ViewNavigator container to control the navigation among
* the views of a mobile application.
* The ViewNavigatorApplication container automatically creates a
* single ViewNavigator container for the entire application.
*
* <p>Navigation in a mobile application is controlled by a stack of View objects.
* The top View object on the stack defines the currently visible view.
* The ViewNavigator container maintains the stack.
* To change views, push a new View object onto the stack,
* or pop the current View object off of the stack.
* Popping the currently visible View object from the stack destroys
* the View object, and returns the user to the previous view on the stack.</p>
*
* <p>When a view is pushed on top of the stack, the old view's <code>data</code>
* property is automatically persisted.
* It is restored when the view is reactived as a result of
* the current view being popped off the stack.
* When a new view becomes active by being pushed onto the stack,
* the old view's instance is destroyed.</p>
*
* <p>The ViewNavigator displays an optional ActionBar control that displays contextual
* information defined by the active view.
* When the active view changes, the action bar is automatically updated.</p>
*
* @mxml
*
* <p>The <code>&lt;s:ViewNavigator&gt;</code> tag inherits all of the tag
* attributes of its superclass and adds the following tag attributes:</p>
*
* <pre>
* &lt;s:ViewNavigator
* <strong>Properties</strong>
* actionContent="null"
* actionLayout="null"
* defaultPopTransition="SlideViewTransition"
* defaultPushTransition="SlideViewTransition"
* firstView="null"
* firstViewData="null"
* navigationContent="null"
* navigationLayout="null"
* poppedViewReturnedObject="null"
* title=""
* titleContent="null"
* titleLayout="null"
*
* &gt;
* </pre>
*
* @see spark.components.View
* @see spark.components.ActionBar
* @see spark.components.TabbedViewNavigator
* @see spark.transitions.ViewTransitionBase
*
* @includeExample examples/ViewNavigatorExample.mxml -noswf
* @includeExample examples/ViewNavigatorExampleHomeView.mxml -noswf
* @includeExample examples/ViewNavigatorExampleSearch.mxml -noswf
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public class ViewNavigator extends ViewNavigatorBase
{
//--------------------------------------------------------------------------
//
// Class variables
//
//--------------------------------------------------------------------------
/**
* @private
* The animation duration used when hiding and showing the action bar.
*/
private static const ACTION_BAR_ANIMATION_DURATION:Number = 250;
/**
* @private
* The animation duration used when running a default view transition.
*/
private static const DEFAULT_VIEW_TRANSITION_DURATION:Number = 300;
/**
* @private
* Flag indicating whether the classes required for the PersistenceManager
* have been registered with the player.
*/
private static var classAliasesRegistered:Boolean = false;
/**
* @private
*/
private static var viewTransitionSuspendCount:int = 0;
private static var eventDispatcher:EventDispatcher;
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function ViewNavigator()
{
super();
if (!classAliasesRegistered)
{
// Register aliases for custom classes that will be written to
// persistence store by navigator
registerClassAlias("ViewDescriptor", ViewDescriptor);
registerClassAlias("NavigationStack", NavigationStack);
classAliasesRegistered = true;
}
// Default view transitions
var slideLeft:SlideViewTransition = new SlideViewTransition();
slideLeft.duration = DEFAULT_VIEW_TRANSITION_DURATION;
slideLeft.direction = ViewTransitionDirection.LEFT;
defaultPushTransition = slideLeft;
var slideRight:SlideViewTransition = new SlideViewTransition();
slideRight.duration = DEFAULT_VIEW_TRANSITION_DURATION;
slideRight.direction = ViewTransitionDirection.RIGHT;
defaultPopTransition = slideRight;
}
//--------------------------------------------------------------------------
//
// Skin Parts
//
//--------------------------------------------------------------------------
//----------------------------------------
// Navigator Controls
//----------------------------------------
[SkinPart(required="false")]
/**
* A skin part that defines the action bar of the navigator.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public var actionBar:ActionBar;
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var actionBarProps:Object;
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function get actionBarPropertyInvalidated():Boolean
{
return actionContentInvalidated ||
actionLayoutInvalidated ||
navigationContentInvalidated ||
navigationLayoutInvalidated ||
titleInvalidated ||
titleContentInvalidated ||
titleLayoutInvalidated ||
overlayControlsInvalidated;
}
/**
* @private
* The show/hide effect that is currently being played on the action bar.
*/
private var actionBarVisibilityEffect:IEffect;
/**
* @private
*/
private var contentGroupProps:Object;
/**
* @private
* Internal flag used to track whether a show/hide effect should be
* played when the action bar visibility is updated.
*/
private var animateActionBarVisbility:Boolean = false;
/**
* @private
* Flag indicating the that actionBar visiblity has been invalidated
* by the active view.
*/
private var actionBarVisibilityInvalidated:Boolean = false;
/**
* @private
* Flag indicates that the backKey handler has run and the navigator
* is waiting a validation pass.
*/
private var backKeyWasPressed:Boolean = false;
/**
* @private
* The view data for the active view.
*/
private var currentViewDescriptor:ViewDescriptor = null;
/**
* @private
*/
private var delayedNavigationActions:Vector.<Object> = new Vector.<Object>();
/**
* @private
* Flag indicates that the navigator has been requested to show
* a different view.
*/
mx_internal var viewChangeRequested:Boolean = false;
/**
* @private
*/
private var emptyViewDescriptor:ViewDescriptor = null;
/**
* @private
* Variable used to count how many enterframes the navigator has
* received after preparing a transition.
*/
private var enterFrameCount:int = 0;
/**
* @private
* This following property stores the <code>mouseEnabled</code>
* value defined on the navigator so that it can be
* restored after a view transition.
*/
private var explicitMouseEnabled:Boolean;
/**
* @private
* This following property stores the <code>mouseChildren</code>
* value defined on the navigator so that it can be
* restored after a view transition.
*/
private var explicitMouseChildren:Boolean;
/**
* @private
*/
private var overlayControlsInvalidated:Boolean = false;
/**
* @private
* The view data for the pending view.
*/
private var pendingViewDescriptor:ViewDescriptor = null;
/**
* @private
* The transition to play when the pending view is activated.
*/
private var pendingViewTransition:ViewTransitionBase = null;
/**
* @private
* A variable used to store the transition to play after a
* validation pass. This needs to be a different variable than
* pendingViewTransition because the pending transition can
* change as push and pops come in.
*/
mx_internal var activeTransition:ViewTransitionBase;
/**
* @private
*/
private var showingActionBar:Boolean;
/**
* @private
* Flag indicates whether the navigator is in the process of
* changing a view.
*/
// mx_internal so that TabbedViewNavigator can access
mx_internal var viewChanging:Boolean = false;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// active
//----------------------------------
/**
* @private
* Only gets called by TabbedViewNavigator.
*/
override mx_internal function setActive(value:Boolean, clearNavigationStack:Boolean = false):void
{
if (value == isActive)
return;
if (clearNavigationStack)
navigationStack.popToFirstView();
if (value)
{
createTopView();
// If the view is already initialized, that means it was cached. We can complete the
// activation process if the view hasn't been invalidated in any way.
if (activeView)
{
if (activeView.initialized &&
!activeView.invalidatePropertiesFlag &&
!activeView.invalidateSizeFlag &&
!activeView.invalidateDisplayListFlag)
{
completeViewCommitProcess();
}
else
{
// Wait until the view validates before activating it
activeView.addEventListener(FlexEvent.UPDATE_COMPLETE, view_updateCompleteHandler);
}
}
// Need to force a validation on the actionBar
invalidateActionBarProperties();
}
else
{
if (activeView)
{
var canDestroy:Boolean = (activeView.destructionPolicy != ContainerDestructionPolicy.NEVER);
// If the instance of the view is being destroyed but our navigationStack is
// maintained, the active view needs to serialize its data is application
// persistence is enabled.
if (canDestroy || clearNavigationStack)
destroyViewInstance(navigationStack.topView, !clearNavigationStack);
else
deactiveView(activeView);
}
}
// Call super after the above code so that the view has a chance
// to be created before its active property is set.
super.setActive(value, clearNavigationStack);
}
//----------------------------------
// activeView
//----------------------------------
[Bindable("viewChangeComplete")]
/**
* <p>During a view transition, this property references the
* view that the navigator is transitioning to.</p>
*
* @inheritDoc
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
override public function get activeView():View
{
if (pendingViewDescriptor)
return pendingViewDescriptor.instance;
if (currentViewDescriptor && currentViewDescriptor != emptyViewDescriptor)
return currentViewDescriptor.instance;
return null;
}
//----------------------------------
// exitApplicationOnBackKey
//----------------------------------
/**
* @private
* This method is used to determine whether the application can
* return to the home screen on Android when the back key is
* pressed. An application can return to the home screen if the
* length of the navigator is 1 or less.
*/
override mx_internal function get exitApplicationOnBackKey():Boolean
{
// If a back key is already being processed, we know that this
// method is being called as a result of a duplicate back key press
// during the same validation pass. So don't return to the home screen
// and let the navigator process the navigation action during the
// next validation.
return !backKeyWasPressed && length <= 1;
}
//----------------------------------
// context
//----------------------------------
/**
* The string that describes the context in which the current view was
* created.
* This property is assigned to the value of the <code>context</code>
* parameter passed to the <code>ViewNavigator.pushView()</code> method.
*
* @default null
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get context():Object
{
if (pendingViewDescriptor)
return pendingViewDescriptor.context;
else if (currentViewDescriptor)
return currentViewDescriptor.context;
return null;
}
//---------------------------------
// defaultPushTransition
//---------------------------------
private var _defaultPushTransition:ViewTransitionBase;
/**
* Specifies the default view transition for push navigation operations.
*
* @default SlideViewTransition
*
* @see spark.transitions.SlideViewTransition
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get defaultPushTransition():ViewTransitionBase
{
return _defaultPushTransition;
}
/**
* @private
*/
public function set defaultPushTransition(value:ViewTransitionBase):void
{
_defaultPushTransition = value;
}
//---------------------------------
// defaultPopTransition
//---------------------------------
private var _defaultPopTransition:ViewTransitionBase;
/**
* Specifies the default view transition for pop navigation operations.
*
* @default SlideViewTransition
*
* @see spark.transitions.SlideViewTransition
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get defaultPopTransition():ViewTransitionBase
{
return _defaultPopTransition;
}
/**
* @private
*/
public function set defaultPopTransition(value:ViewTransitionBase):void
{
_defaultPopTransition = value;
}
//----------------------------------
// firstView
//----------------------------------
private var _firstView:Class;
/**
* Each view in an application corresponds to a View container
* class defined in an ActionScript or MXML file.
* This property specifies the view to use to initialize the first view
* of the stack.
* This property must reference a class that extends View container.
*
* <p>Specify any data passed to the first view by using
* the <code>firstViewData</code> property.</p>
*
* @default null
*
* @see #firstViewData
* @see View
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get firstView():Class
{
return _firstView;
}
/**
* @private
*/
public function set firstView(value:Class):void
{
_firstView = value;
}
//----------------------------------
// firstViewData
//----------------------------------
private var _firstViewData:Object;
/**
* The Object to pass to the <code>data</code> property
* of the first view when the navigator is initialized.
* Specify the first view by using the <code>firstView</code> property.
*
* @default null
*
* @see #firstView
* @see View
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get firstViewData():Object
{
return _firstViewData;
}
/**
* @private
*/
public function set firstViewData(value:Object):void
{
_firstViewData = value;
}
//----------------------------------
// length
//----------------------------------
[Bindable("lengthChanged")]
/**
* Returns the number of views being managed by the navigator.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get length():int
{
return navigationStack.length;
}
//----------------------------------
// navigationStack
//----------------------------------
/**
* @private
*/
override mx_internal function set navigationStack(value:NavigationStack):void
{
super.navigationStack = value;
viewChangeRequested = true;
invalidateProperties();
}
//----------------------------------
// poppedViewReturnedObject
//----------------------------------
private var _poppedViewReturnedObject:ViewReturnObject = null;
/**
* Holds the object returned by the last view that was popped
* off the navigation stack or replaced by another view.
* To return a value, the view being popped off the stack overrides
* its <code>createReturnObject()</code> method.
*
* <p>This object is only available when the navigator is in the process of switching
* views in response to a pop or replace navigation operation.
* This object is guaranteed to be valid when the new view receives
* the <code>add</code> event, and is destroyed after
* the view receives a <code>viewActivate</code> event.</p>
*
* @default null
*
* @see View#createReturnObject()
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get poppedViewReturnedObject():ViewReturnObject
{
return _poppedViewReturnedObject;
}
//--------------------------------------------------------------------------
//
// UI Template Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// actionContent
//----------------------------------
private var _actionContent:Array;
private var actionContentInvalidated:Boolean = false;
[ArrayElementType("mx.core.IVisualElement")]
/**
* This property overrides the <code>actionContent</code>
* property in the ActionBar and
* ViewNavigatorApplication components.
*
* @copy ActionBar#actionContent
*
* @default null
*
* @see ActionBar#actionContent
* @see spark.skins.mobile.ActionBarSkin
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get actionContent():Array
{
return _actionContent;
}
/**
* @private
*/
public function set actionContent(value:Array):void
{
_actionContent = value;
if (!activeView || (activeView && !activeView.actionContent))
{
actionContentInvalidated = true;
invalidateProperties();
}
}
//----------------------------------
// actionLayout
//----------------------------------
private var _actionLayout:LayoutBase;
private var actionLayoutInvalidated:Boolean = false;
/**
* @copy ActionBar#actionContent
*
* @default null
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get actionLayout():LayoutBase
{
return _actionLayout;
}
/**
* @private
*/
public function set actionLayout(value:LayoutBase):void
{
_actionLayout = value;
if (!activeView || (activeView && !activeView.actionLayout))
{
actionLayoutInvalidated = true;
invalidateProperties();
}
}
//----------------------------------
// navigationContent
//----------------------------------
private var _navigationContent:Array;
private var navigationContentInvalidated:Boolean = false;
[ArrayElementType("mx.core.IVisualElement")]
/**
* This property overrides the <code>navigationContent</code>
* property in the ActionBar and
* ViewNavigatorApplication components.
*
* @copy ActionBar#navigationContent
*
* @default null
*
* @see ActionBar#navigationContent
* @see spark.skins.mobile.ActionBarSkin
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get navigationContent():Array
{
return _navigationContent;
}
/**
* @private
*/
public function set navigationContent(value:Array):void
{
_navigationContent = value;
if (!activeView || (activeView && !activeView.navigationContent))
{
navigationContentInvalidated = true;
invalidateProperties();
}
}
//----------------------------------
// navigationLayout
//----------------------------------
private var _navigationLayout:LayoutBase;
private var navigationLayoutInvalidated:Boolean = false;
/**
* @copy ActionBar#navigationLayout
*
* @default null
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get navigationLayout():LayoutBase
{
return _navigationLayout;
}
/**
* @private
*/
public function set navigationLayout(value:LayoutBase):void
{
_navigationLayout = value;
if (!activeView || (activeView && !activeView.navigationLayout))
{
navigationLayoutInvalidated = true;
invalidateProperties();
}
}
//----------------------------------
// title
//----------------------------------
private var _title:String;
private var titleInvalidated:Boolean = false;
[Bindable]
/**
* This property overrides the <code>title</code>
* property in the ActionBar and ViewNavigatorApplication components.
*
* @copy ActionBar#title
*
* @default ""
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get title():String
{
return _title;
}
/**
* @private
*/
public function set title(value:String):void
{
if (_title != value)
{
_title = value;
// title will only have an effect on the view if titleContent or title isn't
// set anywhere else
if (!activeView || (activeView && !activeView.title && !activeView.titleContent && !titleContent))
{
titleInvalidated = true;
invalidateProperties();
}
}
}
//----------------------------------
// titleContent
//----------------------------------
private var _titleContent:Array;
private var titleContentInvalidated:Boolean = false;
[ArrayElementType("mx.core.IVisualElement")]
/**
* This property overrides the <code>titleContent</code>
* property in the ActionBar and ViewNavigatorApplication components.
*
* @copy ActionBar#titleContent
*
* @default null
*
* @see ActionBar#titleContent
* @see spark.skins.mobile.ActionBarSkin
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get titleContent():Array
{
return _titleContent;
}
/**
* @private
*/
public function set titleContent(value:Array):void
{
_titleContent = value;
if (!activeView || (activeView && !activeView.titleContent))
{
titleContentInvalidated = true;
invalidateProperties();
}
}
//----------------------------------
// titleLayout
//----------------------------------
private var _titleLayout:LayoutBase;
private var titleLayoutInvalidated:Boolean = false;
/**
* @copy ActionBar#titleLayout
*
* @default null
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get titleLayout():LayoutBase
{
return _titleLayout;
}
/**
* @private
*/
public function set titleLayout(value:LayoutBase):void
{
_titleLayout = value;
if (!activeView || (activeView && !activeView.titleLayout))
{
titleLayoutInvalidated = true;
invalidateProperties();
}
}
//--------------------------------------------------------------------------
//
// Public Methods
//
//--------------------------------------------------------------------------
/**
* Removes all of the views from the navigator stack.
* This method changes the display to a blank screen.
*
* @param transition The view transition to play while switching views.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function popAll(transition:ViewTransitionBase = null):void
{
if (navigationStack.length == 0 || !canRemoveCurrentView())
return;
scheduleAction(ViewNavigatorAction.POP_ALL, null, null, null, transition);
}
/**
* Pops the current view off the navigation stack.
* The current view is represented by the top view on the stack.
* The previous view on the stack becomes the current view.
*
* @param transition The view transition to play while switching views.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function popView(transition:ViewTransitionBase = null):void
{
if (navigationStack.length == 0 || !canRemoveCurrentView())
return;
scheduleAction(ViewNavigatorAction.POP, null, null, null, transition);
}
/**
* Removes all views except the bottom view from the navigation stack.
* The bottom view is the one that was first pushed onto the stack.
*
* @param transition The view transition to play while switching views.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function popToFirstView(transition:ViewTransitionBase = null):void
{
if (navigationStack.length < 2 || !canRemoveCurrentView())
return;
scheduleAction(ViewNavigatorAction.POP_TO_FIRST, null, null, null, transition);
}
/**
* Pushes a new view onto the top of the navigation stack.
* The view pushed onto the stack becomes the current view.
*
* @param viewClass The class used to create the view.
* This argument must reference a class that extends View container.
*
* @param data The data object to pass to the view.
* This argument is written to the <code>data</code> property of the new view.
*
* @param context An arbitrary object written to
* the <code>ViewNavigator.context</code> property.
* When the new view is created, it can reference this property
* and perform an action based on its value.
* For example, the view could display data in different ways based
* on the value of <code>context</code>.
*
* @param transition The view transition to play while switching views.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function pushView(viewClass:Class,
data:Object = null,
context:Object = null,
transition:ViewTransitionBase = null):void
{
if (viewClass == null || !canRemoveCurrentView())
return;
scheduleAction(ViewNavigatorAction.PUSH, viewClass, data, context, transition);
}
/**
* Replaces the top view of the navigation stack with a new view.
* The view replacing the current view on the stack becomes the current view.
*
* @param viewClass The class used to create the replacement view.
* This argument must reference a class that extends View container.
*
* @param data The data object to pass to the view.
* This argument is written to the <code>data</code> property of the new view.
*
* @param context An arbitrary object used to describe the context
* of the push. When the new view is created, it can
* reference this property.
*
* @param transition The view transition to play while switching views.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function replaceView(viewClass:Class,
data:Object = null,
context:Object = null,
transition:ViewTransitionBase = null):void
{
if (viewClass == null || !canRemoveCurrentView())
return;
scheduleAction(ViewNavigatorAction.REPLACE, viewClass, data, context, transition);
}
/**
* Shows the action bar.
*
* @param animate Indicates whether a show effect should be played
* when the action bar appears.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function showActionBar(animate:Boolean = true):void
{
if (!actionBar)
return;
// Ignore this call if the actionBar is already being shown
if (actionBarVisibilityEffect && showingActionBar)
return;
showingActionBar = true;
animateActionBarVisbility = animate;
actionBarVisibilityInvalidated = true;
invalidateProperties();
}
/**
* Hides the action bar.
*
* @param animate Indicates whether a hide effect should be played
* when the action bar is hidden.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function hideActionBar(animate:Boolean = true):void
{
if (!actionBar)
return;
// Ignore this call if the actionBar is already being hidden
if (actionBarVisibilityEffect && !showingActionBar)
return;
showingActionBar = false;
animateActionBarVisbility = animate;
actionBarVisibilityInvalidated = true;
invalidateProperties();
}
/**
* Pops to the previous view of the navigator in response to the back
* key. ViewNavigator only allows this method to be called once during
* a navigation event. All subsequent calls to this method will be ignored
* until the current view transition is complete.
*
* <p>ViewNavigatorApplication automatically calls this method when the back
* key is pressed.</p>
*
* @langversion 3.0
* @playerversion AIR 3.1
* @productversion Flex 4.6
*/
override public function backKeyUpHandler():void
{
if (!backKeyWasPressed && activeView && !activeView.backKeyHandledByView())
{
popView();
backKeyWasPressed = true;
}
}
//--------------------------------------------------------------------------
//
// Protected Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Initializes the view change process by disabling inputs on the
* navigator. If the navigator has a parent, the parents mouse
* interaction flags are disabled.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function committingNavigatorAction():void
{
viewChanging = true;
explicitMouseChildren = mouseChildren;
explicitMouseEnabled = mouseEnabled;
mouseEnabled = false;
mouseChildren = false;
}
/**
* @private
*/
override mx_internal function canRemoveCurrentView():Boolean
{
var view:View;
if (!currentViewDescriptor)
return true;
view = currentViewDescriptor.instance;
return (view == null || view.canRemove());
}
/**
* @private
* Helper method that clears the action bar property invalidation flags.
*
* @default null
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function clearActionBarInvalidationFlags():void
{
actionContentInvalidated = false;
actionLayoutInvalidated = false;
navigationContentInvalidated = false;
navigationLayoutInvalidated = false;
titleInvalidated = false;
titleContentInvalidated = false;
titleLayoutInvalidated = false;
overlayControlsInvalidated = false;
}
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
override protected function commitProperties():void
{
if (!isActive)
return;
// If this is the components first validation pass, push the firstView
// on the stack if possible, otherwise set the currentViewChange flag
// to true so that an empty screen is created. If the currentViewDescriptor
// property exists, that means an empty view was previously created
// because a firstView property wasn't supplied.
if (!initialized && navigationStack.length == 0 && !currentViewDescriptor)
{
if (firstView)
navigationStack.pushView(firstView, firstViewData);
viewChangeRequested = true;
}
if (viewChangeRequested)
commitNavigatorAction();
// Updating the action bar properties and visibility is the responsibility
// of commitViewChange if the current view has changed because they must take
// part in transitions. If the view change is processed during this validation,
// the following flags will be false.
if (actionBarPropertyInvalidated)
updateControlsForView(activeView);
if (actionBarVisibilityInvalidated)
commitVisibilityChanges();
// When true, this flag prevents action bar animations from running. This flag
// is only set to true if the application received an orientation event this
// frame (See ViewNavigatorBase). The flag is reset at the end of commitProperties
// so that animations run again.
if (disableNextControlAnimation)
disableNextControlAnimation = false;
// Call base class' commitProperties after the above so that state changes
// as a result of overlayControls are caught. This is okay because
// this method doesn't rely on any properties from the base class.
super.commitProperties();
}
/**
* @private
*/
private function get lastActionWasAPop():Boolean
{
return ((lastAction == ViewNavigatorAction.POP) ||
(lastAction == ViewNavigatorAction.POP_ALL) ||
(lastAction == ViewNavigatorAction.POP_TO_FIRST) ||
(lastAction == ViewNavigatorAction.REPLACE));
}
/**
* @private
* This method registers a navigation operation with the navigators
* action queue. Navigation operations aren't performed until the
* following frame to allow components to properly update their
* visual state before any complicated actionScript code is run by the
* navigator.
*
* <p>This method will execute all operations when the next ENTER_FRAME
* event is dispatched by the runtime.</P.
*
* @param action The navigation operation that is being performed. Should
* be one of the constants in ViewNavigatorAction.
* @param viewClass The class that will be created in the case of a push action
* @param data The data object to pass to the view in the case of a push action
* @param transition The view transition to play
* @param context An arbitrary string that can be used to describe the context
* of the push. When the new view is created, it will be able to reference
* this property.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function scheduleAction(action:String,
viewClass:Class = null,
data:Object = null,
context:Object = null,
transition:ViewTransitionBase = null):void
{
// ViewNavigator does not allow a push or replace operation to occur
// without the viewClass factory object defined.
if (action == ViewNavigatorAction.PUSH || action == ViewNavigatorAction.REPLACE)
{
if (!viewClass)
return;
}
// Navigation operations are not committed immediately to allow the UI
// to update before beginning the creation process. When an action is
// queued for the first time, we add an enter frame listener.
if (delayedNavigationActions.length == 0)
{
// If the navigator is currently in the process of switching views,
// the queued actions will automatically be run later in
// navigatorActionCommitted() when the transition is complete.
// So there is no need to add the ENTER_FRAME listener.
if (!viewChanging)
addEventListener(Event.ENTER_FRAME, executeDelayedActions);
}
delayedNavigationActions.push({action:action, viewClass:viewClass,
data:data, transition:transition, context:context});
if (activeView)
activeView.dispatchEvent(new Event("_navigationChange_"));
}
/**
* @private
* Executes all the navigation operations that have been queued
* by the navigation methods (e.g., popView, pushView).
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function executeDelayedActions(event:Event = null):void
{
if (event)
removeEventListener(Event.ENTER_FRAME, executeDelayedActions);
if (delayedNavigationActions.length == 0)
return;
var parameters:Object;
var n:int = delayedNavigationActions.length;
for (var i:int = 0; i < n; ++i)
{
parameters = delayedNavigationActions[i];
executeAction(parameters.action, parameters.viewClass,
parameters.data, parameters.context, parameters.transition);
}
delayedNavigationActions.length = 0;
viewChangeRequested = true;
invalidateProperties();
}
/**
* @private
* Helper method that executes navigation operations for the navigator.
*
* @param action The navigation operation that is being performed. Should
* be one of the constants in ViewNavigatorAction.
* @param viewClass The class that will be created in the case of a push action
* @param data The data object to pass to the view in the case of a push action
* @param transition The view transition to play
* @param context An arbitrary string that can be used to describe the context
* of the push. When the new view is created, it will be able to reference
* this property.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function executeAction(action:String, viewClass:Class = null,
data:Object = null,
context:Object = null,
transition:ViewTransitionBase = null):void
{
var defaultTransition:ViewTransitionBase;
lastAction = action;
// Perform the correct operation on the navigation stack based on
// the navigation action
if (action == ViewNavigatorAction.PUSH)
{
defaultTransition = defaultPushTransition;
navigationStack.pushView(viewClass, data, context);
}
else if (action == ViewNavigatorAction.REPLACE)
{
defaultTransition = defaultPushTransition;
navigationStack.popView();
navigationStack.pushView(viewClass, data, context);
}
else
{
defaultTransition = defaultPopTransition;
if (action == ViewNavigatorAction.POP)
{
navigationStack.popView();
}
else if (action == ViewNavigatorAction.POP_TO_FIRST)
{
navigationStack.popToFirstView();
}
else if (action == ViewNavigatorAction.POP_ALL)
{
navigationStack.clear();
}
}
pendingViewTransition = transition;
if (pendingViewTransition == null)
pendingViewTransition = defaultTransition;
}
/**
* @private
* Invalidates all action bar property flags.
*/
private function invalidateActionBarProperties():void
{
actionContentInvalidated = true;
actionLayoutInvalidated = true;
navigationContentInvalidated = true;
navigationLayoutInvalidated = true;
titleInvalidated = true;
titleContentInvalidated = true;
overlayControlsInvalidated = true;
titleLayoutInvalidated = true;
invalidateProperties();
}
/**
* @private
* Commits the visiblity changes that have been requested. This method
* is called during an invalidation pass if the current view has not changed
* and the action bar's visibility has changed.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function commitVisibilityChanges():void
{
if (viewChanging)
{
actionBarVisibilityInvalidated = false;
return;
}
// If an animation is running, end it
if (actionBarVisibilityEffect)
actionBarVisibilityEffect.end();
if (actionBar && showingActionBar != actionBar.visible)
{
if (!disableNextControlAnimation && transitionsEnabled && animateActionBarVisbility)
{
actionBarProps = {target:actionBar, showing:showingActionBar};
actionBarVisibilityEffect = showingActionBar ?
createActionBarShowEffect() :
createActionBarHideEffect();
actionBarVisibilityEffect.addEventListener(EffectEvent.EFFECT_END,
visibilityAnimation_effectEndHandler);
actionBarVisibilityEffect.play();
}
else
{
actionBar.visible = actionBar.includeInLayout = showingActionBar;
if (activeView)
activeView.setActionBarVisible(showingActionBar);
}
}
actionBarVisibilityInvalidated = false;
}
/**
* Creates the effect to play when the ActionBar control is hidden.
* The produced effect is responsible for animating both the
* ActionBar and the view currently displayed in the
* content area of the navigator.
*
* @return An effect to play when the ActionBar control is hidden.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function createActionBarHideEffect():IEffect
{
return createActionBarVisibilityEffect(false);
}
/**
* Creates the effect to play when the ActionBar control appears.
* The produced effect is responsible for animating both the
* ActionBar and the view currently displayed in the
* content area of the navigator.
*
* @return An effect to play when the ActionBar control is appears.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function createActionBarShowEffect():IEffect
{
return createActionBarVisibilityEffect(true);
}
/**
* @private
*/
private function createActionBarVisibilityEffect(showAnimation:Boolean):IEffect
{
var effect:IEffect;
var finalEffect:Parallel = new Parallel();
// Grab initial values
actionBarProps.start = captureAnimationValues(actionBar);
contentGroupProps = { target:contentGroup, start:captureAnimationValues(contentGroup) };
// Update actionBar layout properties so we can capture the final state of
// of the navigator
actionBar.visible = actionBar.includeInLayout = showAnimation;
// Calculate final positions and position actionBar. This method will force a validation
prepareActionBarForAnimation(showAnimation);
// Create animation for action bar
var animate:Animate = new Animate();
animate.target = actionBar;
animate.duration = ACTION_BAR_ANIMATION_DURATION;
animate.motionPaths = new Vector.<MotionPath>();
animate.motionPaths.push(new SimpleMotionPath("y", actionBarProps.start.y, actionBarProps.end.y));
// Add action bar effect to final parallel effect
effect = animate;
finalEffect.addChild(effect);
// Create animation for content group
effect = createContentVisibilityEffect(contentGroupProps);
effect.target = contentGroup;
finalEffect.addChild(effect);
return finalEffect;
}
/**
* @private
* Responsible for calculating the final positions of the action bar
* when it's visiblity is changed. This method will force a validation
* pass.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function prepareActionBarForAnimation(showAnimation:Boolean):void
{
var animateActionBarUp:Boolean;
// Determine whether the action bar should be animated up or down
if (overlayControls)
{
// If the control is overlaid on top, the actionBar is animated up if
// the actionBar is above the center of the navigator
animateActionBarUp = (actionBar.y + (actionBar.height / 2)) <= height / 2;
}
else
{
// The actionBar is animated up if it is above the contentGroup
animateActionBarUp = actionBar.y <= contentGroup.y;
}
// Need to validate to capture final positions and sizes of skin parts.
// If the navigator is a child of another, we need the root navigator
// to perform the validation so that all widths and heights of all
// containers are sized.
LayoutManager.getInstance().validateNow();
// This will store the final location and sizes of the components
actionBarProps.end = captureAnimationValues(actionBar);
contentGroupProps.end = captureAnimationValues(contentGroup);
// Update the end position of the animation based on whether the
// actionBar is showing/hiding and if it is animating up or down.
if (animateActionBarUp)
{
if (showAnimation)
actionBarProps.start.y = -actionBar.height;
else
actionBarProps.end.y = -actionBar.height;
}
else
{
if (showAnimation)
actionBarProps.start.y = this.height;
else
actionBarProps.end.y = this.height;
}
actionBar.visible = true;
actionBar.includeInLayout = false;
actionBar.cacheAsBitmap = true;
}
/**
* @private
* Creates the effect to play on the contentGroup when the navigator is
* generating an animation to play to hide or show the action bar. This effect
* should only target the contentGroup as it will be played in parallel with
* other effects that animate the other navigator skin parts.
*
* @param hiding Indicates whether the action bar is hiding or showing
* @param props The bounds properties that were captured for the actionBar.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function createContentVisibilityEffect(props:Object):IEffect
{
var animate:Animate = new Animate();
animate.target = contentGroup;
animate.duration = ACTION_BAR_ANIMATION_DURATION;
animate.motionPaths = new Vector.<MotionPath>();
animate.motionPaths.push(new SimpleMotionPath("height", props.start.height, props.end.height));
animate.motionPaths.push(new SimpleMotionPath("y", props.start.y, props.end.y));
contentGroup.includeInLayout = false;
return animate;
}
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function visibilityAnimation_effectEndHandler(event:EffectEvent):void
{
event.target.removeEventListener(EffectEvent.EFFECT_END, visibilityAnimation_effectEndHandler);
// Clear flags and temporary properties
actionBarVisibilityEffect = null;
if (activeView)
activeView.setActionBarVisible(actionBarProps.showing);
// Check if the visible and cacheAsBitmap properties have been set. These are
// only set if the default transitions are used. If developers create their
// own animations, these properties won't be set.
if (actionBarProps.start != undefined)
{
actionBar.visible = actionBar.includeInLayout = !actionBarProps.start.visible;
actionBar.cacheAsBitmap = actionBarProps.start.cacheAsBitmap;
}
actionBarProps = null;
// Content group properties object only created if the default transitions were used
if (contentGroupProps)
{
contentGroup.includeInLayout = contentGroupProps.start.includeInLayout;
// The default action bar hide and show animation will animate the width and height
// of the navigator's contentGroup. If the explicitWidth or explicitHeight properties
// were NaN before the animation, they'll be set to real values by the animation. As
// a result, it is necessary to restore them to NaN so that layout properly sizes these
// components.
if (isNaN(contentGroupProps.start.explicitHeight))
contentGroup.explicitHeight = NaN;
if (isNaN(contentGroupProps.start.explicitWidth))
contentGroup.explicitWidth = NaN;
if (!isNaN(contentGroupProps.start.percentWidth))
contentGroup.percentWidth = contentGroupProps.start.percentWidth;
if (!isNaN(contentGroupProps.start.percentHeight))
contentGroup.percentHeight = contentGroupProps.start.percentHeight;
contentGroupProps = null;
}
}
/**
* @private
* This method is responsible for completing a view change validation pass.
* It is responsible for cleaning up and destroying the old view, as well as
* activating the new one.
*
* <p>If a transition was played, this method is called after the transition
* completes.</p>
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function navigatorActionCommitted():void
{
// Destroy the previous view
if (currentViewDescriptor)
destroyViewInstance(currentViewDescriptor);
// Update view pointers
currentViewDescriptor = pendingViewDescriptor;
pendingViewDescriptor = null;
// Clear empty flag if necessary
if (emptyViewDescriptor && currentViewDescriptor != emptyViewDescriptor)
emptyViewDescriptor = null;
// If there is no focus or the item that had focus isn't
// on the display list anymore, update the focus to be
// the active view or the view navigator
updateFocus();
// Clear the returned object
_poppedViewReturnedObject = null;
// Restore mouse children properties before revalidation occurs. This
// needs to occur before a possible revalidation occurs so that the
// saved mouseChildren and mouseEnabled flags aren't overwritten.
mouseChildren = explicitMouseChildren;
mouseEnabled = explicitMouseEnabled;
// SDK-28230
// Wait a frame before sending the complete event so that the player
// has the chance to render the last frame before any custom actionscript
// is run in response to a VIEW_ACTIVATE event.
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
/**
* @private
* Called when the activeView has received its first updateComplete event
* after the navigator has been activated. See setActive().
*/
private function view_updateCompleteHandler(event:FlexEvent):void
{
event.target.removeEventListener(FlexEvent.UPDATE_COMPLETE, view_updateCompleteHandler);
completeViewCommitProcess();
}
/**
* @private
* Called after a navigation operation is complete. See
* navigatorActionCommitted().
*/
private function enterFrameHandler(event:Event):void
{
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
completeViewCommitProcess();
}
/**
* @private
* Activates the current view and completes the view change process.
*/
private function completeViewCommitProcess():void
{
// ViewNavigator doesn't allow for another navigation operation to
// be run during a view change. If the component attempts to do
// one, it is queued and run after the current transition is complete.
// The delayedNavigationActions queue size will be non zero in that case.
// If there are items in the queue, force another validation to commit
// navigation change. Otherwise the transition process can end.
if (delayedNavigationActions.length > 0)
{
executeDelayedActions();
commitNavigatorAction();
return;
}
// The viewChanging flag is set to false right before the current view
// activates so that navigation operations run during VIEW_ACTIVATE
// are properly executed by the navigator
viewChanging = false;
// At this point, currentViewDescriptor points to the new view.
// The navigator needs to listen for property change events on the
// view so that it can be notified when the template properties
// (e.g, title, titleContent, etc) are changed.
if (currentViewDescriptor)
{
var currentView:View = currentViewDescriptor.instance;
if (currentView)
{
currentView.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE,
view_propertyChangeHandler);
// Activate the current view. This will dispatch a VIEW_ACTIVATE event.
currentView.setActive(true);
}
}
// Notify listeners that the view change is complete
if (hasEventListener("viewChangeComplete"))
dispatchEvent(new Event("viewChangeComplete"));
// Clear flag indicating that the back key was pressed
backKeyWasPressed = false;
lastAction = ViewNavigatorAction.NONE;
}
/**
* @private
* Called in commitProperties() and begins the view transition
* process.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function commitNavigatorAction():void
{
if (!isActive)
{
viewChangeRequested = false;
return;
}
// Private event
if (hasEventListener("viewChangeStart"))
dispatchEvent(new Event("viewChangeStart"));
// If a ui control is animating, force it to end
if (actionBarVisibilityEffect)
actionBarVisibilityEffect.end();
if (activeView && lastActionWasAPop)
{
_poppedViewReturnedObject = createViewReturnObject(currentViewDescriptor);
}
committingNavigatorAction();
pendingViewDescriptor = navigationStack.topView;
// Create an empty view if no firstView viewClass is defined
if (pendingViewDescriptor == null)
{
emptyViewDescriptor = new ViewDescriptor(View);
pendingViewDescriptor = emptyViewDescriptor;
}
if (pendingViewDescriptor.viewClass != null)
{
var view:View = createViewInstance(pendingViewDescriptor);
viewChangeRequested = false;
// Hide the view so that it doesn't render this frame
view.visible = false;
activeTransition = transitionsEnabled ? pendingViewTransition : null;
// TODO (chiedozi): Consider capturingStartValues now and updating actionBar
// to remove forced validation in prepareViewTransition()
// Prepare the view transition after the navigator has validated the new View
addEventListener(FlexEvent.UPDATE_COMPLETE, prepareViewTransition);
}
pendingViewTransition = null;
}
/**
* @private
* This method is used to create the top view of the ViewNavigator. This
* is only used by TabbedViewNavigator when the selected tab has changed.
*/
override mx_internal function createTopView():void
{
// Check if the top view already exists
if (activeView)
return;
invalidateActionBarProperties();
// If the navigation stack is empty, push on the firstView for the
// navigator.
if (navigationStack.length == 0)
{
if (firstView != null)
navigationStack.pushView(firstView, firstViewData);
else
return;
}
// Update the current view reference
currentViewDescriptor = navigationStack.topView;
// Create the view if needed
var view:View = currentViewDescriptor.instance;
if (!view)
{
view = createViewInstance(currentViewDescriptor);
view.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, view_propertyChangeHandler);
}
// Cancel any view change requests that occurred prior to this call
// since the top most view was just created.
viewChangeRequested = false;
}
/**
* @private
*/
private function createViewInstance(viewProxy:ViewDescriptor):View
{
var view:View;
if (viewProxy.instance == null)
{
view = new viewProxy.viewClass();
viewProxy.instance = view;
}
else
{
view = viewProxy.instance;
// Need to update the view's orientation state if it was saved
view.setCurrentState(view.getCurrentViewState(), false);
}
// Restore persistence data if necessary
if (viewProxy.data == null && viewProxy.persistenceData != null)
viewProxy.data = view.deserializeData(viewProxy.persistenceData);
view.setNavigator(this);
view.data = viewProxy.data;
view.percentWidth = view.percentHeight = 100;
addElement(view);
return view;
}
/**
* @private
*/
private function createViewReturnObject(viewProxy:ViewDescriptor):ViewReturnObject
{
var view:View = viewProxy.instance;
if (view)
return new ViewReturnObject(view.createReturnObject(), viewProxy.context);
return null;
}
/**
* @private
*/
private function destroyViewInstance(viewProxy:ViewDescriptor, forceDataPersist:Boolean = false):void
{
var currentView:View = viewProxy.instance;
if (!currentView)
return;
// Deactivate the view if it is active
deactiveView(currentView);
removeElement(currentView);
// Grab the data from the old view and persist it
if (lastAction == ViewNavigatorAction.PUSH || forceDataPersist)
{
viewProxy.data = currentView.data;
viewProxy.persistenceData = currentView.serializeData();
}
// Check if we can delete the reference for the view instance. If the current
// view is being replaced or popped of the stack, we know we can delete it.
// Otherwise a push is happening and we need to check the destructionPolicy
// of the view.
if (lastActionWasAPop || currentView.destructionPolicy != ContainerDestructionPolicy.NEVER)
{
currentView.setNavigator(null);
viewProxy.instance = null;
}
}
/**
* @private
*/
private function deactiveView(view:View):void
{
if (view.isActive)
view.setActive(false);
view.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE,
view_propertyChangeHandler);
}
/**
* @private
*/
override public function saveViewData():Object
{
var savedData:Object = super.saveViewData();
if (currentViewDescriptor && currentViewDescriptor.instance)
currentViewDescriptor.persistenceData = currentViewDescriptor.instance.serializeData();
if (!savedData)
savedData = {};
savedData.navigationStack = navigationStack;
return savedData;
}
/**
* @private
*/
override public function loadViewData(value:Object):void
{
super.loadViewData(value);
if (value)
navigationStack = value.navigationStack as NavigationStack;
}
/**
* @private
* Method is called during the view transition process after the
* instance of the new view is added to the display list. It initializes
* the underlying ViewDescriptor object and prepares the transition.
*
* Called after the UPDATE_COMPLETE event is received on the navigator
* after the pendingView is added to the display list.
* See commitNavigatorAction.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function prepareViewTransition(event:Event):void
{
removeEventListener(FlexEvent.UPDATE_COMPLETE, prepareViewTransition);
var currentView:View;
var pendingView:View;
// Deactivate the current view
if (currentViewDescriptor)
{
currentView = currentViewDescriptor.instance;
currentView.setActive(false);
// In most cases this is a No-Op because this method is called in response
// to UPDATE_COMPLETE on the navigator and there should be no objects
// in the LayoutManager's queue. It's only here to ensure that the
// current view is up to date incase anything changed.
currentView.validateNow();
}
// Store new view
if (pendingViewDescriptor)
{
pendingView = pendingViewDescriptor.instance;
pendingView.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE,
view_propertyChangeHandler);
}
if (activeTransition)
{
activeTransition.addEventListener(FlexEvent.TRANSITION_END, transitionComplete);
activeTransition.startView = currentView;
activeTransition.endView = pendingView;
activeTransition.navigator = this;
activeTransition.preInit();
if (stage)
stage.dispatchEvent(new Event("viewTransitionPrepare"));
}
// Only dispatch this event if the stage exists.
if (stage && viewTransitionSuspendCount > 0)
{
if (!eventDispatcher)
eventDispatcher = new EventDispatcher();
eventDispatcher.addEventListener("viewTransitionReady", completeTransitionPreparations);
}
else
{
completeTransitionPreparations();
}
}
/**
* @private
*/
mx_internal static function suspendTransitions():void
{
viewTransitionSuspendCount++;
}
/**
* @private
*/
mx_internal static function resumeTransitions():void
{
if (viewTransitionSuspendCount == 0)
return;
viewTransitionSuspendCount--;
if (viewTransitionSuspendCount == 0)
eventDispatcher.dispatchEvent(new Event("viewTransitionReady"));
}
/**
* @private
*/
private function completeTransitionPreparations(event:Event = null):void
{
if (event)
event.target.removeEventListener("viewTransitionReady", completeTransitionPreparations);
var pendingView:View;
if (pendingViewDescriptor)
{
pendingView = pendingViewDescriptor.instance;
pendingView.visible = true;
}
// Give the transition a chance to prepare before the view updates
if (activeTransition)
activeTransition.captureStartValues();
// This event is dispatched here to allow developers to incorporate
// length specific changes into the view navigator transitions
if (hasEventListener("lengthChanged"))
dispatchEvent(new Event("lengthChanged"));
// Invalidate the actionBar properties
if (actionBar)
{
invalidateActionBarProperties();
updateControlsForView(pendingView);
}
// Only force validation for the navigator if initialized to avoid
// validation from occurring with wrong measured dimensions
if (initialized)
{
// Need to validate my children now to prevent flicker when no transition,
// or so sizes can be measured before transition
if (parentNavigator)
UIComponent(parentNavigator).validateNow();
else
validateNow();
}
if (activeTransition)
{
activeTransition.captureEndValues();
activeTransition.prepareForPlay();
// Wait a frame so that any queued work can be completed by the framework
// and runtime before the transition starts. As of Flex 4.6, we wait 2
// frames to allow StageText to fully render the swapped in bitmaps.
// Otherwise rendering time would overlap with the first frame of the animation.
enterFrameCount = 0;
addEventListener(Event.ENTER_FRAME, startViewTransition);
}
else
{
navigatorActionCommitted();
}
}
/**
* @private
* Starts the view transition.
*/
private function startViewTransition(event:Event):void
{
// Incrememnt the enterFrameCount. ViewNavigator waits two frames
// before begining the animation.
enterFrameCount++;
if (enterFrameCount < 2)
return;
// Remove the enter frame listener
removeEventListener(Event.ENTER_FRAME, startViewTransition);
if (hasEventListener(FlexEvent.TRANSITION_START))
dispatchEvent(new FlexEvent(FlexEvent.TRANSITION_START, false, false));
// Force the master clock of the animation engine to update its
// current time so that the overhead of creating the view and preparing
// the transition is not included in our animation interpolation.
// See SDK-27793
Animation.pulse();
activeTransition.play();
}
/**
* @private
* Called when a transition dispatches an FlexEvent.TRANSITION_END event.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function transitionComplete(event:Event):void
{
ViewTransitionBase(event.target).removeEventListener(FlexEvent.TRANSITION_END, transitionComplete);
if (hasEventListener(FlexEvent.TRANSITION_END))
dispatchEvent(new FlexEvent(FlexEvent.TRANSITION_END, false, false));
activeTransition = null;
navigatorActionCommitted();
}
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
override public function updateControlsForView(view:View):void
{
super.updateControlsForView(view);
if (!actionBar)
return;
// If there is no view, update the actionBar should display the
// navigator defaults
if (view == null)
{
actionBar.actionContent = actionContent;
actionBar.actionLayout = actionLayout;
actionBar.navigationContent = navigationContent;
actionBar.navigationLayout = navigationLayout;
actionBar.title = title;
actionBar.titleContent = titleContent;
actionBar.titleLayout = titleLayout;
overlayControls = false;
}
else
{
if (actionContentInvalidated)
{
actionBar.actionContent = view && view.actionContent ?
view.actionContent : actionContent;
actionContentInvalidated = false;
}
if (actionLayoutInvalidated)
{
actionBar.actionLayout = view && view.actionLayout ?
view.actionLayout : actionLayout;
actionLayoutInvalidated = false;
}
if (navigationContentInvalidated)
{
actionBar.navigationContent = view && view.navigationContent ?
view.navigationContent : navigationContent;
navigationContentInvalidated = false;
}
if (navigationLayoutInvalidated)
{
actionBar.navigationLayout = view && view.navigationLayout ?
view.navigationLayout : navigationLayout;
navigationLayoutInvalidated = false;
}
if (titleInvalidated)
{
actionBar.title = view && view.title ? view.title : title;
titleInvalidated = false;
}
if (titleContentInvalidated)
{
actionBar.titleContent = view && view.titleContent ?
view.titleContent : titleContent;
titleContentInvalidated = false;
}
if (titleLayoutInvalidated)
{
actionBar.titleLayout = view && view.titleLayout ?
view.titleLayout : titleLayout;
titleLayoutInvalidated = false;
}
if (overlayControlsInvalidated)
{
if (overlayControls != view.overlayControls)
{
overlayControls = view.overlayControls;
// We need to call super commitProperties() so that the new state is applied
super.commitProperties();
}
overlayControlsInvalidated = false;
}
actionBar.visible = actionBar.includeInLayout = view && view.actionBarVisible;
actionBarVisibilityInvalidated = false;
actionBar.invalidateSize();
actionBar.invalidateDisplayList();
}
}
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
private function view_propertyChangeHandler(event:PropertyChangeEvent):void
{
var property:Object = event.property;
// Check for actionBar related property changes
if (actionBar)
{
var propertyInvalidated:Boolean = true;
if (property == "title")
titleInvalidated = true;
else if (property == "titleContent")
titleContentInvalidated = true;
else if (property == "titleLayout")
titleLayoutInvalidated = true;
else if (property == "actionContent")
actionContentInvalidated = true;
else if (property == "actionLayout")
actionLayoutInvalidated = true;
else if (property == "navigationContent")
navigationContentInvalidated = true;
else if (property == "navigationLayout")
navigationLayoutInvalidated = true;
else
propertyInvalidated = false;
if (propertyInvalidated)
invalidateProperties();
}
if (property == "overlayControls")
{
overlayControlsInvalidated = true;
invalidateProperties();
}
}
/**
* @private
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
override protected function partAdded(partName:String, instance:Object):void
{
super.partAdded(partName, instance);
// If the actionBar changes, need to reset the properties on it
if (instance == actionBar)
{
actionContentInvalidated = true;
actionLayoutInvalidated = true;
navigationContentInvalidated = true;
navigationLayoutInvalidated = true;
titleInvalidated = true;
titleContentInvalidated = true;
titleLayoutInvalidated = true;
invalidateProperties();
}
}
/**
* @private
*/
override protected function partRemoved(partName:String, instance:Object):void
{
super.partRemoved(partName, instance);
// Clear out all the content that is within the actionBar
if (instance == actionBar)
{
actionBar.actionContent = null;
actionBar.actionLayout = null;
actionBar.titleContent = null;
actionBar.titleLayout = null;
actionBar.navigationContent = null;
actionBar.navigationContent = null;
}
}
}
}