| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // TODO (chiedozi): TabbedViewNavigator should override setActive and activate |
| // the correct ViewNavigator. |
| package spark.components |
| { |
| import flash.display.Stage; |
| import flash.events.Event; |
| import flash.events.IEventDispatcher; |
| import flash.events.MouseEvent; |
| |
| import mx.core.ISelectableList; |
| import mx.core.IVisualElement; |
| import mx.core.UIComponent; |
| import mx.core.mx_internal; |
| import mx.effects.IEffect; |
| import mx.effects.Parallel; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.EffectEvent; |
| import mx.events.FlexEvent; |
| import mx.events.PropertyChangeEvent; |
| import mx.events.PropertyChangeEventKind; |
| import mx.managers.LayoutManager; |
| import mx.resources.ResourceManager; |
| |
| import spark.components.supportClasses.ButtonBarBase; |
| import spark.components.supportClasses.ViewNavigatorBase; |
| import spark.effects.Animate; |
| import spark.effects.animation.MotionPath; |
| import spark.effects.animation.SimpleMotionPath; |
| import spark.events.ElementExistenceEvent; |
| import spark.events.IndexChangeEvent; |
| import spark.events.RendererExistenceEvent; |
| |
| use namespace mx_internal; |
| |
| //-------------------------------------- |
| // Events |
| //-------------------------------------- |
| |
| /** |
| * Dispatched when the current view navigator changes as a result of |
| * a change to the <code>selectedIndex</code> property or a change |
| * to the selected tab in the TabBar control. |
| * |
| * @eventType spark.events.IndexChangeEvent.CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| [Event(name="change", type="spark.events.IndexChangeEvent")] |
| |
| /** |
| * Dispatched before the selected view navigator is changed. |
| * Canceling this event prevents the active view navigator |
| * from changing. |
| * |
| * @eventType spark.events.IndexChangeEvent.CHANGING |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| [Event(name="changing", type="spark.events.IndexChangeEvent")] |
| |
| /** |
| * Dispatched when the collection of view navigators managed by the |
| * TabbedViewNavigator changes. |
| * |
| * @eventType mx.events.CollectionEvent.COLLECTION_CHANGE |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| [Event(name="collectionChange", type="mx.events.CollectionEvent")] |
| |
| /** |
| * Dispatched when the view navigator's selected index changes. |
| * When this event is dispatched, the <code>selectedIndex</code> |
| * and <code>selectedNavigator</code> properties reference the |
| * newly selected view navigator. |
| * |
| * @eventType mx.events.FlexEvent.VALUE_COMMIT |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| [Event(name="valueCommit", type="mx.events.FlexEvent")] |
| |
| //-------------------------------------- |
| // Other metadata |
| //-------------------------------------- |
| |
| [IconFile("TabbedViewNavigator.png")] |
| |
| /** |
| * The TabbedViewNavigator class is a container that manages a collection |
| * of view navigator containers. |
| * Only one view navigator is active and visible at a time. |
| * This class includes a TabBar control that provides the ability to toggle between |
| * the collection of view navigators. |
| * |
| * <p>The following image shows a TabbedViewNavigator with three sections: |
| * Employees, Contacts, and Search:</p> |
| * |
| * <p> |
| * <img src="../../images/tvn_sections_with_ab_tvn.png" alt="Tabbed view navigator" /> |
| * </p> |
| * |
| * <p>The TabbedViewNavigatorApplication container automatically creates |
| * a single TabbedViewNavigator container for the entire application. |
| * You can reference the TabbedViewNavigator object by using the <code>navigator</code> |
| * property of the TabbedViewNavigatorApplication container.</p> |
| * |
| * <p>The active or selected navigator can be changed by clicking the corresponding |
| * tab in the TabBar or by changing the <code>selectedIndex</code> property of |
| * the component.</p> |
| * |
| * <p>The contents of a child view navigator is destroyed when it is deactivate, |
| * and dynamically created when activated.</p> |
| * |
| * @see spark.components.View |
| * @see spark.components.ViewNavigator |
| * @see spark.components.TabBar |
| * |
| * @includeExample examples/TabbedViewNavigatorExample.mxml -noswf |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public class TabbedViewNavigator extends ViewNavigatorBase implements ISelectableList |
| { |
| //-------------------------------------------------------------------------- |
| // |
| // Class variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| // TODO (chiedozi): Consider having a NO_SELECTION constant to differentiate |
| // between NO_PROPOSED_SELECTION and clearing the selection. See List. It |
| // seems redundant because TabbedViewNavigator expects requireSelection to be |
| // true on its TabBar. |
| /** |
| * @private |
| * Static constant representing no proposed selection. |
| */ |
| private static const NO_PROPOSED_SELECTION:int = -1; |
| |
| /** |
| * @private |
| * The animation duration used when hiding and showing the action bar. |
| */ |
| private static const TAB_BAR_ANIMATION_DURATION:Number = 250; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function TabbedViewNavigator() |
| { |
| super(); |
| |
| addEventListener(ElementExistenceEvent.ELEMENT_ADD, elementAddHandler); |
| addEventListener(ElementExistenceEvent.ELEMENT_REMOVE, elementRemoveHandler); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Skin Parts |
| // |
| //-------------------------------------------------------------------------- |
| |
| [Bindable] |
| [SkinPart(required="false")] |
| /** |
| * A skin part that defines the tab bar of the navigator. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public var tabBar:ButtonBarBase; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var animateTabBarVisbility:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var contentGroupProps:Object; |
| |
| /** |
| * @private |
| * Determines if a changing event has already been dispatched. |
| * This was introduced to prevent the IndexChangeEvent from |
| * dispatching twice when the tabBar selection changes. |
| */ |
| private var changingEventDispatched:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var dataProviderChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var explicitTabBarMouseEnabled:Boolean = false; |
| |
| /** |
| * @private |
| * Keeps track of the tab that was last selected. See |
| * tabBarRendererClicked() for information on how it's used. |
| */ |
| private var lastSelectedIndex:int = -1; |
| |
| /** |
| * @private |
| */ |
| private var selectedIndexChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var selectedIndexAdjusted:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var showingTabBar:Boolean; |
| |
| /** |
| * @private |
| */ |
| private var tabBarVisibilityEffect:IEffect; |
| |
| /** |
| * @private |
| */ |
| private var tabBarProps:Object; |
| |
| /** |
| * @private |
| */ |
| private var tabBarVisibilityChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| mx_internal var selectedNavigatorChangingView:Boolean = false; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // activeView |
| //---------------------------------- |
| |
| [Bindable("viewChangeComplete")] |
| /** |
| * @private |
| */ |
| override public function get activeView():View |
| { |
| if (selectedNavigator) |
| return selectedNavigator.activeView; |
| |
| return null; |
| } |
| |
| //---------------------------------- |
| // exitApplicationOnBackKey |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override mx_internal function get exitApplicationOnBackKey():Boolean |
| { |
| if (selectedNavigator) |
| return selectedNavigator.exitApplicationOnBackKey; |
| |
| return super.exitApplicationOnBackKey; |
| } |
| |
| //---------------------------------- |
| // maintainNavigationStack |
| //---------------------------------- |
| private var _maintainNavigationStack:Boolean = true; |
| |
| /** |
| * @private |
| * Specifies whether the navigation stack of the view navigator |
| * should remain intact when the view navigator is deactivated. |
| * If <code>true</code>, when reactivated the view history |
| * remains the same. |
| * If <code>false</code>, the navigator displays the |
| * first view in its navigation stack. |
| * |
| * @default true |
| * |
| * @see spark.components.ViewNavigator |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| mx_internal function get maintainNavigationStack():Boolean |
| { |
| return _maintainNavigationStack; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function set maintainNavigationStack(value:Boolean):void |
| { |
| _maintainNavigationStack = value; |
| } |
| |
| //---------------------------------- |
| // navigators |
| //---------------------------------- |
| |
| /** |
| * The view navigators that are managed by this TabbedViewNavigator. |
| * Each view navigator is represented as a tab in the tab bar |
| * of this TabbedViewNavigator. |
| * Only one view navigator can be active at a time. |
| * You can reference the active view navigator by using |
| * the <code>selectedNavigator</code> property. |
| * |
| * <p>Changing this property causes the current view navigator to be removed, |
| * and sets the <code>selectedIndex</code> to 0. |
| * This operation cannot be canceled and is committed immediately.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get navigators():Vector.<ViewNavigatorBase> |
| { |
| var mxmlContent:Array = currentContentGroup.getMXMLContent(); |
| |
| if (!mxmlContent) |
| return null; |
| |
| // When this class was first released, navigators was typed Vector |
| // and not Array. In 4.6, TabbedViewNavigator moved away from this |
| // model and uses mxmlContent to manage children. To maintain backwards |
| // compatibility, the return type for this method remained the same. |
| // As a result, the mxmlContent array will need to converted. |
| return Vector.<ViewNavigatorBase>(mxmlContent); |
| } |
| |
| /** |
| * @private |
| */ |
| public function set navigators(value:Vector.<ViewNavigatorBase>):void |
| { |
| // When this class was first released, navigators was typed Vector |
| // and not Array. In 4.6, TabbedViewNavigator moved away from this |
| // model and uses mxmlContent to manage children. To maintain backwards |
| // compatibility, the return type for this method remained the same. |
| // As a result, the value vector needs to be converted to an array |
| var contentArray:Array = []; |
| |
| for each (var navigator:ViewNavigatorBase in value) |
| { |
| contentArray.push(navigator); |
| setupNavigator(navigator); |
| } |
| |
| mxmlContent = contentArray; |
| |
| // The proposed selected index is reset because it is no longer valid |
| // since the array of navigators has changed. |
| selectedIndexChanged = false; |
| |
| // Invalidate property flags |
| dataProviderChanged = true; |
| invalidateProperties(); |
| |
| // Notify listeners that the collection changed |
| internalDispatchEvent(CollectionEventKind.RESET); |
| } |
| |
| //---------------------------------- |
| // selectedNavigator |
| //---------------------------------- |
| |
| [Bindable("change")] |
| /** |
| * The selected view navigator for the TabbedViewNavigator. |
| * Only one view navigator can be active at a time. |
| * The active view navigator can be set by changing the |
| * <code>selectedIndex</code> property or by selecting |
| * a tab in the TabBar control. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get selectedNavigator():ViewNavigatorBase |
| { |
| if (length == 0 || selectedIndex < 0 || selectedIndex >= length) |
| return null; |
| |
| return getElementAt(selectedIndex) as ViewNavigatorBase; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Public Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Hides the tab bar of the navigator. |
| * |
| * @param animate Indicates whether a hide effect should play |
| * as the tab bar disappears. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function hideTabBar(animate:Boolean = true):void |
| { |
| if (!tabBar) |
| return; |
| |
| showingTabBar = false; |
| animateTabBarVisbility = animate; |
| tabBarVisibilityChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| /** |
| * Shows the tab bar of the navigator |
| * |
| * @param animate Indicates whether a show effect should play |
| * as the tab bar appears. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function showTabBar(animate:Boolean = true):void |
| { |
| if (!tabBar) |
| return; |
| |
| showingTabBar = true; |
| animateTabBarVisbility = animate; |
| tabBarVisibilityChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| /** |
| * @private |
| * Updates the navigator's state based on the properties set |
| * on the active view. The main use cases are to hide the |
| * TabBar and the change the overlay state. |
| * |
| * @param view The active view. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| override public function updateControlsForView(view:View):void |
| { |
| super.updateControlsForView(view); |
| |
| if (view) |
| { |
| if (tabBar) |
| tabBar.visible = tabBar.includeInLayout = view.tabBarVisible; |
| |
| overlayControls = view.overlayControls; |
| } |
| else |
| { |
| // If the current view is null, the tab bar is shown so that the |
| // user still has a ui for changing the selected tab. |
| if (tabBar) |
| tabBar.visible = tabBar.includeInLayout = true; |
| |
| overlayControls = false; |
| } |
| |
| tabBarVisibilityChanged = false; |
| } |
| |
| /** |
| * Calls the backKeyUpHandler() of the selected navigator. |
| * |
| * <p>TabbedViewNavigatorApplication 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 (selectedNavigator) |
| selectedNavigator.backKeyUpHandler(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Private Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Calculates the final positions for the tab bar visibility animations. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| private function prepareTabBarForAnimation(showAnimation:Boolean):void |
| { |
| var animateTabBarUp:Boolean; |
| |
| if (overlayControls) |
| { |
| // If the control is overlaid on top, the tabBar is animated up if |
| // the tabBar is above the center of the navigator |
| animateTabBarUp = (tabBar.y + (tabBar.height / 2)) <= height / 2; |
| } |
| else |
| { |
| // The tabBar is animated up if it is above the contentGroup |
| animateTabBarUp = tabBar.y <= contentGroup.y; |
| } |
| |
| // Need to validate to capture final positions and sizes of skin parts |
| LayoutManager.getInstance().validateNow(); |
| |
| // This will store the final location and sizes of the components |
| tabBarProps.end = captureAnimationValues(tabBar); |
| contentGroupProps.end = captureAnimationValues(contentGroup); |
| |
| if (tabBarVisibilityChanged) |
| { |
| if (animateTabBarUp) |
| { |
| if (tabBarProps.start.visible) |
| tabBarProps.end.y = -tabBar.height; |
| else |
| tabBarProps.start.y = -tabBar.height; |
| } |
| else |
| { |
| if (tabBarProps.start.visible) |
| tabBarProps.end.y = this.height; |
| else |
| tabBarProps.start.y = this.height; |
| } |
| } |
| |
| tabBar.visible = true; |
| tabBar.includeInLayout = false; |
| tabBar.cacheAsBitmap = true; |
| } |
| |
| /** |
| * @private |
| */ |
| override mx_internal function canRemoveCurrentView():Boolean |
| { |
| return (!selectedNavigator || selectedNavigator.canRemoveCurrentView()); |
| } |
| |
| /** |
| * @private |
| */ |
| private function addNavigatorListeners(navigator:ViewNavigatorBase):void |
| { |
| navigator.addEventListener("viewChangeComplete", navigator_viewChangeCompleteHandler); |
| navigator.addEventListener("viewChangeStart", navigator_viewChangeStartHandler); |
| navigator.addEventListener(ElementExistenceEvent.ELEMENT_ADD, navigator_elementAddHandler); |
| navigator.addEventListener(ElementExistenceEvent.ELEMENT_REMOVE, navigator_elementRemoveHandler); |
| } |
| |
| /** |
| * @private |
| */ |
| private function removeNavigatorListeners(navigator:ViewNavigatorBase):void |
| { |
| navigator.removeEventListener("viewChangeComplete", navigator_viewChangeCompleteHandler); |
| navigator.removeEventListener("viewChangeStart", navigator_viewChangeStartHandler); |
| navigator.removeEventListener(ElementExistenceEvent.ELEMENT_ADD, navigator_elementAddHandler); |
| navigator.removeEventListener(ElementExistenceEvent.ELEMENT_REMOVE, navigator_elementRemoveHandler); |
| } |
| |
| /** |
| * @private |
| */ |
| private function setupNavigator(navigator:ViewNavigatorBase):void |
| { |
| navigator.setParentNavigator(this); |
| navigator.visible = false; |
| navigator.includeInLayout = false; |
| |
| // All navigators should be inactive when initialized. The proper |
| // navigator will be activated during commitProperties(). |
| navigator.setActive(false); |
| |
| startTrackingUpdates(navigator); |
| } |
| |
| /** |
| * @private |
| */ |
| private function cleanUpNavigator(navigator:ViewNavigatorBase):void |
| { |
| navigator.setParentNavigator(null); |
| |
| if (navigator.isActive) |
| navigator.setActive(false); |
| |
| if (navigator.activeView) |
| { |
| navigator.activeView.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, |
| view_propertyChangeHandler); |
| } |
| |
| removeNavigatorListeners(navigator); |
| stopTrackingUpdates(navigator); |
| } |
| |
| /** |
| * @private |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| override protected function commitProperties():void |
| { |
| var changeEvent:IndexChangeEvent; |
| |
| if (selectedIndexChanged || selectedIndexAdjusted || dataProviderChanged) |
| { |
| var navigator:ViewNavigatorBase; |
| |
| // Store the old index |
| var oldIndex:int = _selectedIndex; |
| |
| if (selectedIndex >= length) |
| selectedIndex = (length > 0) ? 0 : NO_PROPOSED_SELECTION; |
| |
| var indexChangedPrevented:Boolean = false; |
| // The selected index property can be changed programmitically on the |
| // TabbedViewNavigator or by its tab bar in response to a user action. If |
| // this was initiated by the tab bar, a chaning event would have already been |
| // dispatched in tabBar_indexChanging(). |
| if (initialized && selectedIndexChanged && !changingEventDispatched) |
| { |
| // If the active view's REMOVING event or the navigator's |
| // CHANGING event was canceled, prevent the index change |
| if (!indexCanChange(selectedIndex)) |
| { |
| _proposedSelectedIndex = NO_PROPOSED_SELECTION; |
| indexChangedPrevented = true; |
| } |
| } |
| |
| changingEventDispatched = false; |
| |
| if (!indexChangedPrevented) |
| { |
| // If the data provider has changed, the navigator elements have |
| // already been removed, so the following code doesn't need to run |
| if (!selectedIndexAdjusted && !dataProviderChanged && _selectedIndex >= 0 && _selectedIndex < length) |
| { |
| navigator = getElementAt(_selectedIndex) as ViewNavigatorBase; |
| navigator.setActive(false, !maintainNavigationStack); |
| navigator.visible = false; |
| navigator.includeInLayout = false; |
| |
| if (navigator.activeView) |
| navigator.activeView.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, |
| view_propertyChangeHandler); |
| |
| removeNavigatorListeners(navigator); |
| } |
| |
| commitSelection(); |
| |
| if (_selectedIndex >= 0) |
| { |
| // 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(); |
| |
| navigator = getElementAt(_selectedIndex) as ViewNavigatorBase; |
| navigator.setActive(true); |
| navigator.visible = true; |
| navigator.includeInLayout = true; |
| |
| addNavigatorListeners(navigator); |
| |
| // Update the states of the controls on the newly activated view navigator. |
| // The updateControlsForView() method will automatically bubble up to all |
| // parents of the navigator. |
| navigator.updateControlsForView(navigator.activeView); |
| |
| // Force a validation of the new navigator to prevent a flicker from |
| // occurring in cases where multiple validation passes are required |
| // to completely validate a view |
| if (initialized) |
| currentContentGroup.validateNow(); |
| |
| if (navigator.activeView) |
| { |
| navigator.activeView.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, |
| view_propertyChangeHandler); |
| } |
| } |
| |
| // Dispatch selection change event |
| if (hasEventListener(IndexChangeEvent.CHANGE)) |
| { |
| changeEvent = new IndexChangeEvent(IndexChangeEvent.CHANGE, false, false); |
| changeEvent.oldIndex = oldIndex; |
| changeEvent.newIndex = _selectedIndex; |
| } |
| } |
| |
| selectedIndexAdjusted = false; |
| dataProviderChanged = false; |
| selectedIndexChanged = false; |
| } |
| |
| if (tabBarVisibilityChanged) |
| commitVisibilityChanges(); |
| |
| // When true, this flag prevents tab 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 should be okay because |
| // TabbedViewNavigator doesn't need any properties in its base class being |
| // validated to complete the above code |
| super.commitProperties(); |
| |
| // Dispatch the index change event after super.commitProperties() has changed |
| // so the callbacks can deal with state changes performed by commitProperties. |
| if (changeEvent) |
| dispatchEvent(changeEvent); |
| } |
| |
| /** |
| * @private |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected function commitSelection():void |
| { |
| _selectedIndex = _proposedSelectedIndex; |
| _proposedSelectedIndex = NO_PROPOSED_SELECTION; |
| |
| lastSelectedIndex = _selectedIndex; |
| |
| if (hasEventListener(FlexEvent.VALUE_COMMIT)) |
| dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT)); |
| } |
| |
| /** |
| * @private |
| * Commits the requested visibility changes made to the action |
| * bar and tab bar. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| private function commitVisibilityChanges():void |
| { |
| // Can't change the visibility during a view transition |
| if (selectedNavigatorChangingView) |
| return; |
| |
| // If an animation is running, end it |
| if (tabBarVisibilityEffect) |
| tabBarVisibilityEffect.end(); |
| |
| // Change the visibility of the tabBar if the desired state |
| // is different than the current state |
| if (tabBar && showingTabBar != tabBar.visible) |
| { |
| // The animateTabBarVisibility flag is set to true if the |
| // hideTabBar() or showTabBar() methods are called with the |
| // animate flag set to true |
| if (!disableNextControlAnimation && transitionsEnabled && animateTabBarVisbility) |
| { |
| tabBarProps = {target:tabBar, showing:showingTabBar}; |
| tabBarVisibilityEffect = showingTabBar ? |
| createTabBarShowEffect() : |
| createTabBarHideEffect(); |
| |
| tabBarVisibilityEffect.addEventListener(EffectEvent.EFFECT_END, |
| visibilityAnimation_effectEndHandler); |
| tabBarVisibilityEffect.play(); |
| } |
| else |
| { |
| // Since the visibility is not being animated, toggle the state |
| tabBar.visible = tabBar.includeInLayout = showingTabBar; |
| |
| if (activeView) |
| activeView.setTabBarVisible(showingTabBar); |
| } |
| } |
| |
| tabBarVisibilityChanged = false; |
| } |
| |
| /** |
| * Creates the effect to play when the TabBar control is shown. |
| * The produced effect is responsible for animating both the |
| * TabBar and the content group of the navigator. |
| * |
| * <p>TabbedViewNavigator will expect the <code>includeInLayout</code> |
| * and <code>visible</code> properties of the TabBar to be <code>true</code> |
| * after this effect is run.</p> |
| * |
| * @return An effect to play when the TabBar control appears. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected function createTabBarShowEffect():IEffect |
| { |
| return createTabBarVisibilityEffect(true); |
| } |
| |
| /** |
| * Creates the effect to play when the TabBar control is hidden. |
| * The produced effect is responsible for animating both the |
| * TabBar and the content group of the navigator. |
| * |
| * <p>TabbedViewNavigator expects the <code>includeInLayout</code> |
| * and <code>visible</code> properties of the TabBar to be <code>false</code> |
| * after this effect runs.</p> |
| * |
| * @return An effect to play when the TabBar control is hidden. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected function createTabBarHideEffect():IEffect |
| { |
| return createTabBarVisibilityEffect(false); |
| } |
| |
| /** |
| * Creates the animation that will be played when the TabBar is |
| * hidden. |
| * |
| * @return The effect ot play |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| private function createTabBarVisibilityEffect(showAnimation:Boolean):IEffect |
| { |
| var effect:IEffect; |
| var finalEffect:Parallel = new Parallel(); |
| |
| // Grab initial values |
| tabBarProps.start = captureAnimationValues(tabBar); |
| contentGroupProps = { start:captureAnimationValues(contentGroup) }; |
| |
| // Update actionBar layout properties |
| tabBar.visible = tabBar.includeInLayout = showingTabBar; |
| |
| // Calculate final positions. This method will force a validation |
| prepareTabBarForAnimation(showAnimation); |
| |
| var animate:Animate = new Animate(); |
| animate.target = tabBar; |
| animate.duration = TAB_BAR_ANIMATION_DURATION; |
| animate.motionPaths = new Vector.<MotionPath>(); |
| animate.motionPaths.push(new SimpleMotionPath("y", tabBarProps.start.y, tabBarProps.end.y)); |
| |
| effect = animate; |
| finalEffect.addChild(effect); |
| |
| // Create content group animation |
| animate = new Animate(); |
| animate.target = contentGroup; |
| animate.duration = TAB_BAR_ANIMATION_DURATION; |
| animate.motionPaths = new Vector.<MotionPath>(); |
| animate.motionPaths.push(new SimpleMotionPath("y", contentGroupProps.start.y, |
| contentGroupProps.end.y)); |
| animate.motionPaths.push(new SimpleMotionPath("height", contentGroupProps.start.height, |
| contentGroupProps.end.height)); |
| |
| contentGroup.includeInLayout = false; |
| |
| finalEffect.addChild(animate); |
| finalEffect.duration = TAB_BAR_ANIMATION_DURATION; |
| return finalEffect; |
| } |
| |
| /** |
| * @private |
| */ |
| private function navigator_elementAddHandler(event:ElementExistenceEvent):void |
| { |
| event.element.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, |
| view_propertyChangeHandler); |
| } |
| |
| /** |
| * @private |
| */ |
| private function navigator_elementRemoveHandler(event:ElementExistenceEvent):void |
| { |
| event.element.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, |
| view_propertyChangeHandler); |
| } |
| |
| /** |
| * @private |
| * Dispatched when a property on a child navigator changed. |
| */ |
| private function navigator_propertyChangeHandler(event:PropertyChangeEvent):void |
| { |
| itemUpdated(event.target, event.property, event.oldValue, event.newValue); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function partAdded(partName:String, instance:Object):void |
| { |
| super.partAdded(partName, instance); |
| |
| if (instance == tabBar) |
| { |
| tabBar.addEventListener(IndexChangeEvent.CHANGING, tabBar_indexChanging); |
| tabBar.dataGroup.addEventListener(RendererExistenceEvent.RENDERER_ADD, tabBar_elementAddHandler); |
| tabBar.dataProvider = this; |
| tabBar.requireSelection = true; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function partRemoved(partName:String, instance:Object):void |
| { |
| super.partRemoved(partName, instance); |
| |
| if (instance == tabBar) |
| { |
| tabBar.dataGroup.removeEventListener(RendererExistenceEvent.RENDERER_ADD, tabBar_elementAddHandler); |
| tabBar.removeEventListener(IndexChangeEvent.CHANGING, tabBar_indexChanging); |
| tabBar.dataProvider = null; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override public function loadViewData(value:Object):void |
| { |
| super.loadViewData(value); |
| |
| var savedStacks:Vector.<Object>; |
| var savedSelectedIndex:Number = value.selectedIndex as Number; |
| |
| // By the time this method is called, all skin parts should be |
| // added to the component meaning that it is okay to directly |
| // manipulate the child navigators. |
| if (!isNaN(savedSelectedIndex) && savedSelectedIndex < length) |
| selectedIndex = savedSelectedIndex; |
| |
| savedStacks = value.childrenStates as Vector.<Object>; |
| |
| if (savedStacks) |
| { |
| var navigator:ViewNavigatorBase; |
| var len:Number = Math.min(savedStacks.length, length); |
| |
| for (var i:int = 0; i < len; i++) |
| { |
| navigator = getElementAt(i) as ViewNavigatorBase; |
| navigator.loadViewData(savedStacks[i]); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override public function saveViewData():Object |
| { |
| var savedData:Object = super.saveViewData(); |
| var childrenStates:Vector.<Object> = new Vector.<Object>(); |
| |
| for (var i:int = 0; i < length; i++) |
| childrenStates.push((getElementAt(i) as ViewNavigatorBase).saveViewData()); |
| |
| if (!savedData) |
| savedData = {}; |
| |
| savedData.selectedIndex = selectedIndex; |
| savedData.childrenStates = childrenStates; |
| |
| return savedData; |
| } |
| |
| /** |
| * @private |
| * Redispatches the child navigator's COMPLETE event. |
| */ |
| private function navigator_viewChangeCompleteHandler(event:Event):void |
| { |
| if (hasEventListener("viewChangeComplete")) |
| dispatchEvent(event); |
| |
| selectedNavigatorChangingView = false; |
| |
| if (tabBar) |
| tabBar.mouseEnabled = explicitTabBarMouseEnabled; |
| } |
| |
| /** |
| * @private |
| */ |
| private function navigator_viewChangeStartHandler(event:Event):void |
| { |
| selectedNavigatorChangingView = true; |
| |
| // Disable mouse interaction with the tabBar |
| if (tabBar) |
| { |
| explicitTabBarMouseEnabled = tabBar.mouseEnabled; |
| tabBar.mouseEnabled = false; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function tabBar_elementAddHandler(event:RendererExistenceEvent):void |
| { |
| event.target.addEventListener(MouseEvent.CLICK, tabBarRenderer_clickHandler); |
| } |
| |
| /** |
| * @private |
| * Determines if the selection of the tab bar can change. |
| */ |
| private function tabBar_indexChanging(event:IndexChangeEvent):void |
| { |
| if (!indexCanChange(event.newIndex)) |
| { |
| event.preventDefault(); |
| |
| // Clear changing flag |
| changingEventDispatched = false; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function elementRemoveHandler(event:ElementExistenceEvent):void |
| { |
| cleanUpNavigator(event.element as ViewNavigatorBase); |
| internalDispatchEvent(CollectionEventKind.REMOVE, event.element, event.index); |
| |
| // Element removed event is called before the element is removed from the |
| // display list. So if the last navigator is about to be removed, numElements |
| // should be 1 |
| if (numElements == 1) |
| selectedIndex = -1; |
| } |
| |
| /** |
| * @private |
| */ |
| private function elementAddHandler(event:ElementExistenceEvent):void |
| { |
| setupNavigator(event.element as ViewNavigatorBase); |
| internalDispatchEvent(CollectionEventKind.ADD, event.element, event.index); |
| |
| // If the added element is the first view navigator, update the selection |
| // to point to it |
| if (numElements == 1) |
| selectedIndex = 0; |
| } |
| |
| /** |
| * @private |
| * Method is called when a tab is clicked by the user. The default |
| * implementation checks if the clicked tab is currently selected, and if |
| * so, pops the active navigator to its root view. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| mx_internal function tabBarRenderer_clickHandler(event:MouseEvent):void |
| { |
| // lastSelectedIndex is used instead of tabBar.selectedIndex |
| // because by the time this method is called, the tabBar's |
| // proposedSelectedIndex has changed, which causes its selectedIndex |
| // method to return the proposed index instead of the currently |
| // selected index. |
| if ((event.target is IItemRenderer) && |
| (IItemRenderer(event.target).itemIndex == lastSelectedIndex)) |
| { |
| if (selectedNavigator is ViewNavigator) |
| ViewNavigator(selectedNavigator).popToFirstView(); |
| } |
| } |
| |
| /** |
| * @private |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| private function view_propertyChangeHandler(event:PropertyChangeEvent):void |
| { |
| if (event.property == "tabBarVisible") |
| { |
| if (event.newValue) |
| showTabBar(); |
| else |
| hideTabBar(); |
| } |
| else if (event.property == "overlayControls") |
| { |
| overlayControls = event.newValue; |
| } |
| } |
| |
| /** |
| * @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); |
| tabBarVisibilityEffect = null; |
| |
| if (activeView) |
| activeView.setTabBarVisible(tabBarProps.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 (tabBarProps.start != undefined) |
| { |
| tabBar.visible = tabBar.includeInLayout = tabBarProps.end.visible; |
| tabBar.cacheAsBitmap = tabBarProps.start.cacheAsBitmap; |
| } |
| |
| tabBarProps = null; |
| |
| if (contentGroupProps) |
| { |
| contentGroup.includeInLayout = contentGroupProps.start.includeInLayout; |
| contentGroup.cacheAsBitmap = contentGroupProps.start.cacheAsBitmap; |
| |
| // The default tab 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; |
| } |
| } |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods: ISelectableList |
| // |
| //-------------------------------------------------------------------------- |
| //---------------------------------- |
| // selectedIndex |
| //---------------------------------- |
| |
| /** |
| * @private |
| * The proposed selected index. This is a temporary variable that is |
| * used until the selected index is committed. |
| */ |
| protected var _proposedSelectedIndex:int = NO_PROPOSED_SELECTION; |
| |
| /** |
| * @private |
| * The backing variable for selectedIndex. |
| */ |
| protected var _selectedIndex:int = NO_PROPOSED_SELECTION; |
| |
| [Bindable("change")] |
| [Bindable("valueCommit")] |
| /** |
| * The 0-based index of the selected view navigator, or -1 if none is selected. |
| * Setting the <code>selectedIndex</code> property deselects the currently selected |
| * navigator and selects the navigator at the specified index. |
| * |
| * <p>The value is always between -1 and (<code>navigators.length</code> - 1). |
| * If items at a lower index than <code>selectedIndex</code> are |
| * removed from the component, the selected index is adjusted downward |
| * accordingly.</p> |
| * |
| * <p>If the selected item is removed, the selected index is set to:</p> |
| * |
| * <ul> |
| * <li>-1 if there are no remaining items.</li> |
| * <li><code>selectedIndex</code> - 1 if there is at least one item.</li> |
| * </ul> |
| * |
| * @default -1 |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get selectedIndex():int |
| { |
| if (_proposedSelectedIndex != NO_PROPOSED_SELECTION) |
| return _proposedSelectedIndex; |
| |
| return _selectedIndex; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set selectedIndex(value:int):void |
| { |
| if (!dataProviderChanged && value == selectedIndex) |
| return; |
| |
| if (activeView) |
| activeView.dispatchEvent(new Event("_navigationChange_")); |
| |
| _proposedSelectedIndex = value; |
| selectedIndexChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| /** |
| * @private |
| * This method determines the navigator can change its selected index. |
| * This function will be called if the selection changed on the |
| * tab bar, or if the selectedIndex property has changed. |
| */ |
| private function indexCanChange(proposedSelection:int):Boolean |
| { |
| changingEventDispatched = true; |
| if (!canRemoveCurrentView()) |
| return false; |
| |
| if (hasEventListener(IndexChangeEvent.CHANGING)) |
| { |
| var e:IndexChangeEvent = new IndexChangeEvent(IndexChangeEvent.CHANGING, false, true); |
| e.oldIndex = _selectedIndex; |
| e.newIndex = proposedSelection; |
| |
| if (!dispatchEvent(e)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| //---------------------------------- |
| // length |
| //---------------------------------- |
| |
| /** |
| * The number of child view navigators being managed by the |
| * this component. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get length():int |
| { |
| return numElements; |
| } |
| |
| /** |
| * Add the specified view navigator to the end of the list. |
| * Equivalent to calling <code>addItemAt(item, length);</code>. |
| * |
| * @param item The view navigator to add. |
| * It must extend the <code>ViewNavigatorBase</code> class. |
| * |
| * @see spark.components.supportClasses.ViewNavigatorBase |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function addItem(item:Object):void |
| { |
| addItemAt(item, length); |
| } |
| |
| /** |
| * Add the view navigator at the specified index. |
| * Any view navigator that was after this index is moved down by one. |
| * |
| * @param item The view navigator to add. |
| * It must extend the <code>ViewNavigatorBase</code> class. |
| * |
| * @param index The index at which to place the item. |
| * |
| * @throws RangeError If index is less than 0 or greater than the length |
| * |
| * @see spark.components.supportClasses.ViewNavigatorBase |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function addItemAt(item:Object, index:int):void |
| { |
| // If the added type is not a ViewNavigatorBase, we ignore the call |
| // TODO (chiedozi): Consider throwing an exception here |
| if (!(item is ViewNavigatorBase)) |
| return; |
| |
| if (index < 0 || index > length) |
| { |
| var message:String = ResourceManager.getInstance().getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| setupNavigator(ViewNavigatorBase(item)); |
| addElementAt(ViewNavigatorBase(item), index); |
| |
| if (selectedIndex == NO_PROPOSED_SELECTION || numElements == 1) |
| { |
| selectedIndex = 0; |
| } |
| else if (index <= selectedIndex) |
| { |
| selectedIndex++; |
| selectedIndexAdjusted = true; |
| } |
| } |
| |
| /** |
| * Get the view navigator object at the specified index. |
| * |
| * @param index The index in the list from which to retrieve the item. |
| * |
| * @param prefetch Indicating both the direction and amount of items |
| * to fetch during the request, should the item not be local. |
| * |
| * @return The navigator at the specified index, or null if there is none. |
| * |
| * @throws RangeError If the index < 0 or index >= length |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function getItemAt(index:int, prefetch:int = 0):Object |
| { |
| if (index < 0 || index >= length) |
| { |
| var message:String = ResourceManager.getInstance().getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| return getElementAt(index); |
| } |
| |
| /** |
| * Return the index of the view navigator if it is in the list |
| * of view navigators. |
| * |
| * @param item The view navigator object to locate. |
| * |
| * @return The index of the view navigator, or -1 if the item is not in the list. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function getItemIndex(item:Object):int |
| { |
| return getElementIndex(item as ViewNavigatorBase); |
| } |
| |
| /** |
| * Notify external components that a property on a view navigator has |
| * been updated. |
| * |
| * @param item The view navigator that was updated. |
| * |
| * @param property A String, QName, or int |
| * specifying the property that was updated. |
| * |
| * @param oldValue The old value of that property. |
| * If property was null, this can be the old value of the item. |
| * |
| * @param newValue The new value of that property. |
| * If property was null, there's no need to specify this |
| * as the item is assumed to be the new value. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function itemUpdated(item:Object, property:Object = null, |
| oldValue:Object = null, |
| newValue:Object = null):void |
| { |
| var event:PropertyChangeEvent = |
| new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); |
| |
| event.kind = PropertyChangeEventKind.UPDATE; |
| event.source = item; |
| event.property = property; |
| event.oldValue = oldValue; |
| event.newValue = newValue; |
| |
| internalDispatchEvent(CollectionEventKind.UPDATE, event); |
| } |
| |
| /** |
| * Remove all child view navigators from the navigator. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function removeAll():void |
| { |
| // Deactive the active navigator and its view |
| if (selectedNavigator) |
| selectedNavigator.setActive(false); |
| |
| // Remove all navigators |
| navigators = null; |
| } |
| |
| /** |
| * Removes the specified item from this list, should it exist. |
| * Relies on ArrayList implementation |
| * |
| * @param item Object reference to the item that should be removed. |
| * @return Boolean indicating if the item was removed. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Apache Flex 4.10 |
| */ |
| public function removeItem( item:Object ):Boolean |
| { |
| var element:IVisualElement = removeElement(item as ViewNavigatorBase); |
| return element != null; |
| } |
| |
| /** |
| * Remove the view navigator at the specified index and return it. |
| * The index of any items that were after this index are decreased by one. |
| * |
| * @param index The index from which to remove the item. |
| * |
| * @return The item that was removed. |
| * |
| * @throws RangeError If index < 0 or index >= length. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function removeItemAt(index:int):Object |
| { |
| if (index < 0 || index >= length) |
| { |
| var message:String = ResourceManager.getInstance().getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| if (index <= selectedIndex) |
| { |
| selectedIndex--; |
| selectedIndexAdjusted = true; |
| } |
| |
| var removed:Object = removeElementAt(index); |
| return removed; |
| } |
| |
| /** |
| * Add the view navigator at the specified index. |
| * If an item was already at that index, the new item replaces it, |
| * and it is returned. |
| * |
| * @param item The view navigator to place at the index. |
| * |
| * @param index The index at which to place the navigator. |
| * |
| * @return The navigator that was replaced, or null if none. |
| * |
| * @throws RangeError If index is less than 0 or greater than or equal to length |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function setItemAt(item:Object, index:int):Object |
| { |
| // If the added type is not a ViewNavigatorBase, we ignore the call |
| // TODO (chiedozi): Consider throwing an exception here |
| if (!(item is ViewNavigator)) |
| return null; |
| |
| if (index < 0 || index >= length) |
| { |
| var message:String = ResourceManager.getInstance().getString( |
| "collections", "outOfBounds", [ index ]); |
| throw new RangeError(message); |
| } |
| |
| var oldItem:Object = removeElementAt(index); |
| addElementAt(item as ViewNavigatorBase, index); |
| |
| // Let the navigator know that the item at the selectedIndex changed. |
| // The commitProperties method will pick this up. |
| if (index == selectedIndex) |
| { |
| selectedIndexAdjusted = true; |
| invalidateProperties(); |
| } |
| |
| if (hasEventListener(CollectionEvent.COLLECTION_CHANGE)) |
| { |
| var updateInfo:PropertyChangeEvent; |
| |
| updateInfo = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); |
| updateInfo.kind = PropertyChangeEventKind.UPDATE; |
| updateInfo.oldValue = oldItem; |
| updateInfo.newValue = item; |
| updateInfo.property = index; |
| |
| internalDispatchEvent(CollectionEventKind.REPLACE, updateInfo, index); |
| } |
| |
| return oldItem; |
| } |
| |
| /** |
| * Return an Array that is populated in the same order as the IList |
| * implementation. |
| * |
| * @return The Array. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function toArray():Array |
| { |
| return contentGroup.getMXMLContent(); |
| } |
| |
| /** |
| * @private |
| * Dispatches a collection event with the specified information. |
| * |
| * @param kind String indicates what the kind property of the event should be |
| * @param item Object reference to the item that was added or removed |
| * @param location int indicating where in the source the item was added. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| private function internalDispatchEvent(kind:String, item:Object = null, location:int = -1):void |
| { |
| if (hasEventListener(CollectionEvent.COLLECTION_CHANGE)) |
| { |
| var event:CollectionEvent = |
| new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = kind; |
| event.items.push(item); |
| event.location = location; |
| dispatchEvent(event); |
| } |
| |
| // now dispatch a complementary PropertyChangeEvent |
| if (hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE) && |
| (kind == CollectionEventKind.ADD || kind == CollectionEventKind.REMOVE)) |
| { |
| var objEvent:PropertyChangeEvent = |
| new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); |
| |
| objEvent.property = location; |
| if (kind == CollectionEventKind.ADD) |
| objEvent.newValue = item; |
| else |
| objEvent.oldValue = item; |
| |
| dispatchEvent(objEvent); |
| } |
| } |
| |
| /** |
| * @private |
| * If the item is an IEventDispatcher watch it for updates. |
| * This is called by addItemAt and when the source is initially |
| * assigned. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| private function startTrackingUpdates(item:Object):void |
| { |
| if (item && (item is IEventDispatcher)) |
| { |
| IEventDispatcher(item).addEventListener( |
| PropertyChangeEvent.PROPERTY_CHANGE, |
| navigator_propertyChangeHandler, false, 0, true); |
| } |
| } |
| |
| /** |
| * @private |
| * If the item is an IEventDispatcher stop watching it for updates. |
| * This is called by removeItemAt, removeAll, and before a new |
| * source is assigned. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| private function stopTrackingUpdates(item:Object):void |
| { |
| if (item && item is IEventDispatcher) |
| { |
| IEventDispatcher(item).removeEventListener( |
| PropertyChangeEvent.PROPERTY_CHANGE, |
| navigator_propertyChangeHandler); |
| } |
| } |
| } |
| } |