The primary purpose for this feature is to provide necessary infrastructure for the following use cases:
Through prototyping, it was identified that for the mobile use cases the biggest hurdles are defining animations for pop-ups and pop-up / tear-down.
To solve these issues, the feature introduces the SkinnablePopUpContainer class:
Josh needs a simple pop-up message for Alert, but he wants to configure the text the way he likes it. He notices the SkinnablePopUpContainer and gives it a try:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Declarations>
<s:SkinnablePopUpContainer id="myAlert">
<s:Panel title="Launching World Domination...">
<s:VGroup horizontalAlign="center" paddingTop="20" gap="20" width="100%">
<s:Label text="Server connection failed. Sorry!"/>
<s:Button label="OK" click="myAlert.close()"/>
</s:VGroup>
</s:Panel>
</s:SkinnablePopUpContainer>
</fx:Declarations>
<s:Button label="Launch World Domination" click="myAlert.open(this, false)"/>
</s:Application>
Mike needs a dialog for his app. He needs some title, a list of action items and some nice animations for opening and closing the dialog. Mike decides to go with the SkinnablePopUpContainer since it holds the promise to allow him to define the animations through skinning and states. First, Mike creates a mxml skin - MyDialogSkin.mxml. He arranges the visuals, hard-codes the title. He makes sure to add the "contentGroup" part.
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:states>
<s:State name="closed"/>
<s:State name="normal"/>
<s:State name="disabled"/>
<s:State name="disabledAndClosed"/>
</s:states>
<!-- background, it will cover the whole stage as the hostComponent is
initialized to cover the stage -->
<s:Rect id="bg" left="0" right="0" top="0" bottom="0"
alpha.closed="0" alpha.normal="0.4">
<s:fill>
<s:SolidColor color="0"/>
</s:fill>
</s:Rect>
<!-- the chrome -->
<s:Group id="chromeGroup"
width="80%"
height="80%"
horizontalCenter="0"
verticalCenter="0"
alpha="1"
alpha.closed="0"
>
<s:layout>
<s:VerticalLayout gap="0" horizontalAlign="justify" />
</s:layout>
<!--- @private -->
<s:Group id="topGroup">
<!-- layer 0: title bar fill -->
<s:Rect id="tbFill" left="0" right="0" top="0" bottom="1"/>
<!-- layer 1: title bar highlight -->
<s:Rect id="tbHilite" left="0" right="0" top="0" bottom="0"/>
<!-- layer 2: title bar divider -->
<s:Rect id="tbDiv" left="0" right="0" height="1" bottom="0"/>
<!-- layer 3: text -->
<s:Label id="titleDisplay" maxDisplayedLines="1" text="Select Action"
left="9" right="3" top="1" bottom="0" minHeight="30"
verticalAlign="middle" textAlign="start" fontWeight="bold">
</s:Label>
</s:Group>
<s:Group id="contentGroup" width="100%" height="100%" minWidth="0" minHeight="0">
</s:Group>
</s:Group>
</s:Skin>
For the animations, Mike defines transitions between the "closed" and the "normal" states.
<s:transitions>
<s:Transition fromState="closed" toState="normal">
<s:Sequence>
<s:Parallel>
<!-- fade in the panel -->
<s:Fade target="{chromeGroup}" duration="150"/>
<!-- scale in the panel -->
<s:Scale target="{chromeGroup}" duration="200"
applyChangesPostLayout="true"
autoCenterTransform="true"
scaleXFrom="0.8" scaleXTo="1"
scaleYFrom="0.8" scaleYTo="1"
/>
</s:Parallel>
<!-- gradually fade in the background -->
<s:Fade target="{bg}" startDelay="250" duration="1500"
easer="{new Power(0.2, 2)}"/>
</s:Sequence>
</s:Transition>
<s:Transition fromState="normal" toState="closed">
<s:Parallel>
<!-- fade out the panel -->
<s:Fade target="{chromeGroup}" duration="150"/>
<!-- scale out the panel -->
<s:Scale target="{chromeGroup}" duration="200"
applyChangesPostLayout="true"
autoCenterTransform="true"
scaleXFrom="1" scaleXTo="0.8"
scaleYFrom="1" scaleYTo="0.8"
/>
<!-- fade out the background -->
<s:Fade target="{bg}" duration="200"
easer="{new Power(0.2, 2)}"/>
</s:Parallel>
</s:Transition>
</s:transitions>
Mike then creates the dialog component MyDialog.mxml. He uses the "close()" method from the cancel button click handler to dismiss the dialog, but he hasn't yet hooked up the dialog to perform any actions:
<?xml version="1.0" encoding="utf-8"?>
<s:SkinnablePopUpContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
skinClass="skins.MyDialogSkin">
<s:Rect width="100%" height="100%">
<s:fill>
<s:SolidColor color="0x444444"/>
</s:fill>
</s:Rect>
<s:VGroup width="100%" height="100%">
<s:List id="list1" width="100%" height="100%">
<s:layout>
<s:VerticalLayout horizontalAlign="justify" gap="-1"/>
</s:layout>
<s:dataProvider>
<s:ArrayCollection source="{<a href="%26%2339%3BDelete%26%2339%3B%2C%20%26%2339%3BDuplicate%26%2339%3B%2C%20%26%2339%3BShare%26%2339%3B">'Delete', 'Duplicate', 'Share'</a>}"/>
</s:dataProvider>
</s:List>
<s:HGroup horizontalAlign="center" width="100%">
<s:Button label="Calcel" click="close()"/>
</s:HGroup>
</s:VGroup>
</s:SkinnablePopUpContainer>
Now in his code that needs to bring up the dialog, Mike writes some code like this:
protected function showDialog():void
{
// Create
var dialog:MyDialog = new MyDialog();
// Size to the stage (the skin relies on that)
dialog.width = systemManager.screen.width;
dialog.height = systemManager.screen.height;
dialog.open(this, true /*modal*/);
}
The final step is to hook-up the dialog to perform some actions based on the user choice. Mike notices that there are default parameters to the "close()" method and the ASDoc suggest using them when committing some data/action. He uses these parameters to pass in the action choice to close() and adds an event listener in his view code (same code that brings up the dialog):
<?xml version="1.0" encoding="utf-8"?>
<s:SkinnablePopUpContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
skinClass="skins.MyDialogSkin">
...
<s:List id="list1" width="100%" height="100%"
change="close(true, list1.selectedItem);">
...
</s:SkinnablePopUpContainer>
protected function showDialog():void
{
// Create
var dialog:MyDialog = new MyDialog();
// Size to the stage (the skin relies on that)
dialog.width = systemManager.screen.width;
dialog.height = systemManager.screen.height;
// add event listeners
dialog.addEventListener(PopUpEvent.CLOSE, dialogClose_handler);
dialog.open(this, true /*modal*/);
}
private function dialogClose_handler(event:PopUpEvent):void
{
MyDialog(event.target).removeEventListener(PopUpEvent.CLOSE,
dialogClose_handler);
if (!event.commit)
return;
switch (event.data as String)
{
case "Delete":
var index:int = list.selectedIndex;
list.dataProvider.removeItemAt(index);
list.selectedIndex = index - 1;
break;
case "Duplicate":
list.dataProvider.addItemAt(list.selectedItem,
list.selectedIndex);
break;
}
}
Vito is a Flex developer and he needs to change the animations of the ViewMenu skin. Vito creates a copy of the default ViewMenu skin as a starting point of his custom skin. Corleone then updates the skin to include transitions between the open and the close states.
State
enabled
isOpen
"closed"
true
false
"normal"
true
true
"disabled"
false
true/false
The default SkinnablePopUpContainer skins is just the same as the SkinnalbeContainerSkin with the added fade of 200ms between open and close states.
Two new events "stateChangeComplete" and "stateChangeInterrupted" events are introduced for UIComponent.
The stateChangeComplete event is dispatched when the component switches state, after any state transition animations are finished playing. If there are no state transitions, the event is dispatched immediately. If the component switches to a third state, interrupting the state transition, the event doesn't dispatch for the interrupted state.
The stateChangeInterrupted event is dispatched when the component is interrupted (by switching to state C) while playing a transition from state A to state B.
The new events allows developers to execute code after transitioning to a state, including animations, completes. This is also what SkinnablePopUpContainer uses to dsipatch "open" and "close" events and to remove itself from the PopUpManager when close transition completes.
State
enabled
isOpen
lanscape
"closed"
true
false
false
"normal"
true
true
false
"disabled"
false
true/false
false
"closedAndLandscape"
true
false
true
"normalAndLandscape"
true
true
true
"disabledAndLandscape"
false
true/false
true
In the Mobile theme, the PopUpManager styles that define default animation/effects will be turned off.
global
{
...
modalTransparency : 0;
modalTransparencyBlur : 0;
modalTransparencyDuration: 0;
modalTransparencyColor : 0;
}
/**
* Dispatched after the component has entered a new state and
* any state transition animation to that state has finished playing.
*
* The event is dispatched immediately if there's no transition playing
* between the states.
*
* If the component switches to a different state while the transition is
* underway, this event will be dispatched after the component completes the
* transition to that new state.
*
* <p>This event is only dispatched when there are one or more
* relevant listeners attached to the dispatching object.</p>
*
* @eventType mx.events.FlexEvent.STATE_CHANGE_COMPLETE
*/
<a href="Event%28name%3D%26quot%3BstateChangeComplete%26quot%3B%2C%20type%3D%26quot%3Bmx.events.FlexEvent%26quot%3B%29">Event(name="stateChangeComplete", type="mx.events.FlexEvent")</a>
/**
* Dispatched when a component interrupts a transition to its current
* state in order to switch to a new state.
*
* <p>This event is only dispatched when there are one or more
* relevant listeners attached to the dispatching object.</p>
*
* @eventType mx.events.FlexEvent.STATE_CHANGE_INTERRUPTED
*
*/
<a href="Event%28name%3D%26quot%3BstateChangeInterrupted%26quot%3B%2C%20type%3D%26quot%3Bmx.events.FlexEvent%26quot%3B%29">Event(name="stateChangeInterrupted", type="mx.events.FlexEvent")</a>
public class UIComponent
{
...
}
package spark.components
{
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched by the container when it's opened and ready for user interaction.
*
* This event is dispatched when the container switches from "closed" to "normal"
* state and the transition to that state completes.
*
* @eventType mx.events.PopUpEvent.OPEN
*
*/
<a href="Event%28name%3D%26quot%3Bopen%26quot%3B%2C%20type%3D%26quot%3Bspark.events.PopUpEvent%26quot%3B%29">Event(name="open", type="spark.events.PopUpEvent")</a>
/**
* Dispatched by the container when it's closed.
*
* This event is dispatched when the container switches from "normal" to "closed"
* state and the transition to that state completes.
*
* <p>The event provides a mechanism to pass commit information from the container to
* a listener. One typical usage scenario is building a multiple-choice dialog with a
* cancel button. When a valid option is selected, the developer closes the dialog
* with a call to the <code>SkinnablePopUpContainer.close()</code> method, passing
* <code>true</code> to the <code>commit</code> parameter and optionally passing in
* any relevant data. When the <code>SkinnablePopUpContainer</code> has completed closing,
* it will dispatch this event. Then, in the listener, the developer can check
* the <code>commit</code> parameter and perform the appropriate action. </p>
*
* @eventType mx.events.PopUpEvent.CLOSE
*
*/
<a href="Event%28name%3D%26quot%3Bclose%26quot%3B%2C%20type%3D%26quot%3Bspark.events.PopUpEvent%26quot%3B%29">Event(name="close", type="spark.events.PopUpEvent")</a>
//--------------------------------------
// States
//--------------------------------------
/**
* Closed State
*
*/
<a href="SkinState%28%26quot%3Bclosed%26quot%3B%29">SkinState("closed")</a>
/**
* The SkinnablePopUpContainer class is a SkinnableContainer that
* also acts as a pop-up.
*
* The SkinnablePopUpContainer is initially in its "closed" state and
* when it's opened it will add itself as a pop-up to the PopUpManager
* and transition to its "normal" state.
*
* <p>When using SkinnablePopUpContainer the pop-up is defined in mxml
* as a SkinnablePopUpContainer component. To show the component create
* an instance and call the <code>open()</code> method. The developers
* are responsible for the sizing and positioning of the component.
* To close the component call the <code>close()</code> method.
* If the pop-up needs to pass data back to a handler, you can add a
* listener for the <code>PopUp.CLOSE</code> event and specify the data
* in the <code>close()</code> method.</p>
*
* To define open and close animations, use a custom skin with transitions
* between the "closed" and "normal" states.
*
* @see spark.skins.spark.SkinnablePopUpContainerSkin
*/
public class SkinnablePopUpContainer extends SkinnableContainer
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*/
public function SkinnablePopUpContainer()
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// isOpen
//----------------------------------
<a href="Inspectable%28category%3D%26quot%3BGeneral%26quot%3B%2C%20defaultValue%3D%26quot%3Bfalse%26quot%3B%29">Inspectable(category="General", defaultValue="false")</a>
/**
* True when the container is open and is currently showing as a pop-up.
*
* @see #open
* @see #close
*
*/
public function get isOpen():Boolean
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* Brings up the container as a pop-up and switches the state from
* "closed" to "normal", waits till any state transitions are finished
* playing and dispatches <code>FlexEvent.OPEN</code> event.
*
* @param owner The owner of the container.
*
* @param modal Whether the container should be modal.
*
* @see #close
*
*/
public function open(owner:DisplayObjectContainer,
modal:Boolean = false):void
/**
* Changes the current state to "closed", waits till any state transitions
* are finished playing, dispatches a <code>PopUpEvent.CLOSE</code> event
* and removes the container from the PopUpManager.
*
* <p>The PopUpEvent provides a mechanism to pass commit information from
* the container to a listener. One typical usage scenario is building a
* multiple-choice dialog with a cancel button. When a valid option is
* selected, the developer closes the dialog with a call to the
* <code>SkinnablePopUpContainer.close()</code> method, passing
* <code>true</code> to the <code>commit</code> parameter and optionally
* passing in any relevant data. When the <code>SkinnablePopUpContainer</code>
* has completed closing, it will dispatch this event. Then, in the listener,
* the developer can check the <code>commit</code> parameter and perform
* the appropriate actions. </p>
*
* @param commit The value for the <code>commit</code> property of the
* <code>PopUpEvent</code> event.
* @param data The value for the <code>data</code> property for the
* <code>PopUpEvent</code> event.
*
* @see #open
*
*/
public function close(commit:Boolean = false, data:* = undefined):void
}
package spark.events
{
/**
* Dispatched by <code>SkinnablePopUpContainer</code> to single
* opening or closing.
*
* <p>When closing, this event provides a mechanism to pass
* commit information to a listener. One typical usage scenario
* is building a multiple-choice dialog with a cancel button.
* When a valid option is selected, the developer closes the dialog
* with a call to the <code>SkinnablePopUpContainer.close()</code>
* method, passing <code>true</code> to the <code>commit</code>
* parameter and optionally passing in any relevant data.
* When the <code>SkinnablePopUpContainer</code> has completed
* closing, it will dispatch this event. Then, in the listener,
* the developer can check the <code>commit</code> parameter and
* perform the appropriate action.</p>
*/
public class PopUpEvent extends Event
{
//--------------------------------------------------------------------------
//
// Class constants
//
//--------------------------------------------------------------------------
/**
* @eventType open
*/
public static const OPEN:String = "open";
/**
* @eventType close
*/
public static const CLOSE:String = "close";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @param type The event type; indicates the action that caused the event.
*
* @param bubbles Specifies whether the event can bubble up the display list
* hierarchy.
*
* @param cancelable Specifies whether the behavior associated with the event
* can be prevented.
*
* @param commit <p>Indicates whether the listener should commit the data
* from the PopUp. Only used with <code>PopUpEvent.CLOSE</code>.</p>
*
* @param data <p>The PopUp data to commit. Only used with
* <code>PopUpEvent.CLOSE</code>.</p>
*
*/
public function PopUpEvent(type:String,
bubbles:Boolean = false,
cancelable:Boolean = false,
commit:Boolean = false,
data:* = undefined)
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// commit
//----------------------------------
/**
* Whether the listener should commit the PopUp data.
* Only used with <code>PopUpEvent.CLOSE</code>.
*
*/
public var commit:Boolean;
//----------------------------------
// data
//----------------------------------
/**
* The PopUp data to commit.
* Only used with <code>PopUpEvent.CLOSE</code>.
*
*/
public var data:*;
}
}
public class FlexEvent extends Event
{
...
/**
* The <code>FlexEvent.STATE_CHANGE_COMPLETE</code> constant defines the
* value of the <code>type</code> property of the event object for a
* <code>stateChangeComplete</code> event.
*
* <p>This event will only be dispatched when there are one or more relevant
* listeners attached to the dispatching object.</p>
*
* <p>The properties of the event object have the following values:</p>
* <table class="innertable">
* <tr><th>Property</th><th>Value</th></tr>
* <tr><td><code>bubbles</code></td><td>false</td></tr>
* <tr><td><code>cancelable</code></td><td>false</td></tr>
* <tr><td><code>currentTarget</code></td><td>The Object that defines the
* event listener that handles the event. For example, if you use
* <code>myButton.addEventListener()</code> to register an event listener,
* myButton is the value of the <code>currentTarget</code>. </td></tr>
* <tr><td><code>target</code></td><td>The Object that dispatched the event;
* it is not always the Object listening for the event.
* Use the <code>currentTarget</code> property to always access the
* Object listening for the event.</td></tr>
* </table>
*
* @eventType stateChangeComplete
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public static const STATE_CHANGE_COMPLETE:String = "stateChangeComplete";
/**
* The <code>FlexEvent.STATE_CHANGE_INTERRUPTED</code> constant defines the
* value of the <code>type</code> property of the event object for a
* <code>stateChangeInterrupted</code> event.
*
* <p>This event will only be dispatched when there are one or more relevant
* listeners attached to the dispatching object.</p>
*
* <p>The properties of the event object have the following values:</p>
* <table class="innertable">
* <tr><th>Property</th><th>Value</th></tr>
* <tr><td><code>bubbles</code></td><td>false</td></tr>
* <tr><td><code>cancelable</code></td><td>false</td></tr>
* <tr><td><code>currentTarget</code></td><td>The Object that defines the
* event listener that handles the event. For example, if you use
* <code>myButton.addEventListener()</code> to register an event listener,
* myButton is the value of the <code>currentTarget</code>. </td></tr>
* <tr><td><code>target</code></td><td>The Object that dispatched the event;
* it is not always the Object listening for the event.
* Use the <code>currentTarget</code> property to always access the
* Object listening for the event.</td></tr>
* </table>
*
* @eventType stateChangeInterrupted
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public static const STATE_CHANGE_INTERRUPTED:String = "stateChangeInterrupted";
...
}
Detail features here that are to be considered for B Feature time.
See usage scenarios section above.
No
N/A
N/A
N/A
N/A
h3.
N/A
N/A
N/A