diff --git a/.gitignore b/.gitignore index 6aec5f76..50366b3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,17 @@ -Thumbs.db .* -bin-* bin +bin-* +doc* +report* html-template -*.swf -docs build.properties reports -tests +flex-config.xml +FlexUnit* +Thumbs.db +*.iml +*.ipr +*.iws +*.swf +target + diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..dea53d75 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT Open Source License + +Copyright (c) 2009-2010 the original author or authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 113c3513..5f1c6e00 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# Reflex +# Reflex Framework -Reflex is a light weight component framework being built for the Flash Platform. You can find more information at [reflex.io](http://reflex.io). +The Reflex Framework is an ActionScript 3 component framework for the Flash Platform. You can find more information at [reflexplatform.com](http://reflexplatform.com). diff --git a/assets/skin/cursors.fla b/assets/skin/cursors.fla deleted file mode 100644 index 7be29c53..00000000 Binary files a/assets/skin/cursors.fla and /dev/null differ diff --git a/assets/skin/skins-cs5.fla b/assets/skin/skins-cs5.fla deleted file mode 100644 index f5ff0208..00000000 Binary files a/assets/skin/skins-cs5.fla and /dev/null differ diff --git a/assets/skin/skins.fla b/assets/skin/skins.fla deleted file mode 100644 index c2f83311..00000000 Binary files a/assets/skin/skins.fla and /dev/null differ diff --git a/build.xml b/build.xml index 0f91e145..aa065cf4 100644 --- a/build.xml +++ b/build.xml @@ -1,5 +1,5 @@ - + @@ -47,6 +47,7 @@ + http://rx.reflex.io/2010 true @@ -59,9 +60,13 @@ - + + + + + @@ -73,8 +78,9 @@ + - + diff --git a/example.properties b/example.properties index 03a8e31f..66e27aec 100644 --- a/example.properties +++ b/example.properties @@ -3,7 +3,7 @@ # Modify these path values to reflect paths on your system ################################################################### -library.name = Reflex +library.name = ReflexFramework # ----------------------------------------------------------------- # Flex SDK Properties @@ -27,7 +27,7 @@ docs.templates = ${flex-sdk.dir}/asdoc/templates link-report.name = link-report link-report.dir = ${docs.dir} -reports.dir = /Users/mac/Reflex/ReflexTests/reports +reports.dir = /Users/mac/Reflex/reflex-framework/reports tests.dir = ${basedir}/tests # ----------------------------------------------------------------- diff --git a/libs/flight-framework.swc b/libs/flight-framework.swc deleted file mode 100644 index ca61f027..00000000 Binary files a/libs/flight-framework.swc and /dev/null differ diff --git a/libs/skins.swc b/libs/skins.swc deleted file mode 100644 index 74b8eff8..00000000 Binary files a/libs/skins.swc and /dev/null differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..dff4ab95 --- /dev/null +++ b/pom.xml @@ -0,0 +1,134 @@ + + + 4.0.0 + reflex + reflex-framework + 1.0-SNAPSHOT + swc + reflex framework + + src + tests + + + org.sonatype.flexmojos + flexmojos-maven-plugin + 4.0-SNAPSHOT + true + + false + + http://rx.reflex.io/2010 + + + + http://rx.reflex.io/2010 + ${project.build.sourceDirectory}/manifest.xml + + + true + true + true + + Binding + EventListener + LayoutProperty + Commit + + + + + site + + asdoc + + + + + + maven-site-plugin + 3.0-beta-3 + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.2 + + index + + + + com.adobe.ac + flex-pmd-maven-plugin + 1.2 + + + com.adobe.ac + flex-pmd-cpd-maven-plugin + 1.2 + + 25 + + + + com.adobe.ac + flex-pmd-metrics-maven-plugin + 1.2 + + + + + + + + + com.adobe.flex.framework + flex-framework + 4.5.0.17689 + pom + + + com.adobe.flexunit + flexunit + 4.0-rc-1 + swc + test + + + + + flexmojos-repository + http://repository.sonatype.org/content/groups/flexgroup/ + + true + + + true + + + + + + flexmojos-plugin-repository + http://repository.sonatype.org/content/groups/flexgroup/ + + true + + + true + + + + flexpmd.opensource.adobe + + true + + + true + + FlexPMD repository on opensource.adobe.com + http://opensource.adobe.com/svn/opensource/flexpmd/maven-repository/release/ + + + diff --git a/src/FlexClasses.as b/src/FlexClasses.as deleted file mode 100644 index 6106e657..00000000 --- a/src/FlexClasses.as +++ /dev/null @@ -1,45 +0,0 @@ -package -{ - /** - * @private - * This class is used to link additional classes into reflex.swc - * beyond those that are found by dependecy analysis starting from the - * classes specified in manifest.xml. For example, compiler-required - * references to Flex classes for use of an MXML workflow and the [Bindable] - * metadata tag in AS3-only projects - many of these classes are never - * actually used, not even in the auto-generated code. - */ - internal class FlexClasses - { - import mx.core.IMXMLObject; IMXMLObject; - import mx.core.IFlexModuleFactory; IFlexModuleFactory; - - //import mx.collections.IList; IList; - - // binding references for use of the [Bindable] metadata tag - import mx.binding.BindingManager; BindingManager; - import mx.core.IPropertyChangeNotifier; IPropertyChangeNotifier; - import mx.utils.ObjectProxy; ObjectProxy; - import mx.utils.UIDUtil; UIDUtil; - - // mx core references for use of MXML - import mx.styles.StyleManager; StyleManager; - import mx.core.ClassFactory; ClassFactory; - import mx.core.DeferredInstanceFromClass; DeferredInstanceFromClass; - import mx.core.DeferredInstanceFromFunction; DeferredInstanceFromFunction; - - // binding references for use of the curly-brace binding in MXML - import mx.binding.IWatcherSetupUtil; IWatcherSetupUtil; - // Required class for Flash Buider 4 binding in AS3 projects - import mx.binding.IBindingClient; IBindingClient; - import mx.binding.IWatcherSetupUtil2; IWatcherSetupUtil2; - import mx.binding.ArrayElementWatcher; ArrayElementWatcher; - import mx.binding.FunctionReturnWatcher; FunctionReturnWatcher; - import mx.binding.PropertyWatcher; PropertyWatcher; - import mx.binding.RepeaterComponentWatcher; RepeaterComponentWatcher; - import mx.binding.RepeaterItemWatcher; RepeaterItemWatcher; - import mx.binding.StaticPropertyWatcher; StaticPropertyWatcher; - import mx.binding.XMLWatcher; XMLWatcher; - } - -} diff --git a/src/manifest.xml b/src/manifest.xml index b9a95818..b0de15fc 100644 --- a/src/manifest.xml +++ b/src/manifest.xml @@ -2,38 +2,46 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + - - - - + - + + + + @@ -42,20 +50,63 @@ - --> + + + + + + + + + + - + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - + + + + \ No newline at end of file diff --git a/src/mx/binding/ArrayElementWatcher.as b/src/mx/binding/ArrayElementWatcher.as new file mode 100644 index 00000000..f7a381d9 --- /dev/null +++ b/src/mx/binding/ArrayElementWatcher.as @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import mx.core.mx_internal; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + */ +public class ArrayElementWatcher extends Watcher +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * Constructor + */ + public function ArrayElementWatcher(document:Object, + accessorFunc:Function, + listeners:Array) + { + super(listeners); + + this.document = document; + this.accessorFunc = accessorFunc; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var document:Object; + + /** + * @private + */ + private var accessorFunc:Function; + + /** + * @private + */ + public var arrayWatcher:Watcher; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function updateParent(parent:Object):void + { + if (arrayWatcher.value != null) + { + wrapUpdate(function():void + { + value = arrayWatcher.value[accessorFunc.apply(document)]; + updateChildren(); + }); + } + } + + /** + * @private + */ + override protected function shallowClone():Watcher + { + return new ArrayElementWatcher(document, accessorFunc, listeners); + } +} + +} diff --git a/src/mx/binding/BindabilityInfo.as b/src/mx/binding/BindabilityInfo.as new file mode 100644 index 00000000..d1c4778f --- /dev/null +++ b/src/mx/binding/BindabilityInfo.as @@ -0,0 +1,272 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import mx.events.PropertyChangeEvent; + +[ExcludeClass] + +/** + * @private + * Bindability information for children (properties or methods) + * of a given class, based on the describeType() structure for that class. + */ +public class BindabilityInfo +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * Name of [Bindable] metadata. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const BINDABLE:String = "Bindable"; + + /** + * Name of [Managed] metadata. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const MANAGED:String = "Managed"; + + /** + * Name of [ChangeEvent] metadata. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const CHANGE_EVENT:String = "ChangeEvent"; + + /** + * Name of [NonCommittingChangeEvent] metadata. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const NON_COMMITTING_CHANGE_EVENT:String = + "NonCommittingChangeEvent"; + + /** + * Name of describeType() element. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ACCESSOR:String = "accessor"; + + /** + * Name of describeType() element. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const METHOD:String = "method"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function BindabilityInfo(typeDescription:XML) + { + super(); + + this.typeDescription = typeDescription; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var typeDescription:XML; + + /** + * @private + * event name -> true + */ + private var classChangeEvents:Object; + + /** + * @private + * child name -> { event name -> true } + */ + private var childChangeEvents:Object = {}; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * Object containing { eventName: true } for each change event + * (class- or child-level) that applies to the specified child. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getChangeEvents(childName:String):Object + { + var changeEvents:Object = childChangeEvents[childName]; + + if (!changeEvents) + { + // Seed with class-level events. + changeEvents = copyProps(getClassChangeEvents(), {}); + + // Get child-specific events. + var childDesc:XMLList = + typeDescription.accessor.(@name == childName) + + typeDescription.method.(@name == childName); + + var numChildren:int = childDesc.length(); + + if (numChildren == 0) + { + // we've been asked for events on an unknown property + if (!typeDescription.@dynamic) + { + trace("warning: no describeType entry for '" + + childName + "' on non-dynamic type '" + + typeDescription.@name + "'"); + } + } + else + { + if (numChildren > 1) + { + trace("warning: multiple describeType entries for '" + + childName + "' on type '" + typeDescription.@name + + "':\n" + childDesc); + } + + addBindabilityEvents(childDesc.metadata, changeEvents); + } + + childChangeEvents[childName] = changeEvents; + } + + return changeEvents; + } + + /** + * @private + * Build or return cached class change events object. + */ + private function getClassChangeEvents():Object + { + if (!classChangeEvents) + { + classChangeEvents = {}; + + addBindabilityEvents(typeDescription.metadata, classChangeEvents); + + // Class-level [Managed] means all properties + // dispatch propertyChange. + if (typeDescription.metadata.(@name == MANAGED).length() > 0) + { + classChangeEvents[PropertyChangeEvent.PROPERTY_CHANGE] = true; + } + } + + return classChangeEvents; + } + + /** + * @private + */ + private function addBindabilityEvents(metadata:XMLList, + eventListObj:Object):void + { + addChangeEvents(metadata.(@name == BINDABLE), eventListObj, true); + addChangeEvents(metadata.(@name == CHANGE_EVENT), eventListObj, true); + addChangeEvents(metadata.(@name == NON_COMMITTING_CHANGE_EVENT), + eventListObj, false); + } + + /** + * @private + * Transfer change events from a list of change-event-carrying metadata + * to an event list object. + * Note: metadata's first arg value is assumed to be change event name. + */ + private function addChangeEvents(metadata:XMLList, eventListObj:Object, isCommit:Boolean):void + { + for each (var md:XML in metadata) + { + var arg:XMLList = md.arg; + if (arg.length() > 0) + { + var eventName:String = arg[0].@value; + eventListObj[eventName] = isCommit; + } + else + { + trace("warning: unconverted Bindable metadata in class '" + + typeDescription.@name + "'"); + } + } + } + + /** + * @private + * Copy properties from one object to another. + */ + private function copyProps(from:Object, to:Object):Object + { + for (var propName:String in from) + { + to[propName] = from[propName]; + } + + return to; + } +} + +} diff --git a/src/mx/binding/Binding.as b/src/mx/binding/Binding.as new file mode 100644 index 00000000..06088da8 --- /dev/null +++ b/src/mx/binding/Binding.as @@ -0,0 +1,520 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import mx.collections.errors.ItemPendingError; +import mx.core.mx_internal; +import flash.utils.Dictionary; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + */ +public class Binding +{ + include "../core/Version.as"; + + // Certain errors are normal during binding execution, so we swallow them. + // 1507 - invalid null argument + // 2005 - argument error (null gets converted to 0) + mx_internal static var allowedErrors:Object = generateAllowedErrors(); + mx_internal static function generateAllowedErrors():Object + { + var o:Object = {}; + o[1507] = 1; + o[2005] = 1; + return o; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Create a Binding object + * + * @param document The document that is the target of all of this work. + * + * @param srcFunc The function that returns us the value + * to use in this Binding. + * + * @param destFunc The function that will take a value + * and assign it to the destination. + * + * @param destString The destination represented as a String. + * We can then tell the ValidationManager to validate this field. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function Binding(document:Object, srcFunc:Function, + destFunc:Function, destString:String, + srcString:String = null) + { + super(); + + this.document = document; + this.srcFunc = srcFunc; + this.destFunc = destFunc; + this.destString = destString; + this.srcString = srcString; + + if (this.srcFunc == null) + { + this.srcFunc = defaultSrcFunc; + } + + if (this.destFunc == null) + { + this.destFunc = defaultDestFunc; + } + + _isEnabled = true; + isExecuting = false; + isHandlingEvent = false; + hasHadValue = false; + uiComponentWatcher = -1; + + BindingManager.addBinding(document, destString, this); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Internal storage for isEnabled property. + */ + mx_internal var _isEnabled:Boolean; + + /** + * @private + * Indicates that a Binding is enabled. + * Used to disable bindings. + */ + mx_internal function get isEnabled():Boolean + { + return _isEnabled; + } + + /** + * @private + */ + mx_internal function set isEnabled(value:Boolean):void + { + _isEnabled = value; + + if (value) + { + processDisabledRequests(); + } + } + + /** + * @private + * Indicates that a Binding is executing. + * Used to prevent circular bindings from causing infinite loops. + */ + mx_internal var isExecuting:Boolean; + + /** + * @private + * Indicates that the binding is currently handling an event. + * Used to prevent us from infinitely causing an event + * that re-executes the the binding. + */ + mx_internal var isHandlingEvent:Boolean; + + /** + * @private + * Queue of watchers that fired while we were disabled. + * We will resynch with our binding if isEnabled is set to true + * and one or more of our watchers fired while we were disabled. + */ + mx_internal var disabledRequests:Dictionary; + + /** + * @private + * True as soon as a non-null or non-empty-string value has been used. + * We don't auto-validate until this is true + */ + private var hasHadValue:Boolean; + + /** + * @private + * This is no longer used in Flex 3.0, but it is required to load + * Flex 2.0.0 and Flex 2.0.1 modules. + */ + public var uiComponentWatcher:int; + + /** + * @private + * It's possible that there is a two-way binding set up, in which case + * we'll do a rudimentary optimization by not executing ourselves + * if our counterpart is already executing. + */ + public var twoWayCounterpart:Binding; + + /** + * @private + * If there is a twoWayCounterpart, hasHadValue is false, and + * isTwoWayPrimary is true, then the twoWayCounterpart will be + * executed first. + */ + public var isTwoWayPrimary:Boolean; + + /** + * @private + * True if a wrapped function call does not throw an error. This is used by + * innerExecute() to tell if the srcFunc completed successfully. + */ + private var wrappedFunctionSuccessful:Boolean; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * All Bindings hang off of a document for now, + * but really it's just the root of where these functions live. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + mx_internal var document:Object; + + /** + * The function that will return us the value. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + mx_internal var srcFunc:Function; + + /** + * The function that takes the value and assigns it. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + mx_internal var destFunc:Function; + + /** + * The destination represented as a String. + * This will be used so we can signal validation on a field. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + mx_internal var destString:String; + + /** + * The source represented as a String. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4 + */ + mx_internal var srcString:String; + + /** + * @private + * Used to suppress calls to destFunc when incoming value is either + * a) an XML node identical to the previously assigned XML node, or + * b) an XMLList containing the identical node sequence as the previously assigned XMLList + */ + private var lastValue:Object; + + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + private function defaultDestFunc(value:Object):void + { + var chain:Array = destString.split("."); + var element:Object = document; + var i:uint = 0; + + if (chain[0] == "this") + { + i++; + } + + while (i < (chain.length - 1)) + { + element = element[chain[i++]]; + } + + element[chain[i]] = value; + } + + private function defaultSrcFunc():Object + { + return document[srcString]; + } + + /** + * Execute the binding. + * Call the source function and get the value we'll use. + * Then call the destination function passing the value as an argument. + * Finally try to validate the destination. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function execute(o:Object = null):void + { + if (!isEnabled) + { + if (o != null) + { + registerDisabledExecute(o); + } + return; + } + + if (twoWayCounterpart && !twoWayCounterpart.hasHadValue && twoWayCounterpart.isTwoWayPrimary) + { + twoWayCounterpart.execute(); + hasHadValue = true; + return; + } + + if (isExecuting || (twoWayCounterpart && twoWayCounterpart.isExecuting)) + { + // If there is a twoWayCounterpart already executing, that means that it is + // assigning something of value so even though we won't execute we should be + // sure to mark ourselves as having had a value so that future executions will + // be correct. If isExecuting is true but we re-entered, that means we + // clearly had a value so setting hasHadValue is safe. + hasHadValue = true; + return; + } + + try + { + isExecuting = true; + wrapFunctionCall(this, innerExecute, o); + } + catch(error:Error) + { + if (allowedErrors[error.errorID] == null) + throw error; + } + finally + { + isExecuting = false; + } + } + + /** + * @private + * Take note of any execute request that occur when we are disabled. + */ + private function registerDisabledExecute(o:Object):void + { + if (o != null) + { + disabledRequests = (disabledRequests != null) ? disabledRequests : + new Dictionary(true); + + disabledRequests[o] = true; + } + } + + /** + * @private + * Resynch with any watchers that may have updated while we were disabled. + */ + private function processDisabledRequests():void + { + if (disabledRequests != null) + { + for (var key:Object in disabledRequests) + { + execute(key); + } + + disabledRequests = null; + } + } + + + /** + * @private + * Note: use of this wrapper needs to be reexamined. Currently there's at least one situation where a + * wrapped function invokes another wrapped function, which is unnecessary (i.e., only the inner function + * will throw), and also risks future errors due to the 'wrappedFunctionSuccessful' member variable + * being stepped on. Leaving alone for now to minimize pre-GMC volatility, but should be revisited for + * an early dot release. + * Also note that the set of suppressed error codes below is repeated verbatim in Watcher.wrapUpdate. + * These need to be consolidated and the motivations for each need to be documented. + */ + protected function wrapFunctionCall(thisArg:Object, wrappedFunction:Function, object:Object = null, ...args):Object + { + wrappedFunctionSuccessful = false; + + try + { + var result:Object = wrappedFunction.apply(thisArg, args); + wrappedFunctionSuccessful = true; + return result; + } + catch(itemPendingError:ItemPendingError) + { + itemPendingError.addResponder(new EvalBindingResponder(this, object)); + if (BindingManager.debugDestinationStrings[destString]) + { + trace("Binding: destString = " + destString + ", error = " + itemPendingError); + } + } + catch(rangeError:RangeError) + { + if (BindingManager.debugDestinationStrings[destString]) + { + trace("Binding: destString = " + destString + ", error = " + rangeError); + } + } + catch(error:Error) + { + // Certain errors are normal when executing a srcFunc or destFunc, + // so we swallow them: + // Error #1006: Call attempted on an object that is not a function. + // Error #1009: null has no properties. + // Error #1010: undefined has no properties. + // Error #1055: - has no properties. + // Error #1069: Property - not found on - and there is no default value + // We allow any other errors to be thrown. + if ((error.errorID != 1006) && + (error.errorID != 1009) && + (error.errorID != 1010) && + (error.errorID != 1055) && + (error.errorID != 1069)) + { + throw error; + } + else + { + if (BindingManager.debugDestinationStrings[destString]) + { + trace("Binding: destString = " + destString + ", error = " + error); + } + } + } + + return null; + } + + /** + * @private + * true iff XMLLists x and y contain the same node sequence. + */ + private function nodeSeqEqual(x:XMLList, y:XMLList):Boolean + { + var n:uint = x.length(); + if (n == y.length()) + { + for (var i:uint = 0; i < n && x[i] === y[i]; i++) + { + } + return i == n; + } + else + { + return false; + } + } + + /** + * @private + */ + private function innerExecute():void + { + var value:Object = wrapFunctionCall(document, srcFunc); + + if (BindingManager.debugDestinationStrings[destString]) + { + trace("Binding: destString = " + destString + ", srcFunc result = " + value); + } + + if (hasHadValue || wrappedFunctionSuccessful) + { + // Suppress binding assignments on non-simple XML: identical single nodes, or + // lists over identical node sequences. + // Note: outer tests are inline for efficiency + if (!(lastValue is XML && lastValue.hasComplexContent() && lastValue === value) && + !(lastValue is XMLList && lastValue.hasComplexContent() && value is XMLList && + nodeSeqEqual(lastValue as XMLList, value as XMLList))) + { + destFunc.call(document, value); + + // Note: state is not updated if destFunc throws + lastValue = value; + hasHadValue = true; + } + } + } + + /** + * This function is called when one of this binding's watchers + * detects a property change. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function watcherFired(commitEvent:Boolean, cloneIndex:int):void + { + if (isHandlingEvent) + return; + + try + { + isHandlingEvent = true; + execute(cloneIndex); + } + finally + { + isHandlingEvent = false; + } + } +} + +} diff --git a/src/mx/binding/BindingManager.as b/src/mx/binding/BindingManager.as new file mode 100644 index 00000000..1407ac1f --- /dev/null +++ b/src/mx/binding/BindingManager.as @@ -0,0 +1,265 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import mx.core.mx_internal; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + */ +public class BindingManager +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Store a Binding for the destination relative to the passed in document. + * We don't hold a list of bindings per destination + * even though it is possible to have multiple. + * The reason is that when we refresh the last binding will + * always win anyway, so why execute the ones that will lose. + * + * @param document The document that this binding relates to. + * + * @param destStr The destination field of this binding. + * + * @param b The binding itself. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function addBinding(document:Object, destStr:String, + b:Binding):void + { + if (!document._bindingsByDestination) + { + document._bindingsByDestination = {}; + document._bindingsBeginWithWord = {}; + } + + document._bindingsByDestination[destStr] = b; + document._bindingsBeginWithWord[getFirstWord(destStr)] = true; + } + + /** + * Set isEnabled for all bindings associated with a document. + * + * @param document The document that contains the bindings. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function setEnabled(document:Object, isEnabled:Boolean):void + { + if ((document is IBindingClient) && document._bindings) + { + var bindings:Array = document._bindings as Array; + + for (var i:uint = 0; i < bindings.length; i++) + { + var binding:Binding = bindings[i]; + binding.isEnabled = isEnabled; + } + } + } + + /** + * Execute all bindings that bind into the specified object. + * + * @param document The document that this binding relates to. + * + * @param destStr The destination field that needs to be refreshed. + * + * @param destObj The actual destination object + * (used for RepeatableBinding). + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function executeBindings(document:Object, + destStr:String, + destObj:Object):void + { + // Bail if this method is accidentally called with an empty or + // null destination string. Otherwise, all bindings will + // be executed! + if (!destStr || (destStr == "")) + return; + + // Flex 3 documents will implement IBindingClient when using + // data binding. Flex 2.X document's will have a non-null + // public _bindingsByDestination variable. + if (document && + (document is IBindingClient || document.hasOwnProperty("_bindingsByDestination")) && + document._bindingsByDestination && + document._bindingsBeginWithWord[getFirstWord(destStr)]) + { + for (var binding:String in document._bindingsByDestination) + { + // If we have been told to execute bindings into a UIComponent + // or Repeater with id "a", we want to execute all bindings + // whose destination strings look like "a", "a.b", "a.b.c", + // "a['b']", etc. but not "aa.bb", "b.a", "b.a.c", "b['a']", + // etc. + + // Currently, the only way that this method is used by the + // framework is when the destStr passed in is the id of a + // UIComponent or Repeater. + + // However, advanced users can call + // BindingManager.executeBindings() and pass a compound + // destStr like "a.b.c". This has a gotcha: If they have + // written tags with destination attributes + // like "a['b'].c.d" rather than "a.b.c.d", these will + // not get executed. They should pass the same form of + // destStr to executeBindings() as they used in their + // Binding tags. + + if (binding.charAt(0) == destStr.charAt(0)) + { + if (binding.indexOf(destStr + ".") == 0 || + binding.indexOf(destStr + "[") == 0 || + binding == destStr) + { + // If this is a RepeatableBindings, execute it on just the + // specified object, not on all its repeated siblings. For + // example, if we are instantiating o[2][3], we don't want to also + // refresh o[0][0], o[0][1], etc. + document._bindingsByDestination[binding].execute(destObj); + } + } + } + } + } + + /** + * Enable or disable all bindings that bind into the specified object + * and match the input destStr. + * + * @param document The document that this binding relates to. + * + * @param destStr The destination field that needs to be refreshed. + * + * @param enable If true enables the specified binding(s), otherwise + * disables. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2/5 + * @productversion Flex 4.5 + */ + public static function enableBindings(document:Object, + destStr:String, + enable:Boolean = true):void + { + // See implementation comments above for executeBindings as this + // method follows the same logic. + + if (!destStr || (destStr == "")) + return; + + if (document && + (document is IBindingClient || document.hasOwnProperty("_bindingsByDestination")) && + document._bindingsByDestination && + document._bindingsBeginWithWord[getFirstWord(destStr)]) + { + for (var binding:String in document._bindingsByDestination) + { + if (binding.charAt(0) == destStr.charAt(0)) + { + if (binding.indexOf(destStr + ".") == 0 || + binding.indexOf(destStr + "[") == 0 || + binding == destStr) + { + document._bindingsByDestination[binding].isEnabled = enable; + } + } + } + } + } + + /** + * @private + */ + private static function getFirstWord(destStr:String):String + { + // indexPeriod and indexBracket will be equal only if they + // both are -1. + var indexPeriod:int = destStr.indexOf("."); + var indexBracket:int = destStr.indexOf("["); + if (indexPeriod == indexBracket) + return destStr; + + // Get the characters leading up to the first period or + // bracket. + var minIndex:int = Math.min(indexPeriod, indexBracket); + if (minIndex == -1) + minIndex = Math.max(indexPeriod, indexBracket); + + return destStr.substr(0, minIndex); + } + + /** + * @private + */ + internal static var debugDestinationStrings:Object = {}; + + /** + * Enables debugging output for the Binding or Bindings with a matching + * destination string. + * + * @param destinationString The Binding's destination string. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function debugBinding(destinationString:String):void + { + debugDestinationStrings[destinationString] = true; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * BindingManager has only static methods. + * We don't create instances of BindingManager. + */ + public function BindingManager() + { + super(); + } +} + +} diff --git a/src/mx/binding/EvalBindingResponder.as b/src/mx/binding/EvalBindingResponder.as new file mode 100644 index 00000000..65583e38 --- /dev/null +++ b/src/mx/binding/EvalBindingResponder.as @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import mx.rpc.IResponder; + +[ExcludeClass] + +/** + * @private + * This responder is a fallback in case the set or get methods + * we are invoking to implement this binding do not properly + * catch the ItemPendingError. There may be some issues with + * leaving this in long term as we are not handling the + * case where this binding is executed multiple times in + * rapid succession (and thus piling up responders) and + * also not dealing with a potential stale item responder. + */ +public class EvalBindingResponder implements IResponder +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * Constructor. + */ + public function EvalBindingResponder(binding:Binding, object:Object) + { + super(); + + this.binding = binding; + this.object = object; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var binding:Binding; + + /** + * @private + */ + private var object:Object; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public function result(data:Object):void + { + binding.execute(object); + } + + /** + * @private + */ + public function fault(data:Object):void + { + // skip it + } +} + +} diff --git a/src/mx/binding/FunctionReturnWatcher.as b/src/mx/binding/FunctionReturnWatcher.as new file mode 100644 index 00000000..68327c3c --- /dev/null +++ b/src/mx/binding/FunctionReturnWatcher.as @@ -0,0 +1,291 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import flash.events.Event; +import flash.events.IEventDispatcher; +import mx.core.EventPriority; +import mx.core.mx_internal; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + */ +public class FunctionReturnWatcher extends Watcher +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * Constructor. + */ + public function FunctionReturnWatcher(functionName:String, + document:Object, + parameterFunction:Function, + events:Object, + listeners:Array, + functionGetter:Function = null, + isStyle:Boolean = false) + { + super(listeners); + + this.functionName = functionName; + this.document = document; + this.parameterFunction = parameterFunction; + this.events = events; + this.functionGetter = functionGetter; + this.isStyle = isStyle; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * The name of the property, used to actually get the property + * and for comparison in propertyChanged events. + */ + private var functionName:String; + + /** + * @private + * The document is what we need to use to execute the parameter function. + */ + private var document:Object; + + /** + * @private + * The function that will give us the parameters for calling the function. + */ + private var parameterFunction:Function; + + /** + * @private + * The events that indicate the property has changed. + */ + private var events:Object; + + /** + * @private + * The parent object of this function. + */ + private var parentObj:Object; + + /** + * @private + * The watcher holding onto the parent object. + */ + public var parentWatcher:Watcher; + + /** + * Storage for the functionGetter property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var functionGetter:Function; + + /** + * Storage for the isStyle property. This will be true, when + * watching a function marked with [Bindable(style="true")]. For + * example, UIComponent.getStyle(). + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4 + */ + private var isStyle:Boolean; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function updateParent(parent:Object):void + { + if (!(parent is Watcher)) + setupParentObj(parent); + + else if (parent == parentWatcher) + setupParentObj(parentWatcher.value); + + updateFunctionReturn(); + } + + /** + * @private + */ + override protected function shallowClone():Watcher + { + var clone:FunctionReturnWatcher = new FunctionReturnWatcher(functionName, + document, + parameterFunction, + events, + listeners, + functionGetter); + + return clone; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Get the new return value of the function. + */ + public function updateFunctionReturn():void + { + wrapUpdate(function():void + { + if (functionGetter != null) + { + value = functionGetter(functionName).apply(parentObj, + parameterFunction.apply(document)); + } + else + { + value = parentObj[functionName].apply(parentObj, + parameterFunction.apply(document)); + } + + updateChildren(); + }); + } + + /** + * @private + */ + private function setupParentObj(newParent:Object):void + { + var eventDispatcher:IEventDispatcher; + var eventName:String; + + // Remove listeners from the old "watched" object. + if (parentObj != null && + parentObj is IEventDispatcher) + { + eventDispatcher = parentObj as IEventDispatcher; + + // events can be null when watching a function marked with + // [Bindable(style="true")]. + if (events != null) + { + for (eventName in events) + { + if (eventName != "__NoChangeEvent__") + { + eventDispatcher.removeEventListener(eventName, eventHandler); + } + } + } + + if (isStyle) + { + // For example, if the data binding expression is + // {getStyle("color")}, the eventName will be + // "colorChanged". + eventName = parameterFunction.apply(document) + "Changed"; + eventDispatcher.removeEventListener(eventName, eventHandler); + eventDispatcher.removeEventListener("allStylesChanged", eventHandler); + } + } + + parentObj = newParent; + + // Add listeners the new "watched" object. + if (parentObj != null && + parentObj is IEventDispatcher) + { + eventDispatcher = parentObj as IEventDispatcher; + + // events can be null when watching a function marked with + // [Bindable(style="true")]. + if (events != null) + { + for (eventName in events) + { + if (eventName != "__NoChangeEvent__") + { + eventDispatcher.addEventListener(eventName, eventHandler, + false, + EventPriority.BINDING, + true); + } + } + } + + if (isStyle) + { + // For example, if the data binding expression is + // {getStyle("color")}, the eventName will be + // "colorChanged". + eventName = parameterFunction.apply(document) + "Changed"; + eventDispatcher.addEventListener(eventName, eventHandler, false, + EventPriority.BINDING, true); + eventDispatcher.addEventListener("allStylesChanged", eventHandler, false, + EventPriority.BINDING, true); + } + } + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public function eventHandler(event:Event):void + { + updateFunctionReturn(); + + // events can be null when watching a function marked with + // [Bindable(style="true")]. + if (events != null) + { + notifyListeners(events[event.type]); + } + + if (isStyle) + { + notifyListeners(true); + } + } +} + +} diff --git a/src/mx/binding/IBindingClient.as b/src/mx/binding/IBindingClient.as new file mode 100644 index 00000000..37113236 --- /dev/null +++ b/src/mx/binding/IBindingClient.as @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +/** + * This is used to mark documents, which have data bindings. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IBindingClient +{ +} + +} diff --git a/src/mx/binding/IWatcherSetupUtil2.as b/src/mx/binding/IWatcherSetupUtil2.as new file mode 100644 index 00000000..82501d4b --- /dev/null +++ b/src/mx/binding/IWatcherSetupUtil2.as @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +[ExcludeClass] + +/** + * @private + * This interface is used internally by Flex 4 to enable data binding + * to static private variables and properties. + * Flex 3 used the IWatcherSetupUtil interface. + */ +public interface IWatcherSetupUtil2 +{ + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + function setup(target:Object, propertyGetter:Function, + staticPropertyGetter:Function, + bindings:Array, watchers:Array):void; +} + +} diff --git a/src/mx/binding/PropertyWatcher.as b/src/mx/binding/PropertyWatcher.as new file mode 100644 index 00000000..a4ec81c2 --- /dev/null +++ b/src/mx/binding/PropertyWatcher.as @@ -0,0 +1,381 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import flash.events.Event; +import flash.events.IEventDispatcher; +import flash.utils.getQualifiedClassName; +import mx.core.EventPriority; +import mx.core.mx_internal; +import mx.events.PropertyChangeEvent; +import mx.utils.DescribeTypeCache; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + */ +public class PropertyWatcher extends Watcher +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Create a PropertyWatcher + * + * @param prop The name of the property to watch. + * @param event The event type that indicates the property has changed. + * @param listeners The binding objects that are listening to this Watcher. + * @param propertyGetter A helper function used to access non-public variables. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function PropertyWatcher(propertyName:String, + events:Object, + listeners:Array, + propertyGetter:Function = null) + { + super(listeners); + + _propertyName = propertyName; + this.events = events; + this.propertyGetter = propertyGetter; + useRTTI = !events; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * The parent object of this property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var parentObj:Object; + + /** + * The events that indicate the property has changed + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + protected var events:Object; + + /** + * Storage for the propertyGetter property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + protected var propertyGetter:Function; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // propertyName + //---------------------------------- + + /** + * Storage for the propertyName property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var _propertyName:String; + + /** + * The name of the property this Watcher is watching. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get propertyName():String + { + return _propertyName; + } + + //---------------------------------- + // useRTTI + //---------------------------------- + + /** + * If compiler can't determine bindability from static type, + * use RTTI on runtime values. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var useRTTI:Boolean; + + //-------------------------------------------------------------------------- + // + // Overridden methods: Watcher + // + //-------------------------------------------------------------------------- + + /** + * If the parent has changed we need to update ourselves + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + override public function updateParent(parent:Object):void + { + if (parentObj && parentObj is IEventDispatcher) + { + for (var eventType:String in events) + { + parentObj.removeEventListener(eventType, eventHandler); + } + } + + if (parent is Watcher) + parentObj = parent.value; + else + parentObj = parent; + + if (parentObj) + { + if (useRTTI) + { + // Use RTTI to ensure that parentObj is an IEventDispatcher, + // and that bindability metadata exists + // for parentObj[_propertyName]. + + events = {}; + + if (parentObj is IEventDispatcher) + { + var info:BindabilityInfo = + DescribeTypeCache.describeType(parentObj). + bindabilityInfo; + + events = info.getChangeEvents(_propertyName); + + if (objectIsEmpty(events)) + { + trace("warning: unable to bind to property '" + + _propertyName + "' on class '" + + getQualifiedClassName(parentObj) + "'"); + } + else + { + addParentEventListeners(); + } + } + else + { + trace("warning: unable to bind to property '" + + _propertyName + "' on class '" + + getQualifiedClassName(parentObj) + + "' (class is not an IEventDispatcher)"); + } + } + else + { + // useRTTI == false implies that the compiler + // has provided us with a list of change events. + // NOTE: this normally also implies that parentObj + // is guaranteed to implement IEventDispatcher. + // The guard below is necessitated by Proxy cases, + // which provide blanket bindability information + // on properties which are not strongly typed, + // and so could accept values that do not implement + // IEventDispatcher. + // In these cases, correct binding behavior depends on + // the Proxy implementation providing after-the-fact + // bindability by wrapping assigned values and attaching + // event listeners at that point. + // Here we can only fail silently. + + if (parentObj is IEventDispatcher) + addParentEventListeners(); + } + } + + // Now get our property. + wrapUpdate(updateProperty); + } + + /** + * @private + */ + override protected function shallowClone():Watcher + { + var clone:PropertyWatcher = new PropertyWatcher(_propertyName, + events, + listeners, + propertyGetter); + + return clone; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function addParentEventListeners():void + { + for (var eventType:String in events) + { + if (eventType != "__NoChangeEvent__") + { + parentObj.addEventListener( + eventType, eventHandler, false, EventPriority.BINDING, true); + } + } + } + + /** + * @private + */ + private function traceInfo():String + { + return "Watcher(" + getQualifiedClassName(parentObj) + "." + + _propertyName + "): events = [" + + eventNamesToString() + (useRTTI ? "] (RTTI)" : "]"); + } + + /** + * @private + */ + private function eventNamesToString():String + { + var s:String = " "; + + for (var ev:String in events) + { + s += ev + " "; + } + + return s; + } + + /** + * @private + */ + private function objectIsEmpty(o:Object):Boolean + { + for (var p:String in o) + { + return false; + } + return true; + } + + /** + * Gets the actual property then updates + * the Watcher's children appropriately. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private function updateProperty():void + { + if (parentObj) + { + if (_propertyName == "this") + { + value = parentObj; + } + else + { + if (propertyGetter != null) + { + value = propertyGetter.apply(parentObj, [ _propertyName ]); + } + else + { + value = parentObj[_propertyName]; + } + } + } + else + { + value = null; + } + + updateChildren(); + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * The generic event handler. + * The only event we'll hear indicates that the property has changed. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function eventHandler(event:Event):void + { + if (event is PropertyChangeEvent) + { + var propName:Object = PropertyChangeEvent(event).property; + + if (propName != _propertyName) + return; + } + + wrapUpdate(updateProperty); + + notifyListeners(events[event.type]); + } +} + +} diff --git a/src/mx/binding/RepeaterComponentWatcher.as b/src/mx/binding/RepeaterComponentWatcher.as new file mode 100644 index 00000000..5a7b6d7b --- /dev/null +++ b/src/mx/binding/RepeaterComponentWatcher.as @@ -0,0 +1,160 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import flash.events.Event; +import mx.core.mx_internal; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + */ +public class RepeaterComponentWatcher extends PropertyWatcher +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * + * Create a RepeaterComponentWatcher + * + * @param prop The name of the property to watch. + * @param event The event type that indicates the property has changed. + * @param listeners The binding objects that are listening to this Watcher. + * @param propertyGetter A helper function used to access non-public variables. + */ + public function RepeaterComponentWatcher(propertyName:String, + events:Object, + listeners:Array, + propertyGetter:Function = null) + { + super(propertyName, events, listeners, propertyGetter); + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var clones:Array; + + /** + * @private + */ + private var original:Boolean = true; + + //-------------------------------------------------------------------------- + // + // Overridden methods: Watcher + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function updateChildren():void + { + if (original) + { + updateClones(); + } + else + { + super.updateChildren(); + } + } + + /** + * @private + */ + override protected function shallowClone():Watcher + { + return new RepeaterComponentWatcher(propertyName, events, listeners, propertyGetter); + } + + /** + * @private + */ + private function updateClones():void + { + var components:Array = value as Array; + + if (components) + { + if (clones) + clones = clones.splice(0, components.length); + else + clones = []; + + for (var i:int = 0; i < components.length; i++) + { + var clone:RepeaterComponentWatcher = RepeaterComponentWatcher(clones[i]); + + if (!clone) + { + clone = RepeaterComponentWatcher(deepClone(i)); + clone.original = false; + clones[i] = clone; + } + + clone.value = components[i]; + clone.updateChildren(); + } + } + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * Invokes super's notifyListeners() on each of the clones. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + override public function notifyListeners(commitEvent:Boolean):void + { + if (original) + { + if (clones) + { + for (var i:int = 0; i < clones.length; i++) + { + RepeaterComponentWatcher(clones[i]).notifyListeners(commitEvent); + } + } + } + + super.notifyListeners(commitEvent); + } +} + +} diff --git a/src/mx/binding/RepeaterItemWatcher.as b/src/mx/binding/RepeaterItemWatcher.as new file mode 100644 index 00000000..1dd57f4e --- /dev/null +++ b/src/mx/binding/RepeaterItemWatcher.as @@ -0,0 +1,162 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import mx.collections.CursorBookmark; +import mx.collections.ICollectionView; +import mx.collections.IViewCursor; +import mx.core.mx_internal; +import mx.events.CollectionEvent; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + */ +public class RepeaterItemWatcher extends Watcher +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * Constructor. + */ + public function RepeaterItemWatcher(dataProviderWatcher:PropertyWatcher) + { + super(); + + this.dataProviderWatcher = dataProviderWatcher; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var dataProviderWatcher:PropertyWatcher; + + /** + * @private + */ + private var clones:Array; + + /** + * @private + */ + private var original:Boolean = true; + + //-------------------------------------------------------------------------- + // + // Overridden methods: Watcher + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function updateParent(parent:Object):void + { + var dataProvider:ICollectionView; + + if (dataProviderWatcher) + { + dataProvider = ICollectionView(dataProviderWatcher.value); + if (dataProvider != null) + { + dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, changedHandler, false); + } + } + + dataProviderWatcher = PropertyWatcher(parent); + dataProvider = ICollectionView(dataProviderWatcher.value); + + if (dataProvider) + { + if (original) + { + dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE, changedHandler, false, 0, true); + updateClones(dataProvider); + } + else + { + wrapUpdate(function():void + { + var iterator:IViewCursor = dataProvider.createCursor(); + iterator.seek(CursorBookmark.FIRST, cloneIndex); + value = iterator.current; + updateChildren(); + }); + } + } + } + + /** + * @private + * Handles "Change" events sent by calls to Collection APIs + * on the Repeater's dataProvider. + */ + private function changedHandler(collectionEvent:CollectionEvent):void + { + var dataProvider:ICollectionView = ICollectionView(dataProviderWatcher.value); + + if (dataProvider) + updateClones(dataProvider); + } + + /** + * @private + */ + override protected function shallowClone():Watcher + { + return new RepeaterItemWatcher(dataProviderWatcher); + } + + /** + * @private + */ + private function updateClones(dataProvider:ICollectionView):void + { + if (clones) + clones = clones.splice(0, dataProvider.length); + else + clones = []; + + for (var i:int = 0; i < dataProvider.length; i++) + { + var clone:RepeaterItemWatcher = RepeaterItemWatcher(clones[i]); + + if (!clone) + { + clone = RepeaterItemWatcher(deepClone(i)); + clone.original = false; + clones[i] = clone; + } + + clone.updateParent(dataProviderWatcher); + } + } +} + +} diff --git a/src/mx/binding/StaticPropertyWatcher.as b/src/mx/binding/StaticPropertyWatcher.as new file mode 100644 index 00000000..d2d1e9ca --- /dev/null +++ b/src/mx/binding/StaticPropertyWatcher.as @@ -0,0 +1,272 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import flash.events.Event; +import flash.events.IEventDispatcher; +import mx.core.EventPriority; +import mx.events.PropertyChangeEvent; + +[ExcludeClass] + +/** + * @private + */ +public class StaticPropertyWatcher extends Watcher +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Create a StaticPropertyWatcher + * + * @param prop The name of the static property to watch. + * @param event The event type that indicates the static property has changed. + * @param listeners The binding objects that are listening to this Watcher. + * @param propertyGetter A helper function used to access non-public variables. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function StaticPropertyWatcher(propertyName:String, + events:Object, + listeners:Array, + propertyGetter:Function = null) + { + super(listeners); + + _propertyName = propertyName; + this.events = events; + this.propertyGetter = propertyGetter; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * The parent class of this static property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var parentObj:Class; + + /** + * The events that indicate the static property has changed + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + protected var events:Object; + + /** + * Storage for the propertyGetter property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var propertyGetter:Function; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // propertyName + //---------------------------------- + + /** + * Storage for the propertyName property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var _propertyName:String; + + /** + * The name of the property this Watcher is watching. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get propertyName():String + { + return _propertyName; + } + + //-------------------------------------------------------------------------- + // + // Overridden methods: Watcher + // + //-------------------------------------------------------------------------- + + /** + * If the parent has changed we need to update ourselves + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + override public function updateParent(parent:Object):void + { + // The assumption is that parent is of type, Class, and that + // the class has a static variable or property, + // staticEventDispatcher, of type IEventDispatcher. + parentObj = Class(parent); + + if (parentObj["staticEventDispatcher"] != null) + { + for (var eventType:String in events) + { + if (eventType != "__NoChangeEvent__") + { + var eventDispatcher:IEventDispatcher = parentObj["staticEventDispatcher"]; + + eventDispatcher.addEventListener(eventType, eventHandler, false, + EventPriority.BINDING, true); + } + } + } + + // Now get our property. + wrapUpdate(updateProperty); + } + + /** + * @private + */ + override protected function shallowClone():Watcher + { + var clone:StaticPropertyWatcher = new StaticPropertyWatcher(_propertyName, + events, + listeners, + propertyGetter); + + return clone; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function traceInfo():String + { + return ("StaticPropertyWatcher(" + parentObj + "." + _propertyName + + "): events = [" + eventNamesToString() + "]"); + } + + /** + * @private + */ + private function eventNamesToString():String + { + var s:String = " "; + + for (var ev:String in events) + { + s += ev + " "; + } + + return s; + } + + /** + * Gets the actual property then updates + * the Watcher's children appropriately. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private function updateProperty():void + { + if (parentObj) + { + if (propertyGetter != null) + { + value = propertyGetter.apply(parentObj, [ _propertyName ]); + } + else + { + value = parentObj[_propertyName]; + } + } + else + { + value = null; + } + + updateChildren(); + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * The generic event handler. + * The only event we'll hear indicates that the property has changed. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function eventHandler(event:Event):void + { + if (event is PropertyChangeEvent) + { + var propName:Object = PropertyChangeEvent(event).property; + + if (propName != _propertyName) + return; + } + + wrapUpdate(updateProperty); + + notifyListeners(events[event.type]); + } +} + +} diff --git a/src/mx/binding/Watcher.as b/src/mx/binding/Watcher.as new file mode 100644 index 00000000..970cbf15 --- /dev/null +++ b/src/mx/binding/Watcher.as @@ -0,0 +1,317 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import mx.collections.errors.ItemPendingError; +import mx.core.mx_internal; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + */ +public class Watcher +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function Watcher(listeners:Array = null) + { + super(); + + this.listeners = listeners; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * The binding objects that are listening to this Watcher. + * The standard event mechanism isn't used because it's too heavyweight. + */ + protected var listeners:Array; + + /** + * @private + * Children of this watcher are watching sub values. + */ + protected var children:Array; + + /** + * @private + * The value itself. + */ + public var value:Object; + + /** + * @private + * Keep track of cloning when used in Repeaters. + */ + protected var cloneIndex:int; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * This is an abstract method that subclasses implement. + */ + public function updateParent(parent:Object):void + { + } + + /** + * @private + * Add a child to this watcher, meaning that the child + * is watching a sub value of ours. + */ + public function addChild(child:Watcher):void + { + if (!children) + children = [ child ]; + else + children.push(child); + + child.updateParent(this); + } + + /** + * @private + * Remove all children beginning at a starting index. + * If the index is not specified, it is assumed to be 0. + * This capability is used by Repeater, which must remove + * cloned RepeaterItemWatchers (and their descendant watchers). + */ + public function removeChildren(startingIndex:int):void + { + children.splice(startingIndex); + } + + /** + * We have probably changed, so go through + * and make sure our children are updated. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function updateChildren():void + { + if (children) + { + var n:int = children.length; + for (var i:int = 0; i < n; ++i) + { + children[i].updateParent(this); + } + } + } + + /** + * @private + */ + private function valueChanged(oldval:Object):Boolean + { + if (oldval == null && value == null) + return false; + + var valType:String = typeof(value); + + // The first check is meant to catch the delayed instantiation case + // where a control comes into existence but its value is still + // the equivalent of not having been filled in. + // Otherwise we simply return whether the value has changed. + + if (valType == "string") + { + if (oldval == null && value == "") + return false; + else + return oldval != value; + } + + if (valType == "number") + { + if (oldval == null && value == 0) + return false; + else + return oldval != value; + } + + if (valType == "boolean") + { + if (oldval == null && value == false) + return false; + else + return oldval != value; + } + + return true; + } + + /** + * @private + */ + protected function wrapUpdate(wrappedFunction:Function):void + { + try + { + wrappedFunction.apply(this); + } + catch(itemPendingError:ItemPendingError) + { + // The parent's value is not yet available. This is being ignored for now - + // updateParent() will be called when the parent has a value. + value = null; + } + catch(rangeError:RangeError) + { + // The parent's value is not yet available. This is being ignored for now - + // updateParent() will be called when the parent has a value. + value = null; + } + catch(error:Error) + { + // Certain errors are normal when executing an update, so we swallow them: + // Error #1006: Call attempted on an object that is not a function. + // Error #1009: null has no properties. + // Error #1010: undefined has no properties. + // Error #1055: - has no properties. + // Error #1069: Property - not found on - and there is no default value + // Error #1507: - invalid null argument. + // We allow any other errors to be thrown. + if ((error.errorID != 1006) && + (error.errorID != 1009) && + (error.errorID != 1010) && + (error.errorID != 1055) && + (error.errorID != 1069) && + (error.errorID != 1507)) + { + throw error; + } + } + } + + /** + * @private + * Clone this Watcher and all its descendants. + * Each clone triggers the same Bindings as the original; + * in other words, the Bindings do not get cloned. + * + * This cloning capability is used by Repeater in order + * to watch the subproperties of multiple dataProvider items. + * For example, suppose a repeated LinkButton's label is + * {r.currentItem.firstName} {r.currentItem.lastName} + * where r is a Repeater whose dataProvider is + * [ { firstName: "Matt", lastName: "Chotin" }, + * { firstName: "Gordon", lastName: "Smith" } ] + * The MXML compiler emits a watcher tree (one item of _watchers[]) + * that looks like this: + * PropertyWatcher for "r" + * PropertyWatcher for "dataProvider" + * RepeaterItemWatcher + * PropertyWatcher for "firstName" + * PropertyWatcher for "lastName" + * At runtime the RepeaterItemWatcher serves as a template + * which gets cloned for each dataProvider item: + * PropertyWatcher for "r" + * PropertyWatcher for "dataProvider" + * RepeaterItemWatcher (index: null) + * PropertyWatcher for "firstName" (value: null) + * PropertyWatcher for "lastName" (value: null) + * RepeaterItemWatcher (index: 0) + * PropertyWatcher for "firstName" (value: "Matt") + * PropertyWatcher for "lastName" (value: "Chotin") + * RepeaterItemWatcher (index: 1) + * PropertyWatcher for "firstName" (value: "Gordon") + * PropertyWatcher for "lastName" (value: "Smith") + */ + protected function deepClone(index:int):Watcher + { + // Clone this watcher object itself. + var w:Watcher = shallowClone(); + w.cloneIndex = index; + + // Clone its listener queue. + if (listeners) + { + w.listeners = listeners.concat(); + } + + // Recursively clone its children. + if (children) + { + var n:int = children.length; + for (var i:int = 0; i < n; i++) + { + var clonedChild:Watcher = children[i].deepClone(index); + w.addChild(clonedChild); + } + } + + // Return the cloned tree of watchers. + return w; + } + + /** + * @private + * Clone this watcher object itself, without cloning its children. + * The clone is not connec + * Subclasses must override this method to copy their properties. + */ + protected function shallowClone():Watcher + { + return new Watcher(); + } + + /** + * @private + */ + public function notifyListeners(commitEvent:Boolean):void + { + if (listeners) + { + var n:int = listeners.length; + + for (var i:int = 0; i < n; i++) + { + listeners[i].watcherFired(commitEvent, cloneIndex); + } + } + } +} + +} diff --git a/src/mx/binding/XMLWatcher.as b/src/mx/binding/XMLWatcher.as new file mode 100644 index 00000000..e0de235f --- /dev/null +++ b/src/mx/binding/XMLWatcher.as @@ -0,0 +1,183 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.binding +{ + +import mx.core.mx_internal; +import mx.utils.XMLNotifier; +import mx.utils.IXMLNotifiable; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + */ +public class XMLWatcher extends Watcher implements IXMLNotifiable +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function XMLWatcher(propertyName:String, listeners:Array) + { + super(listeners); + + _propertyName = propertyName; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * The parent object of this property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var parentObj:Object; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // propertyName + //---------------------------------- + + /** + * Storage for the propertyName property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var _propertyName:String; + + /** + * The name of the property this Watcher is watching. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get propertyName():String + { + return _propertyName; + } + + //-------------------------------------------------------------------------- + // + // Overridden methods: Watcher + // + //-------------------------------------------------------------------------- + + /** + * If the parent has changed we need to update ourselves + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + override public function updateParent(parent:Object):void + { + if (parentObj && (parentObj is XML || parentObj is XMLList)) + XMLNotifier.getInstance().unwatchXML(parentObj, this); + + if (parent is Watcher) + parentObj = parent.value; + else + parentObj = parent; + + if (parentObj && (parentObj is XML || parentObj is XMLList)) + XMLNotifier.getInstance().watchXML(parentObj, this); + + // Now get our property. + wrapUpdate(updateProperty); + } + + /** + * @private + */ + override protected function shallowClone():Watcher + { + return new XMLWatcher(_propertyName, listeners); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Gets the actual property then updates + * the Watcher's children appropriately. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private function updateProperty():void + { + if (parentObj) + { + if (_propertyName == "this") + value = parentObj; + else + value = parentObj[_propertyName]; + } + else + { + value = null; + } + + updateChildren(); + } + + /** + * @private + */ + public function xmlNotification(currentTarget:Object, type:String, + target:Object, value:Object, detail:Object):void + { + updateProperty(); + + notifyListeners(true); + } +} + +} diff --git a/src/mx/collections/CursorBookmark.as b/src/mx/collections/CursorBookmark.as new file mode 100644 index 00000000..6db4636d --- /dev/null +++ b/src/mx/collections/CursorBookmark.as @@ -0,0 +1,157 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.collections +{ + +/** + * Encapsulates the positional aspects of a cursor in an + * ICollectionView. Bookmarks are used to return a cursor to + * an absolute position within the ICollectionView. + * + * @see mx.collections.IViewCursor#bookmark + * @see mx.collections.IViewCursor#seek() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class CursorBookmark +{ + include "../core/Version.as"; + + private static var _first:CursorBookmark; + private static var _last:CursorBookmark; + private static var _current:CursorBookmark; + + /** + * A bookmark for the first item in an ICollectionView. + * + * @return The bookmark to the first item. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function get FIRST():CursorBookmark + { + if (!_first) + _first = new CursorBookmark("${F}"); + return _first; + } + + /** + * A bookmark for the last item in an ICollectionView. + * If the view has no items, the cursor is at this bookmark. + * + * @return The bookmark to the last item. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function get LAST():CursorBookmark + { + if (!_last) + _last = new CursorBookmark("${L}"); + return _last; + } + + /** + * A bookmark representing the current item for the IViewCursor in + * an ICollectionView. + * + * @return The bookmark to the current item. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function get CURRENT():CursorBookmark + { + if (!_current) + _current = new CursorBookmark("${C}"); + return _current; + } + + /** + * Creates a new instance of a bookmark with the specified value. + * + * @param value The value of this bookmark. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function CursorBookmark(value:Object) + { + super(); + _value = value; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // value + //---------------------------------- + + private var _value:Object; + + /** + * The underlying marker representation of the bookmark. + * This value is generally understood only by the IViewCursor + * or ICollectionView implementation. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get value():Object + { + return _value; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Gets the approximate index of the item represented by this bookmark + * in its view. If the item has been paged out, this method could throw an + * ItemPendingError. + * + * @return The index of the item. If the item is not in the current view, this method returns + * -1. This method also returns -1 if index-based location is not possible. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getViewIndex():int + { + return -1; + } +} + +} diff --git a/src/mx/collections/ICollectionView.as b/src/mx/collections/ICollectionView.as new file mode 100644 index 00000000..d79ef18f --- /dev/null +++ b/src/mx/collections/ICollectionView.as @@ -0,0 +1,286 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.collections +{ + +import flash.events.IEventDispatcher; +import mx.events.CollectionEvent; + +/** + * Dispatched when the ICollectionView has been updated in some way. + * + * @eventType mx.events.CollectionEvent.COLLECTION_CHANGE + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="collectionChange", type="mx.events.CollectionEvent")] + +/** + * An ICollectionView is a view onto a collection of data. + * The view can be modified to show the data sorted according to various + * criteria or reduced by filters without modifying the underlying data. + * An IViewCursor provides to access items within a + * collection. You can modify the collection by using the IViewCursor + * interface insert() and remove() methods. + * + *

An ICollectionView may be a view onto data that has been + * retrieved from a remote location. + * When Implementing this interface for data + * that may be remote it is important to handle the case where data + * may not yet be available, which is indicated by the + * ItemPendingError.

+ * + *

The IList interface is an alternative to the + * ICollectionView interface.

+ * + * @see mx.collections.IViewCursor + * @see mx.collections.errors.ItemPendingError + * @see mx.collections.IList + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface ICollectionView extends IEventDispatcher +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // length + //---------------------------------- + + /** + * The number of items in this view. + * 0 means no items, while -1 means that the length is unknown. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get length():int; + + //---------------------------------- + // filterFunction + //---------------------------------- + + /** + * A function that the view will use to eliminate items that do not + * match the function's criteria. + * A filterFunction is expected to have the following signature: + * + *
f(item:Object):Boolean
+ * + * where the return value is true if the specified item + * should remain in the view. + * + *

If a filter is unsupported, Flex throws an error when accessing + * this property. + * You must call refresh() after setting the + * filterFunction property for the view to update.

+ * + *

Note: The Flex implementations of ICollectionView retrieve all + * items from a remote location before executing the filter function. + * If you use paging, apply the filter to the remote collection before + * you retrieve the data.

+ * + * @see #refresh() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get filterFunction():Function; + + /** + * @private + */ + function set filterFunction(value:Function):void; + + //---------------------------------- + // sort + //---------------------------------- + + /** + * The ISort that will be applied to the ICollectionView. + * Setting the sort does not automatically refresh the view, + * so you must call the refresh() method + * after setting this property. + * If sort is unsupported an error will be thrown when accessing + * this property. + * + *

Note: The Flex implementations of ICollectionView retrieve all + * items from a remote location before executing a sort. + * If you use paging with a sorted list, apply the sort to the remote + * collection before you retrieve the data.

+ * + * @see #refresh() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get sort():ISort; + + /** + * @private + */ + function set sort(value:ISort):void; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Creates a new IViewCursor that works with this view. + * + * @return A new IViewCursor implementation. + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function createCursor():IViewCursor; + + /** + * Returns whether the view contains the specified object. + * Unlike the IViewCursor.findxxx methods, + * this search is succesful only if it finds an item that exactly + * matches the parameter. + * If the view has a filter applied to it this method may return + * false even if the underlying collection + * does contain the item. + * + * @param item The object to look for. + * + * @return true if the ICollectionView, after applying any filter, + * contains the item; false otherwise. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function contains(item:Object):Boolean; + + /** + * Prevents changes to the collection itself and items within the + * collection from being dispatched by the view. + * Also prevents the view from updating the positions of items + * if the positions change in the collection. + * The changes will be queued and dispatched appropriately + * after enableAutoUpdate is called. + * If more events than updates to a single item occur, + * the view may end up resetting. + * The disableAutoUpdate method acts cumulatively; + * the same number of calls to enableAutoUpdate + * are required for the view to dispatch events and refresh. + * Note that disableAutoUpdate only affects the + * individual view; edits may be detected on an individual + * basis by other views. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function disableAutoUpdate():void; + + /** + * Enables auto-updating. + * See disableAutoUpdate for more information. + * + * @see #disableAutoUpdate() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function enableAutoUpdate():void; + + /** + * Notifies the view that an item has been updated. + * This method is useful if the contents of the view do not implement + * IPropertyChangeNotifier. + * If the call to this method includes a property parameter, + * the view may be able to optimize its notification mechanism. + * Otherwise it may choose to simply refresh the whole view. + * + * @param item The item within the view that was updated. + * + * @param property The name of 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.) + * + * @see mx.events.CollectionEvent + * @see mx.core.IPropertyChangeNotifier + * @see mx.events.PropertyChangeEvent + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function itemUpdated(item:Object, property:Object = null, + oldValue:Object = null, newValue:Object = null):void; + + /** + * Applies the sort and filter to the view. + * The ICollectionView does not detect changes to a sort or + * filter automatically, so you must call the refresh() + * method to update the view after setting the sort + * or filterFunction property. + * If your ICollectionView implementation also implements + * the IMXMLObject interface, you should to call the + * refresh() method from your initialized() + * method. + * + *

Returns true if the refresh was successful + * and false if the sort is not yet complete + * (e.g., items are still pending). + * A client of the view should wait for a CollectionEvent event + * with the CollectionEventKind.REFRESH kind + * property to ensure that the refresh() operation is + * complete.

+ * + * @return true if the refresh() was complete, + * false if the refresh() is incomplete. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function refresh():Boolean; +} + +} diff --git a/src/mx/collections/IList.as b/src/mx/collections/IList.as new file mode 100644 index 00000000..e054d4fe --- /dev/null +++ b/src/mx/collections/IList.as @@ -0,0 +1,263 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.collections +{ + +import flash.events.IEventDispatcher; +import mx.events.CollectionEvent; + +/** + * Dispatched when the IList has been updated in some way. + * + * @eventType mx.events.CollectionEvent.COLLECTION_CHANGE + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="collectionChange", type="mx.events.CollectionEvent")] + +/** + * A collection of items organized in an ordinal fashion. + * Provides access and manipulation methods based on index. + * + *

An IList may be a view onto data + * that has been retrieved from a remote location. + * When writing for a collection that may be remote, + * it is important to handle the case where data + * may not yet be available, which is indicated + * by the ItemPendingError.

+ * + *

The ICollectionView is an alternative + * to the IList.

+ * + * @see mx.collections.errors.ItemPendingError + * @see mx.collections.ICollectionView + * @see mx.collections.ListCollectionView + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IList extends IEventDispatcher +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // length + //---------------------------------- + + /** + * The number of items in this collection. + * 0 means no items while -1 means the length is unknown. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get length():int; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Adds the specified item to the end of the list. + * Equivalent to addItemAt(item, length). + * + * @param item The item to add. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function addItem(item:Object):void; + + /** + * Adds the item at the specified index. + * The index of any item greater than the index of the added item is increased by one. + * If the the specified index is less than zero or greater than the length + * of the list, a RangeError is thrown. + * + * @param item The item to place at the index. + * + * @param index The index at which to place the item. + * + * @throws RangeError if index is less than 0 or greater than the length of the list. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function addItemAt(item:Object, index:int):void; + + /** + * Gets the item at the specified index. + * + * @param index The index in the list from which to retrieve the item. + * + * @param prefetch An int indicating both the direction + * and number of items to fetch during the request if the item is + * not local. + * + * @return The item at that index, or null if there is none. + * + * @throws mx.collections.errors.ItemPendingError if the data for that index needs to be + * loaded from a remote location. + * + * @throws RangeError if index < 0 + * or index >= length. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getItemAt(index:int, prefetch:int = 0):Object; + + /** + * Returns the index of the item if it is in the list such that + * getItemAt(index) == item. + * + *

Note: unlike IViewCursor.findxxx() methods, + * The getItemIndex() method cannot take a parameter with + * only a subset of the fields in the item being serched for; + * this method always searches for an item that exactly matches + * the input parameter.

+ * + * @param item The item to find. + * + * @return The index of the item, or -1 if the item is not in the list. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getItemIndex(item:Object):int; + + /** + * Notifies the view that an item has been updated. + * This is useful if the contents of the view do not implement + * IEventDispatcher and dispatches a + * PropertyChangeEvent. + * If a property is specified the view may be able to optimize its + * notification mechanism. + * Otherwise it may choose to simply refresh the whole view. + * + * @param item The item within the view that was updated. + * + * @param property The name of 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.) + * + * @see mx.events.CollectionEvent + * @see mx.events.PropertyChangeEvent + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function itemUpdated(item:Object, property:Object = null, + oldValue:Object = null, + newValue:Object = null):void; + + /** + * Removes all items from the list. + * + *

If any item is not local and an asynchronous operation must be + * performed, an ItemPendingError will be thrown.

+ * + *

See the ItemPendingError documentation as well as + * the collections documentation for more information + * on using the ItemPendingError.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function removeAll():void; + + /** + * Removes the item at the specified index and returns it. + * Any items that were after this index are now one index earlier. + * + * @param index The index from which to remove the item. + * + * @return The item that was removed. + * + * @throws RangeError is index is less than 0 or greater than length. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function removeItemAt(index:int):Object; + + /** + * Places the item at the specified index. + * If an item was already at that index the new item will replace it + * and it will be returned. + * + * @param item The new item to be placed at the specified index. + * + * @param index The index at which to place the item. + * + * @return The item that was replaced, or null if none. + * + * @throws RangeError if index is less than 0 or greater than length. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function setItemAt(item:Object, index:int):Object; + + /** + * Returns an Array that is populated in the same order as the IList + * implementation. + * This method can throw an ItemPendingError. + * + * @return The array. + * + * @throws mx.collections.errors.ItemPendingError If the data is not yet completely loaded + * from a remote location. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function toArray():Array; +} + +} diff --git a/src/mx/collections/ISort.as b/src/mx/collections/ISort.as new file mode 100644 index 00000000..709c26d7 --- /dev/null +++ b/src/mx/collections/ISort.as @@ -0,0 +1,325 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2010 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.collections +{ + + /** + * The ISort interface defines the interface for classes that + * provide the sorting information required to sort the + * data of a collection view. + * + * @see mx.collections.ICollectionView + * @see mx.collections.ISortField + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4.5 + */ +public interface ISort +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * The method used to compare items when sorting. + * If you specify this property, Flex ignores any + * compareFunction properties that you specify in the + * ISortField objects that you use in this class. + * + *

The compare function must have the following signature:

+ * + *

+     *
+     *     function [name](a:Object, b:Object, fields:Array = null):int
+     *
+     *  
+ * + *

This function must return the following value: + *

    + *
  • -1, if the Object a should appear before the + * Object b in the sorted sequence
  • + *
  • 0, if the Object a equals the + * Object b
  • + *
  • 1, if the Object a should appear after the + * Object b in the sorted sequence
  • + *

+ *

To return to the internal comparision function, set this value to + * null.

+ *

+ * The fields array specifies the object fields + * to compare. + * Typically the algorithm will compare properties until the field list is + * exhausted or a non-zero value can be returned. + * For example:

+ * + *

+     *    function myCompare(a:Object, b:Object, fields:Array = null):int
+     *    {
+     *        var result:int = 0;
+     *        var i:int = 0;
+     *        var propList:Array = fields ? fields : internalPropList;
+     *        var len:int = propList.length;
+     *        var propName:String;
+     *        while (result == 0 && (i < len))
+     *        {
+     *            propName = propList[i];
+     *            result = compareValues(a[propName], b[propName]);
+     *            i++;
+     *        }
+     *        return result;
+     *    }
+     *
+     *    function compareValues(a:Object, b:Object):int
+     *    {
+     *        if (a == null && b == null)
+     *            return 0;
+     *
+     *        if (a == null)
+     *          return 1;
+     *
+     *        if (b == null)
+     *           return -1;
+     *
+     *        if (a < b)
+     *            return -1;
+     *
+     *        if (a > b)
+     *            return 1;
+     *
+     *        return 0;
+     *    }
+     *  
+ * + *

The default value is an internal compare function that can perform + * a string, numeric, or date comparison in ascending or descending order. + * Specify your own function only if you need a need a custom + * comparison algorithm. This is normally only the case if a calculated + * field is used in a display.

+ * + *

Alternatively you can specify separate compare functions for each + * sort field by using the ISortField class + * compareFunction property; This way you can use the default + * comparison for some fields and a custom comparison for others.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4.5 + */ + function get compareFunction():Function; + function set compareFunction(value:Function):void; + + /** + * An Array of ISortField objects that specifies the fields to compare. + * The order of the ISortField objects in the array determines + * field priority order when sorting. + * The default sort comparator checks the sort fields in array + * order until it determinines a sort order for the two + * fields being compared. + * + * @default null + * + * @see ISortField + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4.5 + */ + function get fields():Array; + function set fields(value:Array):void; + + /** + * Indicates if the sort should be unique. + * Unique sorts fail if any value or combined value specified by the + * fields listed in the fields property result in an indeterminate or + * non-unique sort order; that is, if two or more items have identical + * sort field values. An error is thrown if the sort is not unique. + * The sorting logic uses this unique property value only if sort + * field(s) are specified explicitly. If no sort fields are specified + * explicitly, no error is thrown even when there are identical value + * elements. + * + * @default false + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4.5 + */ + function get unique():Boolean; + function set unique(value:Boolean):void; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Finds the specified object within the specified array (or the insertion + * point if asked for), returning the index if found or -1 if not. + * The ListCollectionView class findxxx() + * methods use this method to find the requested item; as a general rule, + * it is easier to use these functions, and not findItem() + * to find data in ListCollectionView-based objects. + * You call the findItem() method directly when writing a + * class that supports sorting, such as a new ICollectionView + * implementation. + * The input items array need to be sorted before calling this function. + * Otherwise this function will not be able to find the specified value + * properly. + * + * @param items the Array within which to search. + * @param values Object containing the properties to look for (or + * the object to search for, itself). + * The object must consist of field name/value pairs, where + * the field names are names of fields specified by the + * fields property, in the same order they + * are used in that property. + * You do not have to specify all of the fields from the + * fields property, but you + * cannot skip any in the order. + * Therefore, if the fields + * properity lists three fields, you can specify its first + * and second fields in this parameter, but you cannot + * specify only the first and third fields. + * @param mode String containing the type of find to perform. + * Valid values are: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ANY_INDEX_MODEReturn any position that + * is valid for the values.
FIRST_INDEX_MODEReturn the position + * where the first occurrance of the values is found.
LAST_INDEX_MODEReturn the position where the + * last ocurrance of the specified values is found. + *
+ * @param returnInsertionIndex If the method does not find an item + * identified by the values parameter, + * and this parameter is true the + * findItem() + * method returns the insertion point for the values, + * that is the point in the sorted order where you + * should insert the item. + * @param compareFunction a comparator function to use to find the item. + * If you do not specify this parameter or , or if you + * provide a null value, + * findItem() function uses the + * compare function determined by the ISort + * instance's compareFunction property, + * passing in the array of fields determined + * by the values object and the current + * SortFields. + * + * If you provide a non-null value, findItem() + * function uses it as the compare function. + * + * The signature of the function passed as + * compareFunction must be as follows: + * function myCompareFunction(a:Object, b:Object):int. + * Note that there is no third argument unlike the + * compare function for ISort.compareFunction() + * property. + * @return int The index in the array of the found item. + * If the returnInsertionIndex parameter is + * false and the item is not found, returns -1. + * If the returnInsertionIndex parameter is + * true and the item is not found, returns + * the index of the point in the sorted array where the + * values would be inserted. + * + * @throws SortError If there are any parameter errors, + * the find critieria is not compatible with the sort + * or the comparator function for the sort can not be determined. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4.5 + */ + function findItem( + items:Array, + values:Object, + mode:String, + returnInsertionIndex:Boolean = false, + compareFunction:Function = null):int; + + /** + * Return whether the specified property is used to control the sort. + * The function cannot determine a definitive answer if the sort uses a + * custom comparator; it always returns true in this case. + * + * @param property The name of the field that to test. + * @return Whether the property value might affect the sort outcome. + * If the sort uses the default compareFunction, returns + * true if the + * property parameter specifies a sort field. + * If the sort or any ISortField uses a custom comparator, + * there's no way to know, so return true. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4.5 + */ + function propertyAffectsSort(property:String):Boolean; + + /** + * Goes through the fields array and calls + * reverse() on each of the ISortField objects in + * the array. If the field was descending now it is ascending, + * and vice versa. + * + *

Note: an ICollectionView does not automatically + * update when the objects in the fields array are modified; + * call its refresh() method to update the view.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4.5 + */ + function reverse():void; + + /** + * Apply the current sort to the specified array (not a copy). + * To prevent the array from being modified, create a copy + * use the copy in the items parameter. + * + *

Flex ICollectionView implementations call the + * sort method automatically and ensure that the sort is + * performed on a copy of the underlying data.

+ * + * @param items Array of items to sort. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4.5 + */ + function sort(items:Array):void; +} +} diff --git a/src/mx/collections/IViewCursor.as b/src/mx/collections/IViewCursor.as new file mode 100644 index 00000000..32609943 --- /dev/null +++ b/src/mx/collections/IViewCursor.as @@ -0,0 +1,455 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.collections +{ + +import flash.events.IEventDispatcher; + +/** + * Dispatched whenever the cursor position is updated. + * + * @eventType mx.events.FlexEvent.CURSOR_UPDATE + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="cursorUpdate", type="mx.events.FlexEvent")] + +/** + * Defines the interface for enumerating a collection view bi-directionally. + * This cursor provides find, seek, and bookmarking capabilities along + * with the modification methods insert and remove. + * When a cursor is first retrieved from a view, (typically by the ICollectionView + * createCursor() method) the value of the + * current property should be the first + * item in the view, unless the view is empty. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IViewCursor extends IEventDispatcher +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // afterLast + //---------------------------------- + + [Bindable("cursorUpdate")] + + /** + * If the cursor is located after the last item in the view, + * this property is true . + * If the ICollectionView is empty (length == 0), + * this property is true. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get afterLast():Boolean; + + //---------------------------------- + // beforeFirst + //---------------------------------- + + [Bindable("cursorUpdate")] + + /** + * If the cursor is located before the first item in the view, + * this property is true. + * If the ICollectionView is empty (length == 0), + * this property is true. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get beforeFirst():Boolean; + + //---------------------------------- + // bookmark + //---------------------------------- + + [Bindable("cursorUpdate")] + + /** + * Provides access to a bookmark that corresponds to the item + * returned by the current property. + * The bookmark can be used to move the cursor + * to a previously visited item, or to a position relative to that item. + * (See the seek() method for more information.) + * + * @see #current + * @see #seek() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get bookmark():CursorBookmark; + + //---------------------------------- + // current + //---------------------------------- + + [Bindable("cursorUpdate")] + + /** + * Provides access the object at the location + * in the source collection referenced by this cursor. + * If the cursor is beyond the ends of the collection + * (beforeFirst, afterLast) + * this will return null. + * + * @see #moveNext() + * @see #movePrevious() + * @see #seek() + * @see #beforeFirst + * @see #afterLast + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get current():Object; + + //---------------------------------- + // view + //---------------------------------- + + /** + * A reference to the ICollectionView with which this cursor is associated. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get view():ICollectionView; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Finds an item with the specified properties within the collection + * and positions the cursor to that item. + * If the item can not be found, the cursor location does not change. + * + *

The findAny() method can only be called on sorted views; + * if the view isn't sorted, a CursorError is thrown.

+ * + *

If the associated collection is remote, and not all of the items + * have been cached locally, this method begins an asynchronous fetch + * from the remote collection. If one is already in progress, this method + * waits for it to complete before making another fetch request.

+ * + *

If multiple items can match the search criteria then the item found + * is non-deterministic. + * If it is important to find the first or last occurrence of an item + * in a non-unique index, use the findFirst() or + * findLast() method.

+ * + *

If the data is not local and an asynchronous operation must be + * performed, an ItemPendingError is thrown.

+ * + * @param values The search criteria. The values in the Object must be configured as name-value pairs, + * as in an associative array (or be the actual object to search for). The values of the names specified must match properties + * specified in the sort. For example, if properties x, y, and + * z are in the current sort, the values specified should be + * {x: x-value, y: y-value, z: z-value}. + * + * @return When all of the data is local this method returns + * true if the item can be found and false + * otherwise. + * + * @see #findFirst() + * @see #findLast() + * @see mx.collections.errors.ItemPendingError + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function findAny(values:Object):Boolean; + + /** + * Finds the first item with the specified properties within the collection + * and positions the cursor to that item. + * If the item can not be found, no cursor location does not change. + * + *

The findFirst() method can only be called on sorted views; + * if the view isn't sorted, a CursorError is thrown.

+ * + *

If the associated collection is remote, and not all of the items + * have been cached locally, this method begins an asynchronous fetch + * from the remote collection. If one is already in progress, this method + * waits for it to complete before making another fetch request.

+ * + *

If it is not important to find the first occurrence of an item + * in a non-unique index, use findAny(), which may be + * a little faster than the findFirst() method.

+ * + *

If the data is not local and an asynchronous operation must be + * performed, an ItemPendingError is thrown.

+ * + * @param values The search criteria. The values in the Object must be configured as name-value pairs, + * as in an associative array (or be the actual object to search for). The values of the names specified must match properties + * specified in the sort. For example, if properties x, y, and + * z are in the current sort, the values specified should be + * {x: x-value, y: y-value, z: z-value}. + * + * @return When all of the data is local this method returns + * true if the item can be found and false + * otherwise. + * + * @see #findAny() + * @see #findLast() + * @see mx.collections.errors.ItemPendingError + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function findFirst(values:Object):Boolean; + + /** + * Finds the last item with the specified properties within the collection + * and positions the cursor on that item. + * If the item can not be found, the cursor location does not chanage. + * + *

The findLast() method can only be called on sorted views; + * if the view isn't sorted, a CursorError is thrown.

+ * + *

If the associated collection is remote, and not all of the items + * have been cached locally, this method begins an asynchronous fetch + * from the remote collection. If one is already in progress, this method + * waits for it to complete before making another fetch request.

+ * + *

If it is not important to find the last occurrence of an item + * in a non-unique index, use the findAny() method, which + * may be a little faster.

+ * + *

If the data is not local and an asynchronous operation must be + * performed, an ItemPendingError is thrown.

+ * + * @param values The search criteria. The values in the Object must be configured as name-value pairs, + * as in an associative array (or be the actual object to search for). The values of the names specified must match properties + * specified in the sort. For example, if properties x, y, and + * z are in the current sort, the values specified should be + * {x: x-value, y: y-value, z: z-value}. + * + * @return When all of the data is local this method returns + * true if the item can be found and false + * otherwise. + * + * @see #findAny() + * @see #findFirst() + * @see mx.collections.errors.ItemPendingError + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function findLast(values:Object):Boolean; + + /** + * Inserts the specified item before the cursor's current position. + * If the cursor is afterLast, + * the insertion occurs at the end of the view. + * If the cursor is beforeFirst on a non-empty view, + * an error is thrown. + * + * @param item The item to insert before the cursor's current position. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function insert(item:Object):void; + + /** + * Moves the cursor to the next item within the collection. + * On success the current property is updated + * to reference the object at this new location. + * Returns true if the resulting current + * property is valid, or false if not + * (the property value is afterLast). + * + *

If the data is not local and an asynchronous operation must be performed, + * an ItemPendingError is thrown. + * See the ItemPendingError documentation and the collections + * documentation for more information on using the ItemPendingError.

+ * + * @return true if still in the list, + * false if the current value initially was + * or now is afterLast. + * + * @see #current + * @see #movePrevious() + * @see mx.collections.errors.ItemPendingError + * + * @example + *
+     *  var myArrayCollection:ICollectionView = new ArrayCollection([ "Bobby", "Mark", "Trevor", "Jacey", "Tyler" ]);
+     *  var cursor:IViewCursor = myArrayCollection.createCursor();
+     *  while (!cursor.afterLast)
+     *  {
+     *      trace(cursor.current);
+     *      cursor.moveNext();
+     *  }
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function moveNext():Boolean; + + /** + * Moves the cursor to the previous item within the collection. + * On success the current property is updated + * to reference the object at this new location. + * Returns true if the resulting current + * property is valid, or false if not + * (the property value is beforeFirst). + * + *

If the data is not local and an asynchronous operation must be performed, + * an ItemPendingError is thrown. + * See the ItemPendingError documentation and the collections + * documentation for more information on using the ItemPendingError.

+ * + * @return true if still in the list, + * false if the current value initially was or + * now is beforeFirst. + * + * For example: + *
+     *  var myArrayCollection:ICollectionView = new ArrayCollection([ "Bobby", "Mark", "Trevor", "Jacey", "Tyler" ]);
+     *  var cursor:IViewCursor = myArrayCollection.createCursor();
+     *  cursor.seek(CursorBookmark.last);
+     *  while (!cursor.beforeFirst)
+     *  {
+     *      trace(current);
+     *      cursor.movePrevious();
+     *  }
+     *  
+ * + * @see #current + * @see #moveNext() + * @see mx.collections.errors.ItemPendingError + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function movePrevious():Boolean; + + /** + * Removes the current item and returns it. + * If the cursor location is beforeFirst or + * afterLast, throws a CursorError. + * If you remove any item other than the last item, + * the cursor moves to the next item. If you remove the last item, the + * cursor is at the AFTER_LAST bookmark. + * + *

If the item after the removed item is not local and an asynchronous + * operation must be performed, an ItemPendingError is thrown. + * See the ItemPendingError documentation and the collections + * documentation for more information on using the ItemPendingError.

+ * + * @return The item that was removed. + * + * @see mx.collections.errors.ItemPendingError + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function remove():Object; + + /** + * Moves the cursor to a location at an offset from the specified + * bookmark. + * The offset can be negative, in which case the cursor is positioned + * an offset number of items prior to the specified bookmark. + * + *

If the associated collection is remote, and not all of the items + * have been cached locally, this method begins an asynchronous fetch + * from the remote collection.

+ * + *

If the data is not local and an asynchronous operation + * must be performed, an ItemPendingError is thrown. + * See the ItemPendingError documentation and the collections + * documentation for more information on using the ItemPendingError.

+ * + * @param bookmark CursorBookmark reference to marker + * information that allows repositioning to a specific location. + * You can set this parameter to value returned from the + * bookmark property, or to one of the following constant + * bookmark values: + *
    + *
  • CursorBookmark.FIRST - + * Seek from the start (first element) of the collection
  • + *
  • CursorBookmark.CURRENT - + * Seek from the current position in the collection
  • + *
  • CursorBookmark.LAST - + * Seek from the end (last element) of the collection
  • + *
+ * + * @param offset Indicates how far from the specified bookmark to seek. + * If the specified number is negative, the cursor attempts to + * move prior to the specified bookmark. + * If the offset specified is beyond the end of the collection, + * the cursor is be positioned off the end, to the + * beforeFirst or afterLast location. + * + * @param prefetch Used for remote data. Indicates an intent to iterate + * in a specific direction once the seek operation completes. + * This reduces the number of required network round trips during a seek. + * If the iteration direction is known at the time of the request, + * the appropriate amount of data can be returned ahead of the request + * to iterate it. + * + * @see mx.collections.errors.ItemPendingError + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function seek(bookmark:CursorBookmark, offset:int = 0, prefetch:int = 0):void; +} + +} diff --git a/src/mx/collections/errors/CollectionViewError.as b/src/mx/collections/errors/CollectionViewError.as new file mode 100644 index 00000000..f8aee23d --- /dev/null +++ b/src/mx/collections/errors/CollectionViewError.as @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.collections.errors +{ + +/** + * The CollectionViewError class represents general errors + * within a collection that are not related to specific activities + * such as Cursor seeking. + * Errors of this class are thrown by the ListCollectionView class. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class CollectionViewError extends Error +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor. + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param message A message providing information about the error cause. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function CollectionViewError(message:String) + { + super(message); + } +} + +} diff --git a/src/mx/collections/errors/CursorError.as b/src/mx/collections/errors/CursorError.as new file mode 100644 index 00000000..67305ddc --- /dev/null +++ b/src/mx/collections/errors/CursorError.as @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.collections.errors +{ + +/** + * This error is thrown by a collection Cursor. + * Errors of this class are thrown by classes + * that implement the IViewCursor interface. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class CursorError extends Error +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor. + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param message A message providing information about the error cause. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function CursorError(message:String) + { + super(message); + } +} + +} diff --git a/src/mx/collections/errors/ItemPendingError.as b/src/mx/collections/errors/ItemPendingError.as new file mode 100644 index 00000000..3e6177d1 --- /dev/null +++ b/src/mx/collections/errors/ItemPendingError.as @@ -0,0 +1,125 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.collections.errors +{ + +import mx.rpc.IResponder; + +/** + * This error is thrown when retrieving an item from a collection view + * requires an asynchronous call. This error occurs when the backing data + * is provided from a remote source and the data is not yet available locally. + * + *

If the receiver of this error needs notification when the requested item + * becomes available (that is, when the asynchronous call completes), it must + * use the addResponder() method and specify + * an object that supports the mx.rpc.IResponder + * interface to respond when the item is available. + * The mx.collections.ItemResponder class implements the + * IResponder interface and supports a data property.

+ * + * @see mx.collections.ItemResponder + * @see mx.rpc.IResponder + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class ItemPendingError extends Error +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + *

Called by the Flex Framework when a request is made + * for an item that isn't local.

+ * + * @param message A message providing information about the error cause. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function ItemPendingError(message:String) + { + super(message); + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // responder + //---------------------------------- + + /** + * @private + */ + private var _responders:Array; + + /** + * An array of IResponder handlers that will be called when + * the asynchronous request completes. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get responders():Array + { + return _responders; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * addResponder adds a responder to an Array of responders. + * The object assigned to the responder parameter must implement the + * mx.rpc.IResponder interface. + * + * @param responder A handler which will be called when the asynchronous request completes. + * + * @see mx.rpc.IResponder + * @see mx.collections.ItemResponder + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function addResponder(responder:IResponder):void + { + if (!_responders) + _responders = []; + + _responders.push(responder); + } +} + +} diff --git a/src/mx/collections/errors/SortError.as b/src/mx/collections/errors/SortError.as new file mode 100644 index 00000000..431bf8a2 --- /dev/null +++ b/src/mx/collections/errors/SortError.as @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.collections.errors +{ + +/** + * This error is thrown when a Sort class is not configured properly; + * for example, if the find criteria are invalid. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class SortError extends Error +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param message A message providing information about the error cause. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function SortError(message:String) + { + super(message); + } +} + +} diff --git a/src/mx/core/AdvancedLayoutFeatures.as b/src/mx/core/AdvancedLayoutFeatures.as new file mode 100644 index 00000000..8965da85 --- /dev/null +++ b/src/mx/core/AdvancedLayoutFeatures.as @@ -0,0 +1,1138 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// +package mx.core +{ + import flash.events.Event; + import flash.geom.Matrix; + import flash.geom.Matrix3D; + import flash.geom.Point; + import flash.geom.Vector3D; + import flash.system.Capabilities; + + import mx.geom.CompoundTransform; + import mx.geom.TransformOffsets; + + use namespace mx_internal; + + /** + * @private + * Transform Offsets can be assigned to any Component or GraphicElement to modify the transform + * of the object beyond where its parent layout places it. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public class AdvancedLayoutFeatures implements IAssetLayoutFeatures + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function AdvancedLayoutFeatures() + { + layout = new CompoundTransform(); + } + + + + /** + * @private + * a flag for use by the owning object indicating whether the owning object has a pending update + * to the computed matrix. it is the owner's responsibility to set this flag. + */ + public var updatePending:Boolean = false; + + /** + * storage for the depth value. Layering is considered 'advanced' layout behavior, and not something + * that gets used by the majority of the components out there. So if a component has a non-zero depth, + * it will allocate a AdvancedLayoutFeatures object and store the value here. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var depth:Number = 0; + + /** + * @private + * slots for the various 2D and 3D matrices for layout, offset, and computed transforms. Note that + * these are only allocated and computed on demand -- many component instances will never use a 3D + * matrix, for example. + */ + protected var _computedMatrix:Matrix; + protected var _computedMatrix3D:Matrix3D; + + /** + * @private + * the layout visible transform as defined by the user and parent layout. + */ + protected var layout:CompoundTransform; + + /** + * @private + * offset values applied by the user + */ + private var _postLayoutTransformOffsets:TransformOffsets; + + /** + * @private + * bit field flags for indicating which transforms are valid -- the layout properties, the matrices, + * and the 3D matrices. Since developers can set any of the three programmatically, the last one set + * will always be valid, and the others will be invalid until validated on demand. + */ + private static const COMPUTED_MATRIX_VALID:uint = 0x1; + private static const COMPUTED_MATRIX3D_VALID:uint = 0x2; + + /** + * @private + * general storage for all of our flags. + */ + private var _flags:uint = 0; + + /** + * @private + * static data used by utility methods below + */ + private static var reVT:Vector3D = new Vector3D(0,0,0); + private static var reVR:Vector3D = new Vector3D(0,0,0); + private static var reVS:Vector3D = new Vector3D(1,1,1); + + private static var reV:Vector. = new Vector.(); + reV.push(reVT); + reV.push(reVR); + reV.push(reVS); + + + private static const RADIANS_PER_DEGREES:Number = Math.PI / 180; + + private static const ZERO_REPLACEMENT_IN_3D:Number = .00000000000001; + + private static var tempLocalPosition:Vector3D; + + /** + * @private + * a pointer to the function we use to transform vectors, to work around a bug + * in early versions of the flash player. + */ + private static var transformVector:Function = initTransformVectorFunction; + + /** + * @private + * an actionscript implementation to transform a vector by a matrix. Bugs in early versions of + * flash 10's implementation of Matrix.transformVector force us to do it ourselves in actionscript. + */ + private static function pre10_0_22_87_transformVector(m:Matrix3D,v:Vector3D):Vector3D + { + var r:Vector. = m.rawData; + return new Vector3D( + r[0] * v.x + r[4] * v.y + r[8] * v.z + r[12], + r[1] * v.x + r[5] * v.y + r[9] * v.z + r[13], + r[2] * v.x + r[6] * v.y + r[10] * v.z + r[14], + 1); + } + + /** + * @private + * a function to transform vectors using the built in player API, if we're in a late enough player version + * that we won't run into bugs.s + */ + private static function nativeTransformVector(m:Matrix3D,v:Vector3D):Vector3D + { + return m.transformVector(v); + } + + /** + * @private + * the first time someone calls transformVector, they'll get this function. It checks the player version, + * and if decides which implementation to use based on whether a bug is present or not. + */ + private static function initTransformVectorFunction(m:Matrix3D,v:Vector3D):Vector3D + { + var canUseNative:Boolean = false; + var version:Array = Capabilities.version.split(' ')[1].split(','); + if (parseFloat(version[0]) > 10) + canUseNative = true; + else if (parseFloat(version[1]) > 0) + canUseNative = true; + else if (parseFloat(version[2]) > 22) + canUseNative = true; + else if (parseFloat(version[3]) >= 87) + canUseNative = true; + if (canUseNative) + transformVector = nativeTransformVector; + else + transformVector = pre10_0_22_87_transformVector; + + return transformVector(m,v); + } + + + //------------------------------------------------------------------------------ + + /** + * layout transform convenience property. Represents the x value of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set layoutX(value:Number):void + { + layout.x = value; + invalidate(); + } + + /** + * @private + */ + public function get layoutX():Number + { + return layout.x; + } + /** + * layout transform convenience property. Represents the y value of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set layoutY(value:Number):void + { + layout.y = value; + invalidate(); + } + + /** + * @private + */ + public function get layoutY():Number + { + return layout.y; + } + + /** + * layout transform convenience property. Represents the z value of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set layoutZ(value:Number):void + { + layout.z = value; + invalidate(); + } + + /** + * @private + */ + public function get layoutZ():Number + { + return layout.z; + } + + //---------------------------------- + // layoutWidth + //---------------------------------- + + private var _layoutWidth:Number = 0; + + /** + * Used by the mirroring transform. See the mirror property. + * @default 0 + */ + public function get layoutWidth():Number + { + return _layoutWidth; + } + + /** + * @private + */ + public function set layoutWidth(value:Number):void + { + if (value == _layoutWidth) + return; + _layoutWidth = value; + invalidate(); + } + + + //------------------------------------------------------------------------------ + + /** + * @private + * the x value of the point around which any rotation and scale is performed in both the layout and computed matrix. + */ + public function set transformX(value:Number):void + { + layout.transformX = value; + invalidate(); + } + /** + * @private + */ + public function get transformX():Number + { + return layout.transformX; + } + + /** + * @private + * the y value of the point around which any rotation and scale is performed in both the layout and computed matrix. + */ + public function set transformY(value:Number):void + { + layout.transformY = value; + invalidate(); + } + + /** + * @private + */ + public function get transformY():Number + { + return layout.transformY; + } + + /** + * @private + * the z value of the point around which any rotation and scale is performed in both the layout and computed matrix. + */ + public function set transformZ(value:Number):void + { + layout.transformZ = value; + invalidate(); + } + + /** + * @private + */ + public function get transformZ():Number + { + return layout.transformZ; + } + +//------------------------------------------------------------------------------ + + + /** + * layout transform convenience property. Represents the rotation around the X axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set layoutRotationX(value:Number):void + { + layout.rotationX= value; + invalidate(); + } + + /** + * @private + */ + public function get layoutRotationX():Number + { + return layout.rotationX; + } + + /** + * layout transform convenience property. Represents the rotation around the Y axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set layoutRotationY(value:Number):void + { + layout.rotationY= value; + invalidate(); + } + + /** + * @private + */ + public function get layoutRotationY():Number + { + return layout.rotationY; + } + + /** + * layout transform convenience property. Represents the rotation around the Z axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set layoutRotationZ(value:Number):void + { + layout.rotationZ= value; + invalidate(); + } + + /** + * @private + */ + public function get layoutRotationZ():Number + { + return layout.rotationZ; + } + + //------------------------------------------------------------------------------ + + + /** + * layout transform convenience property. Represents the scale along the X axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set layoutScaleX(value:Number):void + { + layout.scaleX = value; + invalidate(); + } + + /** + * @private + */ + public function get layoutScaleX():Number + { + return layout.scaleX; + } + + /** + * layout transform convenience property. Represents the scale along the Y axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set layoutScaleY(value:Number):void + { + layout.scaleY= value; + invalidate(); + } + + /** + * @private + */ + public function get layoutScaleY():Number + { + return layout.scaleY; + } + + + /** + * layout transform convenience property. Represents the scale along the Z axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set layoutScaleZ(value:Number):void + { + layout.scaleZ= value; + invalidate(); + } + + /** + * @private + */ + public function get layoutScaleZ():Number + { + return layout.scaleZ; + } + + /** + * @private + * The 2D matrix used during layout calculations to determine the layout and size of the component and its parent and siblings. + * If the convenience properties are set, this matrix is built from those properties. + * If the matrix is set directly, the convenience properties will be updated to values derived from this matrix. + * This matrix is used in the calculation of the computed transform if possible. Under certain circumstances, such as when + * offsets are provided, the decomposed layout properties will be used instead. + */ + public function set layoutMatrix(value:Matrix):void + { + layout.matrix = value; + invalidate(); + } + + + /** + * @private + */ + public function get layoutMatrix():Matrix + { + return layout.matrix; + + } + + + /** + * @private + * The 3D matrix used during layout calculations to determine the layout and size of the component and its parent and siblings. + * This matrix is only used by parents that respect 3D layoyut. + * If the convenience properties are set, this matrix is built from those properties. + * If the matrix is set directly, the convenience properties will be updated to values derived from this matrix. + * This matrix is used in the calculation of the computed transform if possible. Under certain circumstances, such as when + * offsets are provided, the decomposed layout properties will be used instead. + */ + public function set layoutMatrix3D(value:Matrix3D):void + { + layout.matrix3D = value; + invalidate(); + } + + /** + * @private + */ + public function get layoutMatrix3D():Matrix3D + { + return layout.matrix3D; + } + + /** + * true if the computed transform has 3D values. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get is3D():Boolean + { + return (layout.is3D || (postLayoutTransformOffsets != null && postLayoutTransformOffsets.is3D)); + } + + /** + * true if the layout transform has 3D values. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get layoutIs3D():Boolean + { + return layout.is3D; + } + + //------------------------------------------------------------------------------ + + /** offsets to the transform convenience properties that are applied when a component is rendered. If this + * property is set, its values will be added to the layout transform properties to determine the true matrix used to render + * the component + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set postLayoutTransformOffsets(value:TransformOffsets):void + { + if (_postLayoutTransformOffsets != null) + { + _postLayoutTransformOffsets.removeEventListener(Event.CHANGE,postLayoutTransformOffsetsChangedHandler); + _postLayoutTransformOffsets.owner = null; + } + _postLayoutTransformOffsets = value; + if (_postLayoutTransformOffsets != null) + { + _postLayoutTransformOffsets.addEventListener(Event.CHANGE,postLayoutTransformOffsetsChangedHandler); + _postLayoutTransformOffsets.owner = this; + } + invalidate(); + } + + public function get postLayoutTransformOffsets():TransformOffsets + { + return _postLayoutTransformOffsets; + } + + private function postLayoutTransformOffsetsChangedHandler(e:Event):void + { + invalidate(); + } + + //---------------------------------- + // mirror + //---------------------------------- + + private var _mirror:Boolean = false; + + /** + * If true the X axis is scaled by -1 and the x coordinate of the origin + * is translated by the component's width. + * + * The net effect of this "mirror" transform is to flip the direction + * that the X axis increases in without changing the layout element's + * location relative to the parent's origin. + * + * @default false + */ + public function get mirror():Boolean + { + return _mirror; + } + + /** + * @private + */ + public function set mirror(value:Boolean):void + { + _mirror = value; + invalidate(); + } + + + //---------------------------------- + // stretchX + //---------------------------------- + + private var _stretchX:Number = 1; + + /** + * The stretchY is the horizontal component of the stretch scale factor which + * is applied before any other transformation property. + * @default 1 + */ + public function get stretchX():Number + { + return _stretchX; + } + + /** + * @private + */ + public function set stretchX(value:Number):void + { + if (value == _stretchX) + return; + _stretchX = value; + invalidate(); + } + + //---------------------------------- + // stretchY + //---------------------------------- + + private var _stretchY:Number = 1; + + /** + * The stretchY is the vertical component of the stretch scale factor which + * is applied before any other transformation property. + * @default 1 + */ + public function get stretchY():Number + { + return _stretchY; + } + + /** + * @private + */ + public function set stretchY(value:Number):void + { + if (value == _stretchY) + return; + _stretchY = value; + invalidate(); + } + + //------------------------------------------------------------------------------ + + /** + * @private + * invalidates our various cached values. Any change to the AdvancedLayoutFeatures object that affects + * the various transforms should call this function. + * @param reason - the code indicating what changes to cause the invalidation. + * @param affects3D - a flag indicating whether the change affects the 2D/3D nature of the various transforms. + * @param dispatchChangeEvent - if true, the AdvancedLayoutFeatures will dispatch a change indicating that its underlying transforms + * have been modified. + */ + private function invalidate():void + { + _flags &= ~COMPUTED_MATRIX_VALID; + _flags &= ~COMPUTED_MATRIX3D_VALID; + } + + + /** + * the computed matrix, calculated by combining the layout matrix and and any offsets provided.. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get computedMatrix():Matrix + { + if (_flags & COMPUTED_MATRIX_VALID) + return _computedMatrix; + + if (!postLayoutTransformOffsets && !mirror && stretchX == 1 && stretchY == 1) + { + return layout.matrix; + } + + var m:Matrix = _computedMatrix; + if (m == null) + m = _computedMatrix = new Matrix(); + else + m.identity(); + + var tx:Number = layout.transformX; + var ty:Number = layout.transformY; + var sx:Number = layout.scaleX; + var sy:Number = layout.scaleY; + var rz:Number = layout.rotationZ; + var x:Number = layout.x; + var y:Number = layout.y; + + if (mirror) + { + sx *= -1; + x += layoutWidth; + } + + if (postLayoutTransformOffsets) + { + sx *= postLayoutTransformOffsets.scaleX; + sy *= postLayoutTransformOffsets.scaleY; + rz += postLayoutTransformOffsets.rotationZ; + x += postLayoutTransformOffsets.x; + y += postLayoutTransformOffsets.y; + } + + if (stretchX != 1 || stretchY != 1) + m.scale(stretchX, stretchY); + build2DMatrix(m, tx, ty, sx, sy, rz, x, y); + + _flags |= COMPUTED_MATRIX_VALID; + return m; + } + + /** + * the computed 3D matrix, calculated by combining the 3D layout matrix and and any offsets provided.. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get computedMatrix3D():Matrix3D + { + if (_flags & COMPUTED_MATRIX3D_VALID) + return _computedMatrix3D; + + + if (!postLayoutTransformOffsets && !mirror && stretchX == 1 && stretchY == 1) + { + return layout.matrix3D; + } + + var m:Matrix3D = _computedMatrix3D; + if (m == null) + m = _computedMatrix3D = new Matrix3D(); + else + m.identity(); + + var tx:Number = layout.transformX; + var ty:Number = layout.transformY; + var tz:Number = layout.transformZ; + var sx:Number = layout.scaleX; + var sy:Number = layout.scaleY; + var sz:Number = layout.scaleZ; + var rx:Number = layout.rotationX; + var ry:Number = layout.rotationY; + var rz:Number = layout.rotationZ; + var x:Number = layout.x; + var y:Number = layout.y; + var z:Number = layout.z; + + if (mirror) + { + sx *= -1; + x += layoutWidth; + } + + if (postLayoutTransformOffsets) + { + sx *= postLayoutTransformOffsets.scaleX; + sy *= postLayoutTransformOffsets.scaleY; + sz *= postLayoutTransformOffsets.scaleZ; + rx += postLayoutTransformOffsets.rotationX; + ry += postLayoutTransformOffsets.rotationY; + rz += postLayoutTransformOffsets.rotationZ; + x += postLayoutTransformOffsets.x; + y += postLayoutTransformOffsets.y; + z += postLayoutTransformOffsets.z; + } + + build3DMatrix(m, tx, ty, tz, sx, sy, sz, rx, ry, rz, x, y, z); + // Always prepend last + if (stretchX != 1 || stretchY != 1) + m.prependScale(stretchX, stretchY, 1); + + _flags |= COMPUTED_MATRIX3D_VALID; + return m; + } + + + /** + * @private + * convenience function for building a 2D matrix from the convenience properties + */ + public static function build2DMatrix(m:Matrix, + tx:Number,ty:Number, + sx:Number,sy:Number, + rz:Number, + x:Number,y:Number):void + { + m.translate(-tx,-ty); + m.scale(sx,sy); + m.rotate(rz* RADIANS_PER_DEGREES); + m.translate(x+tx,y+ty); + } + + + /** + * @private + * convenience function for building a 3D matrix from the convenience properties + */ + public static function build3DMatrix(m:Matrix3D, + tx:Number,ty:Number,tz:Number, + sx:Number,sy:Number,sz:Number, + rx:Number,ry:Number,rz:Number, + x:Number,y:Number,z:Number):void + { + reVR.x = rx * RADIANS_PER_DEGREES; + reVR.y = ry * RADIANS_PER_DEGREES; + reVR.z = rz * RADIANS_PER_DEGREES; + m.recompose(reV); + if (sx == 0) + sx = ZERO_REPLACEMENT_IN_3D; + if (sy == 0) + sy = ZERO_REPLACEMENT_IN_3D; + if (sz == 0) + sz = ZERO_REPLACEMENT_IN_3D; + m.prependScale(sx,sy,sz); + m.prependTranslation(-tx,-ty,-tz); + m.appendTranslation(tx+x,ty+y,tz+z); + } + + + /** + * A utility method to transform a point specified in the local + * coordinates of this object to its location in the object's parent's + * coordinates. The pre-layout and post-layout result will be set on + * the position and postLayoutPosition + * parameters, if they are non-null. + * + * @param propertyIs3D A boolean reflecting whether the calculation needs + * to take into account the 3D matrix of the object. + * @param localPoint The point to be transformed, specified in the + * local coordinates of the object. + * @position A Vector3D point that will hold the pre-layout + * result. If null, the parameter is ignored. + * @postLayoutPosition A Vector3D point that will hold the post-layout + * result. If null, the parameter is ignored. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public function transformPointToParent(propertyIs3D:Boolean, + localPosition:Vector3D, position:Vector3D, + postLayoutPosition:Vector3D):void + { + var transformedV:Vector3D; + var transformedP:Point; + tempLocalPosition = + localPosition ? + localPosition.clone() : + new Vector3D(); + + if (is3D || propertyIs3D) + { + if (position != null) + { + transformedV = transformVector(layoutMatrix3D, tempLocalPosition); + position.x = transformedV.x; + position.y = transformedV.y; + position.z = transformedV.z; + } + + if (postLayoutPosition != null) + { + // computedMatrix factor in stretchXY, so divide it out of position first + tempLocalPosition.x /= stretchX; + tempLocalPosition.y /= stretchY; + transformedV = transformVector(computedMatrix3D, tempLocalPosition); + postLayoutPosition.x = transformedV.x; + postLayoutPosition.y = transformedV.y; + postLayoutPosition.z = transformedV.z; + } + } + else + { + var localP:Point = new Point(tempLocalPosition.x, + tempLocalPosition.y); + if (position != null) + { + transformedP = layoutMatrix.transformPoint(localP); + position.x = transformedP.x; + position.y = transformedP.y; + position.z = 0; + } + + if (postLayoutPosition != null) + { + // computedMatrix factor in stretchXY, so divide it out of position first + localP.x /= stretchX; + localP.y /= stretchY; + transformedP = computedMatrix.transformPoint(localP); + postLayoutPosition.x = transformedP.x; + postLayoutPosition.y = transformedP.y; + postLayoutPosition.z = 0; + } + } + } + + /** + * @private + * call when you've changed the inputs to the computed transform to make + * any adjustments to keep a particular point fixed in parent coordinates. + */ + private function completeTransformCenterAdjustment(changeIs3D:Boolean, + transformCenter:Vector3D, targetPosition:Vector3D, + targetPostLayoutPosition:Vector3D):void + { + // TODO (chaase): optimize for transformCenter == (0,0,0) + if (is3D || changeIs3D) + { + if (targetPosition != null) + { + var adjustedLayoutCenterV:Vector3D = transformVector(layoutMatrix3D, transformCenter); + if (adjustedLayoutCenterV.equals(targetPosition) == false) + { + layout.translateBy(targetPosition.x - adjustedLayoutCenterV.x, + targetPosition.y - adjustedLayoutCenterV.y, + targetPosition.z - adjustedLayoutCenterV.z); + invalidate(); + } + } + if (targetPostLayoutPosition != null && _postLayoutTransformOffsets != null) + { + // computedMatrix factor in stretchXY, so divide it out of transform center first + var tmpPos:Vector3D = new Vector3D(transformCenter.x, transformCenter.y, transformCenter.z); + tmpPos.x /= stretchX; + tmpPos.y /= stretchY; + var adjustedComputedCenterV:Vector3D = transformVector(computedMatrix3D, tmpPos); + if (adjustedComputedCenterV.equals(targetPostLayoutPosition) == false) + { + postLayoutTransformOffsets.x +=targetPostLayoutPosition.x - adjustedComputedCenterV.x; + postLayoutTransformOffsets.y += targetPostLayoutPosition.y - adjustedComputedCenterV.y; + postLayoutTransformOffsets.z += targetPostLayoutPosition.z - adjustedComputedCenterV.z; + invalidate(); + } + } + } + else + { + var transformCenterP:Point = new Point(transformCenter.x,transformCenter.y); + if (targetPosition != null) + { + var currentPositionP:Point = layoutMatrix.transformPoint(transformCenterP); + if (currentPositionP.x != targetPosition.x || + currentPositionP.y != targetPosition.y) + { + layout.translateBy(targetPosition.x - currentPositionP.x, + targetPosition.y - currentPositionP.y, 0); + invalidate(); + } + } + + if (targetPostLayoutPosition != null && _postLayoutTransformOffsets != null) + { + // computedMatrix factor in stretchXY, so divide it out of transform center first + transformCenterP.x /= stretchX; + transformCenterP.y /= stretchY; + var currentPostLayoutPosition:Point = + computedMatrix.transformPoint(transformCenterP); + if (currentPostLayoutPosition.x != targetPostLayoutPosition.x || + currentPostLayoutPosition.y != targetPostLayoutPosition.y) + { + _postLayoutTransformOffsets.x += targetPostLayoutPosition.x - currentPostLayoutPosition.x; + _postLayoutTransformOffsets.y += targetPostLayoutPosition.y - currentPostLayoutPosition.y; + invalidate(); + } + } + } + } + + private static var staticTranslation:Vector3D = new Vector3D(); + private static var staticOffsetTranslation:Vector3D = new Vector3D(); + + /** + * A utility method to update the rotation and scale of the transform + * while keeping a particular point, specified in the component's own + * coordinate space, fixed in the parent's coordinate space. This + * function will assign the rotation and scale values provided, then + * update the x/y/z properties as necessary to keep tx/ty/tz fixed. + * @param transformCenter the point, in the component's own coordinates, + * to keep fixed relative to its parent. + * @param rotation the new values for the rotation of the transform + * @param scale the new values for the scale of the transform + * @param translation the new values for the translation of the transform + */ + public function transformAround(transformCenter:Vector3D, + scale:Vector3D, + rotation:Vector3D, + transformCenterPosition:Vector3D, + postLayoutScale:Vector3D = null, + postLayoutRotation:Vector3D = null, + postLayoutTransformCenterPosition:Vector3D = null):void + { + var is3D:Boolean = (scale != null && scale.z != 1) || + (rotation != null && ((rotation.x != 0 ) || (rotation.y != 0))) || + (transformCenterPosition != null && transformCenterPosition.z != 0) || + (postLayoutScale != null && postLayoutScale.z != 1) || + (postLayoutRotation != null && + (postLayoutRotation.x != 0 || postLayoutRotation.y != 0)) || + (postLayoutTransformCenterPosition != null && postLayoutTransformCenterPosition.z != 0); + + var needOffsets:Boolean = _postLayoutTransformOffsets == null && + (postLayoutScale != null || postLayoutRotation != null || + postLayoutTransformCenterPosition != null); + if (needOffsets) + _postLayoutTransformOffsets = new TransformOffsets(); + + // now if they gave us a non-trivial transform center, and didn't tell us where they want it, + // we need to calculate where it is so that we can make sure we keep it there. + if (transformCenter != null && + (transformCenterPosition == null || postLayoutTransformCenterPosition == null)) + { + transformPointToParent(is3D, transformCenter, staticTranslation, + staticOffsetTranslation); + if (postLayoutTransformCenterPosition == null && transformCenterPosition != null) + { + staticOffsetTranslation.x = transformCenterPosition.x + staticOffsetTranslation.x - staticTranslation.x; + staticOffsetTranslation.y = transformCenterPosition.y + staticOffsetTranslation.y - staticTranslation.y; + staticOffsetTranslation.z = transformCenterPosition.z + staticOffsetTranslation.z - staticTranslation.z; + } + + } + // if targetPosition/postLayoutTargetPosition is null here, it might be because the caller passed in + // requested values, so we haven't calculated it yet. So that means our target position is the values + // they passed in. + var targetPosition:Vector3D = (transformCenterPosition == null)? staticTranslation:transformCenterPosition; + var postLayoutTargetPosition:Vector3D = (postLayoutTransformCenterPosition == null)? staticOffsetTranslation:postLayoutTransformCenterPosition; + + // now update our transform values. + if (rotation != null) + { + if (!isNaN(rotation.x)) + layout.rotationX = rotation.x; + if (!isNaN(rotation.y)) + layout.rotationY = rotation.y; + if (!isNaN(rotation.z)) + layout.rotationZ = rotation.z; + } + if (scale != null) + { + if (!isNaN(scale.x)) + layout.scaleX = scale.x; + if (!isNaN(scale.y)) + layout.scaleY = scale.y; + if (!isNaN(scale.z)) + layout.scaleZ = scale.z; + } + + if (postLayoutRotation != null) + { + _postLayoutTransformOffsets.rotationX = postLayoutRotation.x; + _postLayoutTransformOffsets.rotationY = postLayoutRotation.y; + _postLayoutTransformOffsets.rotationZ = postLayoutRotation.z; + } + if (postLayoutScale != null) + { + _postLayoutTransformOffsets.scaleX = postLayoutScale.x; + _postLayoutTransformOffsets.scaleY = postLayoutScale.y; + _postLayoutTransformOffsets.scaleZ = postLayoutScale.z; + } + + // if they didn't pass us a transform center, + // then we assume it's the origin. In that case, it's trivially easy + // to make sure the origin is at a particular point...we simply set + // the transformCenterPosition portion of our transforms to that point. + if (transformCenter == null) + { + if (transformCenterPosition != null) + { + layout.x = transformCenterPosition.x; + layout.y = transformCenterPosition.y; + layout.z = transformCenterPosition.z; + } + if (postLayoutTransformCenterPosition != null) + { + _postLayoutTransformOffsets.x = postLayoutTransformCenterPosition.x - layout.x; + _postLayoutTransformOffsets.y = postLayoutTransformCenterPosition.y - layout.y; + _postLayoutTransformOffsets.z = postLayoutTransformCenterPosition.z - layout.z; + } + } + invalidate(); + + // if they did pass in a transform center, go do the adjustments necessary to keep it fixed in place. + if (transformCenter != null) + completeTransformCenterAdjustment(is3D, transformCenter, + targetPosition, postLayoutTargetPosition); + + + } + +} +} + diff --git a/src/mx/core/BitmapAsset.as b/src/mx/core/BitmapAsset.as new file mode 100644 index 00000000..68a72fd4 --- /dev/null +++ b/src/mx/core/BitmapAsset.as @@ -0,0 +1,779 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +import flash.display.BitmapData; +import flash.display.DisplayObjectContainer; +import flash.events.Event; +import flash.geom.Point; +import flash.system.ApplicationDomain; + +/** + * BitmapAsset is a subclass of the flash.display.Bitmap class + * which represents bitmap images that you embed in a Flex application. + * It implements the IFlexDisplayObject interface, which makes it + * possible for an embedded bitmap image to be displayed in an Image control, + * or to be used as a container background or a component skin. + * + *

The bitmap image that you're embedding can be in a JPEG, GIF, + * or PNG file. + * You can also embed a bitmap symbol that is in a SWF file produced + * by Flash. + * In each of these cases, the MXML compiler autogenerates a class + * that extends BitmapAsset to represent the embedded bitmap image.

+ * + *

You don't generally have to use the BitmapAsset class directly + * when you write a Flex application. + * For example, you can embed a GIF file and display the image + * in an Image control by writing the gollowing:

+ * + *
+ *  <mx:Image id="logo" source="@Embed(source='Logo.gif')"/>
+ * + *

or use it as the application's background image in CSS syntax + * by writing

+ * + *
+ *  <fx:Style>
+ *      @namespace mx "library://ns.adobe.com/flex/mx"
+ *      mx|Application {
+ *          backgroundImage: Embed(source="Logo.gif")
+ *      }
+ *  <fx:Style/>
+ * + *

without having to understand that the MXML compiler has created + * a subclass of BitmapAsset for you.

+ * + *

However, it may be useful to understand what is happening + * at the ActionScript level. + * To embed a bitmap image in ActionScript, you declare a variable + * of type Class, and put [Embed] metadata on it. + * For example, you embed a GIF file like this:

+ * + *
+ *  [Bindable]
+ *  [Embed(source="Logo.gif")]
+ *  private var logoClass:Class;
+ * + *

The MXML compiler sees the .gif extension, transcodes the GIF data + * into the bitmap format that the player uses, autogenerates + * a subclass of the BitmapAsset class, and sets your variable + * to be a reference to this autogenerated class. + * You can then use this class reference to create instances of the + * BitmapAsset using the new operator, and you can use + * APIs of the BitmapAsset class on them:

+ * + *
+ *  var logo:BitmapAsset = BitmapAsset(new logoClass());
+ *  logo.bitmapData.noise(4);
+ * + *

However, you rarely need to create BitmapAsset instances yourself + * because image-related properties and styles can simply be set to an + * image-producing class, and components will create image instances + * as necessary. + * For example, to display this image in an Image control, you can + * set the Image's source property to logoClass. + * In MXML you could do this as follows:

+ * + *
+ *  <mx:Image id="logo" source="{logoClass}"/>
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class BitmapAsset extends FlexBitmap + implements IFlexAsset, IFlexDisplayObject, ILayoutDirectionElement +{ + include "../core/Version.as"; + + // Softlink FlexVersion and MatrixUtil to remove dependencies of embeds on + // framework classes. This helps to reduce swf size in AS-only projects. + private static var FlexVersionClass:Class; + private static var MatrixUtilClass:Class; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param bitmapData The data for the bitmap image. + * + * @param pixelSnapping Whether or not the bitmap is snapped + * to the nearest pixel. + * + * @param smoothing Whether or not the bitmap is smoothed when scaled. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function BitmapAsset(bitmapData:BitmapData = null, + pixelSnapping:String = "auto", + smoothing:Boolean = false) + { + super(bitmapData, pixelSnapping, smoothing); + + if (FlexVersionClass == null) + { + var appDomain:ApplicationDomain = ApplicationDomain.currentDomain; + if (appDomain.hasDefinition("mx.core::FlexVersion")) + FlexVersionClass = Class(appDomain.getDefinition("mx.core::FlexVersion")); + } + + if (FlexVersionClass && FlexVersionClass["compatibilityVersion"] >= FlexVersionClass["VERSION_4_0"]) + this.addEventListener(Event.ADDED, addedHandler); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + // Softlink AdvancedLayoutFeatures to remove dependencies of embeds on + // framework classes. This helps to reduce swf size in AS-only projects. + private var layoutFeaturesClass:Class; + private var layoutFeatures:IAssetLayoutFeatures; + + //-------------------------------------------------------------------------- + // + // Overridden Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // x + //---------------------------------- + + /** + * @private + */ + override public function get x():Number + { + // TODO(hmuller): by default get x returns transform.matrix.tx rounded to the nearest 20th. + // should do the same here, if we're returning layoutFeatures.layoutX. + return (layoutFeatures == null) ? super.x : layoutFeatures.layoutX; + } + + /** + * @private + */ + override public function set x(value:Number):void + { + if (x == value) + return; + + if (layoutFeatures == null) + { + super.x = value; + } + else + { + layoutFeatures.layoutX = value; + validateTransformMatrix(); + } + } + + //---------------------------------- + // y + //---------------------------------- + + /** + * @private + */ + override public function get y():Number + { + return (layoutFeatures == null) ? super.y : layoutFeatures.layoutY; + } + + /** + * @private + */ + override public function set y(value:Number):void + { + if (y == value) + return; + + if (layoutFeatures == null) + { + super.y = value; + } + else + { + layoutFeatures.layoutY = value; + validateTransformMatrix(); + } + } + + //---------------------------------- + // z + //---------------------------------- + + /** + * @private + */ + override public function get z():Number + { + return (layoutFeatures == null) ? super.z : layoutFeatures.layoutZ; + } + + /** + * @private + */ + override public function set z(value:Number):void + { + if (z == value) + return; + + if (layoutFeatures == null) + { + super.z = value; + } + else + { + layoutFeatures.layoutZ = value; + validateTransformMatrix(); + } + } + + //---------------------------------- + // width + //---------------------------------- + + /** + * @private + */ + override public function get width():Number + { + if (layoutFeatures == null) + return super.width; + + // Return bounding box width in mirroring case + var p:Point; + if (MatrixUtilClass != null) + p = MatrixUtilClass["transformSize"](layoutFeatures.layoutWidth, _height, transform.matrix); + + return p ? p.x : super.width; + } + + /** + * @private + */ + override public function set width(value:Number):void + { + if (width == value) + return; + + if (layoutFeatures == null) + { + super.width = value; + } + else + { + layoutFeatures.layoutWidth = value; + // Calculate scaleX based on initial width. We set scaleX + // here because resizing a BitmapAsset normally would adjust + // the scale to match. + layoutFeatures.layoutScaleX = measuredWidth != 0 ? value / measuredWidth : 0; + validateTransformMatrix(); + } + } + + //---------------------------------- + // height + //---------------------------------- + + private var _height:Number; + + /** + * @private + */ + override public function get height():Number + { + + if (layoutFeatures == null) + return super.height; + + // Return bounding box height in mirroring case + var p:Point; + if (MatrixUtilClass != null) + p = MatrixUtilClass["transformSize"](layoutFeatures.layoutWidth, _height, transform.matrix); + + return p ? p.y : super.height; + } + + /** + * @private + */ + override public function set height(value:Number):void + { + if (height == value) + return; + + if (layoutFeatures == null) + { + super.height = value; + } + else + { + _height = value; + // Calculate scaleY based on initial height. We set scaleY + // here because resizing a BitmapAsset normally would adjust + // the scale to match. + layoutFeatures.layoutScaleY = measuredHeight != 0 ? value / measuredHeight : 0; + validateTransformMatrix(); + } + } + + //---------------------------------- + // rotation + //---------------------------------- + + /** + * @private + */ + override public function get rotationX():Number + { + return (layoutFeatures == null) ? super.rotationX : layoutFeatures.layoutRotationX; + } + + /** + * @private + */ + override public function set rotationX(value:Number):void + { + if (rotationX == value) + return; + + if (layoutFeatures == null) + { + super.rotationX = value; + } + else + { + layoutFeatures.layoutRotationX = value; + validateTransformMatrix(); + } + } + /** + * @private + */ + override public function get rotationY():Number + { + return (layoutFeatures == null) ? super.rotationY : layoutFeatures.layoutRotationY; + } + + /** + * @private + */ + override public function set rotationY(value:Number):void + { + if (rotationY == value) + return; + + if (layoutFeatures == null) + { + super.rotationY = value; + } + else + { + layoutFeatures.layoutRotationY = value; + validateTransformMatrix(); + } + } + + /** + * @private + */ + override public function get rotationZ():Number + { + return (layoutFeatures == null) ? super.rotationZ : layoutFeatures.layoutRotationZ; + } + + /** + * @private + */ + override public function set rotationZ(value:Number):void + { + if (rotationZ == value) + return; + + if (layoutFeatures == null) + { + super.rotationZ = value; + } + else + { + layoutFeatures.layoutRotationZ = value; + validateTransformMatrix(); + } + } + + /** + * @private + */ + override public function get rotation():Number + { + return (layoutFeatures == null) ? super.rotation : layoutFeatures.layoutRotationZ; + } + + /** + * @private + */ + override public function set rotation(value:Number):void + { + if (rotation == value) + return; + + if (layoutFeatures == null) + { + super.rotation = value; + } + else + { + layoutFeatures.layoutRotationZ = value; + validateTransformMatrix(); + } + } + + //---------------------------------- + // scaleX + //---------------------------------- + + /** + * @private + */ + override public function get scaleX():Number + { + return (layoutFeatures == null) ? super.scaleX : layoutFeatures.layoutScaleX; + } + + /** + * @private + */ + override public function set scaleX(value:Number):void + { + if (scaleX == value) + return; + + if (layoutFeatures == null) + { + super.scaleX = value; + } + else + { + layoutFeatures.layoutScaleX = value; + layoutFeatures.layoutWidth = Math.abs(value) * measuredWidth; + validateTransformMatrix(); + } + } + + //---------------------------------- + // scaleY + //---------------------------------- + + /** + * @private + */ + override public function get scaleY():Number + { + return (layoutFeatures == null) ? super.scaleY : layoutFeatures.layoutScaleY; + } + + /** + * @private + */ + override public function set scaleY(value:Number):void + { + if (scaleY == value) + return; + + if (layoutFeatures == null) + { + super.scaleY = value; + } + else + { + layoutFeatures.layoutScaleY = value; + _height = Math.abs(value) * measuredHeight; + validateTransformMatrix(); + } + } + + //---------------------------------- + // scaleZ + //---------------------------------- + + /** + * @private + */ + override public function get scaleZ():Number + { + return (layoutFeatures == null) ? super.scaleZ : layoutFeatures.layoutScaleZ; + } + + /** + * @private + */ + override public function set scaleZ(value:Number):void + { + if (scaleZ == value) + return; + + if (layoutFeatures == null) + { + super.scaleZ = value; + } + else + { + layoutFeatures.layoutScaleZ = value; + validateTransformMatrix(); + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // layoutDirection + //---------------------------------- + + // Use "ltr" instead of LayoutDirection.LTR to avoid depending + // on that framework class. + private var _layoutDirection:String = "ltr"; + + [Inspectable(category="General", enumeration="ltr,rtl")] + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + public function get layoutDirection():String + { + return _layoutDirection; + } + + public function set layoutDirection(value:String):void + { + if (value == _layoutDirection) + return; + + _layoutDirection = value; + invalidateLayoutDirection(); + } + + //---------------------------------- + // measuredHeight + //---------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get measuredHeight():Number + { + if (bitmapData) + return bitmapData.height + + return 0; + } + + //---------------------------------- + // measuredWidth + //---------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get measuredWidth():Number + { + if (bitmapData) + return bitmapData.width; + + return 0; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + public function invalidateLayoutDirection():void + { + var p:DisplayObjectContainer = parent; + + // We check the closest parent's layoutDirection property + // to create or destroy layoutFeatures if needed. + while (p) + { + if (p is ILayoutDirectionElement) + { + // mirror is true if our layoutDirection differs from our parent's. + var mirror:Boolean = _layoutDirection != null && + ILayoutDirectionElement(p).layoutDirection != null && + (_layoutDirection != ILayoutDirectionElement(p).layoutDirection); + + // If our layoutDirection is different from our parent's and if it used to + // be the same, create layoutFeatures to handle mirroring. + if (mirror && layoutFeatures == null) + { + initAdvancedLayoutFeatures(); + if (layoutFeatures != null) + { + layoutFeatures.mirror = mirror; + validateTransformMatrix(); + } + } + else if (!mirror && layoutFeatures) + { + // If our layoutDirection is not different from our parent's and if + // it used to be different, then recover our matrix and remove layoutFeatures. + layoutFeatures.mirror = mirror; + validateTransformMatrix(); + layoutFeatures = null; + } + + break; + } + + p = p.parent; + } + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function move(x:Number, y:Number):void + { + this.x = x; + this.y = y; + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function setActualSize(newWidth:Number, newHeight:Number):void + { + width = newWidth; + height = newHeight; + } + + /** + * @private + */ + private function addedHandler(event:Event):void + { + invalidateLayoutDirection(); + } + + /** + * @private + * Initializes AdvancedLayoutFeatures for this asset when mirroring. + */ + private function initAdvancedLayoutFeatures():void + { + // Get AdvancedLayoutFeatures if it exists. + if (layoutFeaturesClass == null) + { + var appDomain:ApplicationDomain = ApplicationDomain.currentDomain; + + if (appDomain.hasDefinition("mx.core::AdvancedLayoutFeatures")) + layoutFeaturesClass = Class(appDomain.getDefinition("mx.core::AdvancedLayoutFeatures")); + + // Get MatrixUtil class if it exists + if (MatrixUtilClass == null) + { + if (appDomain.hasDefinition("mx.utils::MatrixUtil")) + MatrixUtilClass = Class(appDomain.getDefinition("mx.utils::MatrixUtil")); + } + } + + if (layoutFeaturesClass != null) + { + var features:IAssetLayoutFeatures = new layoutFeaturesClass(); + + features.layoutScaleX = scaleX; + features.layoutScaleY = scaleY; + features.layoutScaleZ = scaleZ; + features.layoutRotationX = rotationX; + features.layoutRotationY = rotationY; + features.layoutRotationZ = rotation; + features.layoutX = x; + features.layoutY = y; + features.layoutZ = z; + features.layoutWidth = width; // for the mirror transform + _height = height; // for backing storage + layoutFeatures = features; + } + } + + /** + * @private + * Applies the transform matrix calculated by AdvancedLayoutFeatures + * so that this bitmap will not be mirrored if a parent is mirrored. + */ + private function validateTransformMatrix():void + { + if (layoutFeatures != null) + { + if (layoutFeatures.is3D) + super.transform.matrix3D = layoutFeatures.computedMatrix3D; + else + super.transform.matrix = layoutFeatures.computedMatrix; + } + } +} + +} diff --git a/src/mx/core/ByteArrayAsset.as b/src/mx/core/ByteArrayAsset.as new file mode 100644 index 00000000..f5a2fc7e --- /dev/null +++ b/src/mx/core/ByteArrayAsset.as @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +import flash.utils.ByteArray; + +/** + * ByteArrayAsset is a subclass of the flash.utils.ByteArray class + * which represents an arbitrary sequence of byte data that you embed + * in a Flex application. + * + *

The byte data that you are embedding can be in any kind of file, + * and the entire file is always embedded. + * You cannot embed the bytes of a particular asset that is in a SWF file, + * although you can embed an entire SWF file.

+ * + *

The MXML compiler autogenerates a class that extends ByteArrayAsset + * to represent the embedded data.

+ * + *

To embed an arbitrary file, you declare a variable of type Class, + * and put [Embed] metadata on it, using the MIME type + * application/octet-stream. + * For example, you embed a text file like this:

+ * + *
+ *  [Bindable]
+ *  [Embed(source="Story.txt", mimeType="application/octet-stream")]
+ *  private var storyClass:Class;
+ *  
+ * + *

The compiler autogenerates a subclass of the ByteArrayAsset class + * and sets your variable to be a reference to this autogenerated class. + * You can then use this class reference to create instances of the + * ByteArrayAsset using the new operator, and you can extract + * information from the byte array using methods of the ByteArray class:

+ * + *
+ *  var storyByteArray:ByteArrayAsset = ByteArrayAsset(new storyClass());
+ *  var story:String = storyByteArray.readUTFBytes(storyByteArray.length);
+ *  
+ * + *

You must specify that the MIME type for the embedding is + * application/octet-stream, which causes the byte data + * to be embedded "as is", with no interpretation. + * It also causes the autogenerated class to extend ByteArrayAsset + * rather than another asset class.

+ * + *

For example, if you embed a PNG file without specifying this + * MIME type, the PNG data will be automatically transcoded + * into the bitmap format used by the player, and a subclass + * of BitmapAsset will be autogenerated to represent it. + * But if you specify the MIME type as application/octet-stream, + * then no transcoding will occur, the PNG data will be embedded + * as is, and the autogenerated class will extend ByteArrayAsset.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class ByteArrayAsset extends ByteArray implements IFlexAsset +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function ByteArrayAsset() + { + super(); + } +} + +} diff --git a/src/mx/core/ClassFactory.as b/src/mx/core/ClassFactory.as new file mode 100644 index 00000000..80906b23 --- /dev/null +++ b/src/mx/core/ClassFactory.as @@ -0,0 +1,186 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * A ClassFactory instance is a "factory object" which Flex uses + * to generate instances of another class, each with identical properties. + * + *

You specify a generator class when you construct + * the factory object. + * Then you set the properties property on the factory object. + * Flex uses the factory object to generate instances by calling + * the factory object's newInstance() method.

+ * + *

The newInstance() method creates a new instance + * of the generator class, and sets the properties specified + * by properties in the new instance. + * If you need to further customize the generated instances, + * you can override the newInstance() method.

+ * + *

The ClassFactory class implements the IFactory interface. + * Therefore it lets you create objects that can be assigned to properties + * of type IFactory, such as the itemRenderer property of a List control + * or the itemEditor property of a DataGrid control.

+ * + *

For example, suppose you write an item renderer class named + * ProductRenderer containing + * a showProductImage property which can be true + * or false. + * If you want to make a List control use this renderer, and have each renderer + * instance display a product image, you would write the following code:

+ * + *
+ *  var productRenderer:ClassFactory = new ClassFactory(ProductRenderer);
+ *  productRenderer.properties = { showProductImage: true };
+ *  myList.itemRenderer = productRenderer;
+ * + *

The List control calls the newInstance() method on the + * itemRenderer to create individual instances of ProductRenderer, + * each with showProductImage property set to true. + * If you want a different List control to omit the product images, you use + * the ProductRenderer class to create another ClassFactory + * with the properties property set to + * { showProductImage: false }.

+ * + *

Using the properties property to configure the instances + * can be powerful, since it allows a single generator class to be used + * in different ways. + * However, it is very common to create non-configurable generator classes + * which require no properties to be set. + * For this reason, MXML lets you use the following syntax:

+ * + *
+ *  <mx:List id="myList" itemRenderer="ProductRenderer">
+ * + *

The MXML compiler automatically creates the ClassFactory instance + * for you.

+ * + * @see mx.core.IFactory + * @see mx.controls.List + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class ClassFactory implements IFactory +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param generator The Class that the newInstance() method uses + * to generate objects from this factory object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function ClassFactory(generator:Class = null) + { + super(); + + this.generator = generator; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // generator + //---------------------------------- + + /** + * The Class that the newInstance() method uses + * to generate objects from this factory object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var generator:Class; + + //---------------------------------- + // properties + //---------------------------------- + + /** + * An Object whose name/value pairs specify the properties to be set + * on each object generated by the newInstance() method. + * + *

For example, if you set properties to + * { text: "Hello", width: 100 }, then every instance + * of the generator class that is generated by calling + * newInstance() will have its text set to + * "Hello" and its width set to + * 100.

+ * + * @default null + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var properties:Object = null; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Creates a new instance of the generator class, + * with the properties specified by properties. + * + *

This method implements the newInstance() method + * of the IFactory interface.

+ * + * @return The new instance that was created. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function newInstance():* + { + var instance:Object = new generator(); + + if (properties != null) + { + for (var p:String in properties) + { + instance[p] = properties[p]; + } + } + + return instance; + } +} + +} diff --git a/src/mx/core/DeferredInstanceFromClass.as b/src/mx/core/DeferredInstanceFromClass.as new file mode 100644 index 00000000..483cd774 --- /dev/null +++ b/src/mx/core/DeferredInstanceFromClass.as @@ -0,0 +1,117 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * A deferred instance factory that creates and returns an instance + * of a specified class. + * An application can use the getInstance() method to + * create an instance of the class when it is first needed and get + * a reference to the instance thereafter. + * + * @see DeferredInstanceFromFunction + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class DeferredInstanceFromClass implements ITransientDeferredInstance +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param generator The class whose instance the getInstance() + * method creates and returns. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function DeferredInstanceFromClass(generator:Class) + { + super(); + + this.generator = generator; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * The generator class. + */ + private var generator:Class; + + /** + * @private + * The generated value. + */ + private var instance:Object = null; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Creates and returns an instance of the class specified in the + * DeferredInstanceFromClass constructor, if it does not yet exist; + * otherwise, returns the already-created class instance. + * + * @return An instance of the class specified in the + * DeferredInstanceFromClass constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getInstance():Object + { + if (!instance) + instance = new generator(); + + return instance; + } + + /** + * Resets the state of our factory to the initial, uninitialized state. + * The reference to our cached instance is cleared. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4 + */ + public function reset():void + { + instance = null; + } +} + +} diff --git a/src/mx/core/DeferredInstanceFromFunction.as b/src/mx/core/DeferredInstanceFromFunction.as new file mode 100644 index 00000000..8e06affe --- /dev/null +++ b/src/mx/core/DeferredInstanceFromFunction.as @@ -0,0 +1,137 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * A deferred instance factory that uses a generator function + * to create an instance of the required object. + * An application uses the getInstance() method to + * create an instance of an object when it is first needed and get + * a reference to the object thereafter. + * + * @see DeferredInstanceFromClass + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class DeferredInstanceFromFunction implements ITransientDeferredInstance +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param generator A function that creates and returns an instance + * of the required object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function DeferredInstanceFromFunction(generator:Function, + destructor:Function = null ) + { + super(); + + this.generator = generator; + this.destructor = destructor; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * The generator function. + */ + private var generator:Function; + + /** + * @private + * The generated value. + */ + private var instance:Object = null; + + /** + * @private + * An optional function used to cleanup outstanding + * references when reset() is invoked + */ + private var destructor:Function; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Returns a reference to an instance of the desired object. + * If no instance of the required object exists, calls the function + * specified in this class' generator constructor parameter. + * + * @return An instance of the object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getInstance():Object + { + if (!instance) + instance = generator(); + + return instance; + } + + /** + * Resets the state of our factory to the initial, uninitialized state. + * The reference to our cached instance is cleared. + * + * @langversion 4.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4 + */ + public function reset():void + { + instance = null; + + if (destructor != null) + destructor(); + } + +} + +} diff --git a/src/mx/core/EdgeMetrics.as b/src/mx/core/EdgeMetrics.as new file mode 100644 index 00000000..c20d2e25 --- /dev/null +++ b/src/mx/core/EdgeMetrics.as @@ -0,0 +1,193 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2004-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * The EdgeMetrics class specifies the thickness, in pixels, + * of the four edge regions around a visual component. + * + *

The following Flex properties have values that are EdgeMetrics + * objects:

+ * + *
    + *
  • The borderMetrics property of the mx.core.Container and + * mx.skins.Border classes includes only the border in the calculations + * of the property values of the EdgeMetrics object.
  • + * + *
  • The viewMetrics property of the mx.core.Container + * class, and of subclasses of the Container class, includes possible + * scrollbars and non-content elements -- such as a Panel container's + * header area and the area for a ControlBar component -- in the calculations + * of the property values of the EdgeMetrics object.
  • + * + *
  • The viewMetricsAndPadding property of the + * mx.core.Container class includes the items listed for the + * viewMetrics property, plus the any areas defined by + * the margins of the container in the calculations of the + * property values of the EdgeMetrics object.
  • + *
+ * + *

These three properites all return a reference to the same + * EdgeMetrics object that the Container is using for its measurement + * and layout; they do not return a copy of this object. + * If you need a copy, call the clone() method.

+ * + * @see mx.core.Container + * @see mx.skins.Border + * @see mx.containers.Panel + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class EdgeMetrics +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * An EdgeMetrics object with a value of zero for its + * left, top, right, + * and bottom properties. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const EMPTY:EdgeMetrics = new EdgeMetrics(0, 0, 0, 0); + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param left The width, in pixels, of the left edge region. + * + * @param top The height, in pixels, of the top edge region. + * + * @param right The width, in pixels, of the right edge region. + * + * @param bottom The height, in pixels, of the bottom edge region. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function EdgeMetrics(left:Number = 0, top:Number = 0, + right:Number = 0, bottom:Number = 0) + { + super(); + + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // bottom + //---------------------------------- + + /** + * The height, in pixels, of the bottom edge region. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var bottom:Number; + + //---------------------------------- + // left + //---------------------------------- + + /** + * The width, in pixels, of the left edge region. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var left:Number; + + //---------------------------------- + // right + //---------------------------------- + + /** + * The width, in pixels, of the right edge region. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var right:Number; + + //---------------------------------- + // top + //---------------------------------- + + /** + * The height, in pixels, of the top edge region. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var top:Number; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Returns a copy of this EdgeMetrics object. + * + * @return A copy of this EdgeMetrics object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function clone():EdgeMetrics + { + return new EdgeMetrics(left, top, right, bottom); + } +} + +} diff --git a/src/mx/core/EventPriority.as b/src/mx/core/EventPriority.as new file mode 100644 index 00000000..234cc876 --- /dev/null +++ b/src/mx/core/EventPriority.as @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * The EventPriority class defines constant values + * for the priority argument of the + * addEventListener() method of EventDispatcher. + * + *

The higher the number, the higher the priority of the event listener. + * All listeners with priority N will be processed + * before listeners of priority N - 1. + * If two or more listeners share the same priority, + * they are processed in the order in which they were added.

+ * + *

Priorities can be positive, 0, or negative. + * The default priority is 0.

+ * + *

You should not write code that depends on the numeric values + * of these constants. + * They are subject to change in future versions of Flex.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public final class EventPriority +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * The CursorManager has handlers for mouse events + * which must be executed before other mouse event handlers, + * so they have a high priority. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const CURSOR_MANAGEMENT:int = 200; + + /** + * Autogenerated event handlers that evaluate data-binding expressions + * need to be executed before any others, so they have a higher priority + * than the default. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const BINDING:int = 100; + + /** + * Event handlers on component instances are executed with the default + * priority, 0. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const DEFAULT:int = 0; + + /** + * Some components listen to events that they dispatch on themselves + * and let other listeners call the preventDefault() + * method to tell the component not to perform a default action. + * Those components must listen with a lower priority than the default + * priority, so that the other handlers are executed first and have + * a chance to call preventDefault(). + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const DEFAULT_HANDLER:int = -50; + + /** + * Autogenerated event handlers that trigger effects are executed + * after other event handlers on component instances, so they have + * a lower priority than the default. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const EFFECT:int = -100; +} + +} diff --git a/src/mx/core/FlexBitmap.as b/src/mx/core/FlexBitmap.as new file mode 100644 index 00000000..aab88429 --- /dev/null +++ b/src/mx/core/FlexBitmap.as @@ -0,0 +1,122 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +import flash.display.Bitmap; +import flash.display.BitmapData; + +import mx.utils.NameUtil; + +/** + * FlexBitmap is a subclass of the Player's Bitmap class. + * It overrides the toString() method + * to return a string indicating the location of the object + * within the hierarchy of DisplayObjects in the application. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class FlexBitmap extends Bitmap +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + *

Sets the name property to a string + * returned by the createUniqueName() + * method of the mx.utils.NameUtils class. + * This string is the name of the object's class concatenated + * with an integer that is unique within the application, + * such as "FlexBitmap12".

+ * + * @param bitmapData The data for the bitmap. + * + * @param pixelSnapping Whether or not the bitmap is snapped + * to the nearest pixel. + * + * @param smoothing Whether or not the bitmap is smoothed when scaled. + * + * @see flash.display.DisplayObject#name + * @see mx.utils.NameUtil#createUniqueName() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function FlexBitmap(bitmapData:BitmapData = null, + pixelSnapping:String = "auto", + smoothing:Boolean = false) + { + super(bitmapData, pixelSnapping, smoothing); + + try + { + name = NameUtil.createUniqueName(this); + } + catch(e:Error) + { + // The name assignment above can cause the RTE + // Error #2078: The name property of a Timeline-placed + // object cannot be modified. + // if this class has been associated with an asset + // that was created in the Flash authoring tool. + // The only known case where this is a problem is when + // an asset has another asset PlaceObject'd onto it and + // both are embedded separately into a Flex application. + // In this case, we ignore the error and toString() will + // use the name assigned in the Flash authoring tool. + } + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * Returns a string indicating the location of this object + * within the hierarchy of DisplayObjects in the Application. + * This string, such as "MyApp0.HBox5.FlexBitmap12", + * is built by the displayObjectToString() method + * of the mx.utils.NameUtils class from the name + * property of the object and its ancestors. + * + * @return A String indicating the location of this object + * within the DisplayObject hierarchy. + * + * @see flash.display.DisplayObject#name + * @see mx.utils.NameUtil#displayObjectToString() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + override public function toString():String + { + return NameUtil.displayObjectToString(this); + } +} + +} diff --git a/src/mx/core/FlexSprite.as b/src/mx/core/FlexSprite.as new file mode 100644 index 00000000..f749fe20 --- /dev/null +++ b/src/mx/core/FlexSprite.as @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +import flash.display.Sprite; +import mx.utils.NameUtil; + +/** + * FlexSprite is a subclass of the Player's Sprite class + * and the superclass of UIComponent. + * It overrides the toString() method + * to return a string indicating the location of the object + * within the hierarchy of DisplayObjects in the application. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class FlexSprite extends Sprite +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + *

Sets the name property to a string + * returned by the createUniqueName() + * method of the mx.utils.NameUtils class.

+ * + *

This string is the name of the object's class concatenated + * with an integer that is unique within the application, + * such as "Button17".

+ * + * @see flash.display.DisplayObject#name + * @see mx.utils.NameUtil#createUniqueName() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function FlexSprite() + { + super(); + + try + { + name = NameUtil.createUniqueName(this); + } + catch(e:Error) + { + // The name assignment above can cause the RTE + // Error #2078: The name property of a Timeline-placed + // object cannot be modified. + // if this class has been associated with an asset + // that was created in the Flash authoring tool. + // The only known case where this is a problem is when + // an asset has another asset PlaceObject'd onto it and + // both are embedded separately into a Flex application. + // In this case, we ignore the error and toString() will + // use the name assigned in the Flash authoring tool. + } + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * Returns a string indicating the location of this object + * within the hierarchy of DisplayObjects in the Application. + * This string, such as "MyApp0.HBox5.Button17", + * is built by the displayObjectToString() method + * of the mx.utils.NameUtils class from the name + * property of the object and its ancestors. + * + * @return A String indicating the location of this object + * within the DisplayObject hierarchy. + * + * @see flash.display.DisplayObject#name + * @see mx.utils.NameUtil#displayObjectToString() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + override public function toString():String + { + return NameUtil.displayObjectToString(this); + } +} + +} diff --git a/src/mx/core/IAssetLayoutFeatures.as b/src/mx/core/IAssetLayoutFeatures.as new file mode 100644 index 00000000..83cea354 --- /dev/null +++ b/src/mx/core/IAssetLayoutFeatures.as @@ -0,0 +1,366 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// +package mx.core +{ +import flash.geom.Matrix; +import flash.geom.Matrix3D; + +/** + * The IAssetLayoutFeatures interface defines the minimum properties and methods + * required for an Object to support advanced transforms in embedded assets. + * + * @see mx.core.AdvancedLayoutFeatures + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ +public interface IAssetLayoutFeatures +{ + + /** + * Layout transform convenience property. Represents the x value of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutX(value:Number):void; + + /** + * @private + */ + function get layoutX():Number; + + /** + * Layout transform convenience property. Represents the y value of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutY(value:Number):void; + + /** + * @private + */ + function get layoutY():Number; + + /** + * Layout transform convenience property. Represents the z value of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutZ(value:Number):void; + + /** + * @private + */ + function get layoutZ():Number; + + /** + * Used by the mirroring transform. See the mirror property. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function get layoutWidth():Number; + + /** + * @private + */ + function set layoutWidth(value:Number):void; + + //------------------------------------------------------------------------------ + + /** + * The x value of the point around which any rotation and scale is performed in both the layout and computed matrix. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set transformX(value:Number):void; + /** + * @private + */ + function get transformX():Number; + + /** + * The y value of the point around which any rotation and scale is performed in both the layout and computed matrix. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set transformY(value:Number):void; + + /** + * @private + */ + function get transformY():Number; + + /** + * The z value of the point around which any rotation and scale is performed in both the layout and computed matrix. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set transformZ(value:Number):void; + + /** + * @private + */ + function get transformZ():Number; + + //------------------------------------------------------------------------------ + + /** + * Layout transform convenience property. Represents the rotation around the X axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutRotationX(value:Number):void; + + /** + * @private + */ + function get layoutRotationX():Number; + + /** + * Layout transform convenience property. Represents the rotation around the Y axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutRotationY(value:Number):void; + + /** + * @private + */ + function get layoutRotationY():Number; + + /** + * Layout transform convenience property. Represents the rotation around the Z axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutRotationZ(value:Number):void; + + /** + * @private + */ + function get layoutRotationZ():Number; + + //------------------------------------------------------------------------------ + + /** + * Layout transform convenience property. Represents the scale along the X axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutScaleX(value:Number):void; + + /** + * @private + */ + function get layoutScaleX():Number; + + /** + * Layout transform convenience property. Represents the scale along the Y axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutScaleY(value:Number):void; + + /** + * @private + */ + function get layoutScaleY():Number; + + /** + * Layout transform convenience property. Represents the scale along the Z axis of the layout matrix used in layout and in + * the computed transform. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutScaleZ(value:Number):void; + + /** + * @private + */ + function get layoutScaleZ():Number; + + /** + * The 2D matrix used during layout calculations to determine the layout and size of the component and its parent and siblings. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutMatrix(value:Matrix):void; + + /** + * @private + */ + function get layoutMatrix():Matrix; + + /** + * The 3D matrix used during layout calculations to determine the layout and size of the component and its parent and siblings. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function set layoutMatrix3D(value:Matrix3D):void; + + /** + * @private + */ + function get layoutMatrix3D():Matrix3D; + + /** + * True if the computed transform has 3D values. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function get is3D():Boolean; + + /** + * True if the layout transform has 3D values. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function get layoutIs3D():Boolean; + + /** + * If true the X axis is scaled by -1 and the x coordinate of the origin + * is translated by the component's width. + * + * The net effect of this "mirror" transform is to flip the direction + * that the X axis increases in without changing the layout element's + * location relative to the parent's origin. + * + * @default false + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function get mirror():Boolean; + + /** + * @private + */ + function set mirror(value:Boolean):void; + + + /** + * The stretchY is the horizontal component of the stretch scale factor which + * is applied before any other transformation property. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function get stretchX():Number; + + /** + * @private + */ + function set stretchX(value:Number):void; + + /** + * The stretchY is the vertical component of the stretch scale factor which + * is applied before any other transformation property. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function get stretchY():Number; + + /** + * @private + */ + function set stretchY(value:Number):void; + + //------------------------------------------------------------------------------ + + /** + * The computed matrix, calculated by combining the layout matrix and any offsets provided. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function get computedMatrix():Matrix; + + /** + * The computed 3D matrix, calculated by combining the 3D layout matrix and any offsets provided. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function get computedMatrix3D():Matrix3D; +} +} diff --git a/src/mx/core/IBorder.as b/src/mx/core/IBorder.as new file mode 100644 index 00000000..2eb9e27a --- /dev/null +++ b/src/mx/core/IBorder.as @@ -0,0 +1,62 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * The IBorder interface defines the interface that all classes + * used for border skins should implement. + * + *

It is not an error if the border skin does not implement IBorder. + * In this case, however, the container using the skin cannot determine + * the border metrics of the border. + * Therefore, the container places content starting at its top-left edge + * (adjusted for padding, if any). + * For the HaloBorder class, the borderThickness style + * usually determines the value of the borderMetrics style. + * For graphical skin classes, Flex examines the scale9Grid + * property to determine the value of the borderMetrics style.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IBorder +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // borderMetrics + //---------------------------------- + + /** + * Returns an EdgeMetrics object for the border that has four properties: + * left, top, right, + * and bottom. + * The value of each property is equal to the thickness of one side + * of the border, in pixels. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get borderMetrics():EdgeMetrics; + +} + +} diff --git a/src/mx/core/IDataRenderer.as b/src/mx/core/IDataRenderer.as new file mode 100644 index 00000000..fb60b279 --- /dev/null +++ b/src/mx/core/IDataRenderer.as @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * The IDataRenderer interface defines the interface for components that have a data property. + * + *

Components that are used in an item renderer or item editor + * in a list control (such as the List, HorizontalList, TileList, DataGrid, + * and Tree controls), or as renderers in a chart are passed the data + * to render or edit by using the data property. + * The component must implement IDataRenderer so that the host components + * can pass this information. + * All Flex containers and many Flex components implement IDataRenderer and + * the data property.

+ * + *

In a list control, Flex sets the data property + * of an item renderer or item editor to the element in the data provider + * that corresponds to the item being rendered or edited. + * For a DataGrid control, the data property + * contains the data provider element for the entire row of the DataGrid + * control, not just for the item.

+ * + *

To implement this interface, you define a setter and getter method + * to implement the data property. + * Typically, the setter method writes the value of the data + * property to an internal variable and dispatches a dataChange + * event, and the getter method returns the current value of the internal + * variable, as the following example shows:

+ * + *
+ *    // Internal variable for the property value.
+ *    private var _data:Object;
+ *    
+ *    // Make the data property bindable.
+ *    [Bindable("dataChange")]
+ *    
+ *    // Define the getter method.
+ *    public function get data():Object {
+ *        return _data;
+ *    }
+ *    
+ *    // Define the setter method, and dispatch an event when the property
+ *    // changes to support data binding.
+ *    public function set data(value:Object):void {
+ *        _data = value;
+ *    
+ *        dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
+ *    }
+ *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IDataRenderer +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // data + //---------------------------------- + + /** + * The data to render or edit. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get data():Object; + + /** + * @private + */ + function set data(value:Object):void; +} + +} diff --git a/src/mx/core/IDeferredInstance.as b/src/mx/core/IDeferredInstance.as new file mode 100644 index 00000000..c9500bee --- /dev/null +++ b/src/mx/core/IDeferredInstance.as @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * The IDeferredInstance interface defines the Flex deferred instance factory interface. + * An implementation of IDeferredInstance creates a particular instance value + * when the getInstance() method is first called, and returns a reference to that value + * when the getInstance() method is called subsequently. + * + *

The Flex compiler performs the following automatic coercions when it + * encounters MXML that assigns a value to a property with the + * IDeferredInstance type:

+ * + *
    + *
  1. If you assign a property of type IDeferredInstance a value that is + * an MXML child tag representing a class, such as a component + * tag, the compiler creates an IDeferredInstance implementation + * whose getInstance() method returns an instance + * of the class, configured as specified in the MXML code. + * The following example shows this format; in this example, + * MyComp is a custom component that has a variable called + * myDeferredInstanceProperty of type IDeferredInstance. The compiler + * generates an IDeferredInstance1 implementation whose + * getInstance() method returns an instance of the + * Label class, with its text property set to + * "This is a deferred label": + *
    + *          <MyComp>
    + *              <myDeferredInstanceProperty>
    + *                  <Label text="This is a deferred label"/>
    + *              </myDeferredInstanceProperty>
    + *          </MyComp>
    + *
  2. + *
  3. If you assign a text string to a property of type IDeferredInstance, + * the compiler interprets the string as a fully qualified class name, + * and creates an + * IDeferredInstance implementation whose getInstance() + * method returns a new instance of the specified class. + * The specified class must have a constructor with no arguments. + * The following example shows this format; in this example, the compiler + * generates an IDeferredInstance1 implementation whose + * getInstance() method returns an instance of the + * MyClass class: + *
    + *          <MyComp myDeferredInstanceProperty="myPackage.MyClass/>
    + *
  4. + *
+ * + *

Use the IDeferredInstance interface when an ActionScript class defers + * the instantiation of a property value. + * You cannot use IDeferredInstance if an ActionScript class requires + * multiple instances of the same value. + * In those situations, use the IFactory interface.

+ * + *

The states.AddChild class includes a childFactory + * property that is of type IDeferredInstance.

+ * + * @see mx.states.AddChild + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IDeferredInstance +{ + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Creates an instance Object from a class or function, + * if the instance does not yet exist. + * + * @return The instance Object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getInstance():Object; +} + +} diff --git a/src/mx/core/IDisplayObjectInterface.as b/src/mx/core/IDisplayObjectInterface.as new file mode 100644 index 00000000..b34e8a95 --- /dev/null +++ b/src/mx/core/IDisplayObjectInterface.as @@ -0,0 +1,364 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +/* + * The methods here would normally just be in IDisplayObject, + * but for backward compatibility, they have to be included + * directly into IFlexDisplayObject, so they are kept in + * this separate include file. + */ + + /** + * @copy flash.display.DisplayObject#root + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get root():DisplayObject; + + + /** + * @copy flash.display.DisplayObject#stage + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get stage():Stage; + + + /** + * @copy flash.display.DisplayObject#name + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get name():String; + function set name(value:String):void; + + + /** + * @copy flash.display.DisplayObject#parent + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get parent():DisplayObjectContainer; + + + /** + * @copy flash.display.DisplayObject#mask + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get mask():DisplayObject; + function set mask(value:DisplayObject):void; + + + /** + * @copy flash.display.DisplayObject#visible + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get visible():Boolean; + function set visible(value:Boolean):void; + + + /** + * @copy flash.display.DisplayObject#x + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get x():Number; + function set x(value:Number):void; + + + /** + * @copy flash.display.DisplayObject#y + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get y():Number; + function set y(value:Number):void; + + + /** + * @copy flash.display.DisplayObject#scaleX + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get scaleX():Number; + function set scaleX(value:Number):void; + + + /** + * @copy flash.display.DisplayObject#scaleY + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get scaleY():Number; + function set scaleY(value:Number):void; + + + /** + * @copy flash.display.DisplayObject#mouseX + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get mouseX():Number; // note: no setter + + + /** + * @copy flash.display.DisplayObject#mouseY + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get mouseY():Number; // note: no setter + + + /** + * @copy flash.display.DisplayObject#rotation + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get rotation():Number; + function set rotation(value:Number):void; + + + /** + * @copy flash.display.DisplayObject#alpha + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get alpha():Number; + function set alpha(value:Number):void; + + + /** + * @copy flash.display.DisplayObject#width + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get width():Number; + function set width(value:Number):void; + + /** + * @copy flash.display.DisplayObject#height + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get height():Number; + function set height(value:Number):void; + + + /** + * @copy flash.display.DisplayObject#cacheAsBitmap + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get cacheAsBitmap():Boolean; + function set cacheAsBitmap(value:Boolean):void; + + /** + * @copy flash.display.DisplayObject#opaqueBackground + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get opaqueBackground():Object; + function set opaqueBackground(value:Object):void; + + + /** + * @copy flash.display.DisplayObject#scrollRect + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get scrollRect():Rectangle; + function set scrollRect(value:Rectangle):void; + + + /** + * @copy flash.display.DisplayObject#filters + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get filters():Array; + function set filters(value:Array):void; + + /** + * @copy flash.display.DisplayObject#blendMode + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get blendMode():String; + function set blendMode(value:String):void; + + /** + * @copy flash.display.DisplayObject#transform + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get transform():Transform; + function set transform(value:Transform):void; + + /** + * @copy flash.display.DisplayObject#scale9Grid + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get scale9Grid():Rectangle; + function set scale9Grid(innerRectangle:Rectangle):void; + + /** + * @copy flash.display.DisplayObject#globalToLocal() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function globalToLocal(point:Point):Point; + + /** + * @copy flash.display.DisplayObject#localToGlobal() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function localToGlobal(point:Point):Point; + + /** + * @copy flash.display.DisplayObject#getBounds() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getBounds(targetCoordinateSpace:DisplayObject):Rectangle; + + /** + * @copy flash.display.DisplayObject#getRect() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getRect(targetCoordinateSpace:DisplayObject):Rectangle; + + /** + * @copy flash.display.DisplayObject#loaderInfo + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get loaderInfo() : LoaderInfo; + + /** + * @copy flash.display.DisplayObject#hitTestObject() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function hitTestObject(obj:DisplayObject):Boolean; + + /** + * @copy flash.display.DisplayObject#hitTestPoint() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function hitTestPoint(x:Number, y:Number, shapeFlag:Boolean=false):Boolean; + + /** + * @copy flash.display.DisplayObject#accessibilityProperties + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get accessibilityProperties() : AccessibilityProperties; + function set accessibilityProperties( value : AccessibilityProperties ) : void; + diff --git a/src/mx/core/IFactory.as b/src/mx/core/IFactory.as new file mode 100644 index 00000000..df336244 --- /dev/null +++ b/src/mx/core/IFactory.as @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * The IFactory interface defines the interface that factory classes + * such as ClassFactory must implement. + * An object of type IFactory is a "factory object" which Flex uses + * to generate multiple instances of another class, each with identical + * properties. + * + *

For example, a DataGridColumn has an itemRenderer of type + * IFactory; it calls itemRenderer.newInstance() to create + * the cells for a particular column of the DataGrid.

+ * + * @see mx.core.ClassFactory + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IFactory +{ + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Creates an instance of some class (determined by the class that + * implements IFactory). + * + * @return The newly created instance. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function newInstance():*; +} + +} diff --git a/src/mx/core/IFlexAsset.as b/src/mx/core/IFlexAsset.as new file mode 100644 index 00000000..26c55133 --- /dev/null +++ b/src/mx/core/IFlexAsset.as @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * IFlexAsset is a marker interface with the following meaning: + * if a class declares that it implements IFlexAsset, + * then that class represents an asset -- such as a bitmap, a font, + * or a sound -- that has been embedded in a Flex application. + * This interface does not define any properties or methods that the + * class must actually implement. + * + *

The player uses ActionScript classes to represent + * embedded assets as well as executable ActionScript code. + * When you embed an asset in a Flex application, the MXML compiler + * autogenerates a class to represent it, and all such classes + * declare that they implement IFlexAsset so that they can be + * distinguished from the code classes.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IFlexAsset +{ +} + +} diff --git a/src/mx/core/IFlexDisplayObject.as b/src/mx/core/IFlexDisplayObject.as new file mode 100644 index 00000000..9decfe1d --- /dev/null +++ b/src/mx/core/IFlexDisplayObject.as @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +import flash.accessibility.AccessibilityProperties; +import flash.display.DisplayObject; +import flash.display.DisplayObjectContainer; +import flash.display.IBitmapDrawable; +import flash.display.LoaderInfo; +import flash.display.Stage; +import flash.events.IEventDispatcher; +import flash.geom.Rectangle; +import flash.geom.Point; +import flash.geom.Transform; + +/** + * The IFlexDisplayObject interface defines the interface for skin elements. + * At a minimum, a skin must be a DisplayObject and implement this interface. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IFlexDisplayObject extends IBitmapDrawable, IEventDispatcher +{ + +include "IDisplayObjectInterface.as" + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + + //---------------------------------- + // measuredHeight + //---------------------------------- + + /** + * The measured height of this object. + * + *

This is typically hard-coded for graphical skins + * because this number is simply the number of pixels in the graphic. + * For code skins, it can also be hard-coded + * if you expect to be drawn at a certain size. + * If your size can change based on properties, you may want + * to also be an ILayoutManagerClient so a measure() + * method will be called at an appropriate time, + * giving you an opportunity to compute a measuredHeight.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get measuredHeight():Number; + + //---------------------------------- + // measuredWidth + //---------------------------------- + + /** + * The measured width of this object. + * + *

This is typically hard-coded for graphical skins + * because this number is simply the number of pixels in the graphic. + * For code skins, it can also be hard-coded + * if you expect to be drawn at a certain size. + * If your size can change based on properties, you may want + * to also be an ILayoutManagerClient so a measure() + * method will be called at an appropriate time, + * giving you an opportunity to compute a measuredHeight.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get measuredWidth():Number; + + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Moves this object to the specified x and y coordinates. + * + * @param x The new x-position for this object. + * + * @param y The new y-position for this object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function move(x:Number, y:Number):void; + + /** + * Sets the actual size of this object. + * + *

This method is mainly for use in implementing the + * updateDisplayList() method, which is where + * you compute this object's actual size based on + * its explicit size, parent-relative (percent) size, + * and measured size. + * You then apply this actual size to the object + * by calling setActualSize().

+ * + *

In other situations, you should be setting properties + * such as width, height, + * percentWidth, or percentHeight + * rather than calling this method.

+ * + * @param newWidth The new width for this object. + * + * @param newHeight The new height for this object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function setActualSize(newWidth:Number, newHeight:Number):void; +} + +} diff --git a/src/mx/core/IFlexModuleFactory.as b/src/mx/core/IFlexModuleFactory.as new file mode 100644 index 00000000..c45f79ee --- /dev/null +++ b/src/mx/core/IFlexModuleFactory.as @@ -0,0 +1,268 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * The IFlexModuleFactory interface represents the contract expected + * for bootstrapping Flex applications and dynamically loaded + * modules. + * + *

Calling the info() method is legal immediately after + * the complete event is dispatched.

+ * + *

A well-behaved module dispatches a ready event when + * it is safe to call the create() method.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IFlexModuleFactory +{ + import flash.display.LoaderInfo; + import flash.utils.Dictionary; + + import mx.core.RSLData; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * Controls whether the domains allowed by calls to allowDomain() + * are also allowed by RSLs loaded after the call. Additional RSLs + * may be loaded into this module factory by sub-applications or modules. + * + * @default true + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Flex 4.5 + */ + function get allowDomainsInNewRSLs():Boolean; + function set allowDomainsInNewRSLs(value:Boolean):void; + + /** + * Controls whether the domains allowed by calls to allowInsecureDomain() + * are also allowed by RSLs loaded after the call. Additional RSLs + * may be added to this module factory by sub-applications or modules. + * + * @default true + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Flex 4.5 + */ + function get allowInsecureDomainsInNewRSLs():Boolean; + function set allowInsecureDomainsInNewRSLs(value:Boolean):void; + + /** + * The RSLs loaded by this SystemManager or FlexModuleFactory before the + * application starts. This dictionary may also include RSLs loaded into this + * module factory's application domain by other modules or + * sub-applications. When a new dictionary entry is added by a child module + * factory an RSLEvent.RSL_ADD_PRELOADED event is dispatched + * by module factory owning the dictionary. + * + * Information about preloadedRSLs is stored in a Dictionary. The key is + * the RSL's LoaderInfo. The value is the a Vector of RSLData where the + * first element is the primary RSL and the remaining elements are + * failover RSLs. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 3 + */ + function get preloadedRSLs():Dictionary; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Adds an RSL to the preloadedRSLs list. This method is called by child + * module factories when they add load an RSL into this module factory's + * application domain. + * + *

You do not call this method directly. This method is called by child + * module factories when they add load an RSL into this module factory's + * application domain.

+ * + * @param loaderInfo The loaderInfo of the loaded RSL. + * @param rsl The RSL's configuration information. A Vector of RSLData. + * The first element in the array is the primary RSL. The remaining + * elements are failover RSLs. + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Flex 4.5 + */ + function addPreloadedRSL(loaderInfo:LoaderInfo, rsl:Vector.):void; + + /** + * Calls the Security.allowDomain() method for the SWF + * associated with this IFlexModuleFactory plus all the SWFs associated + * with RSLs preloaded by this IFlexModuleFactory. RSLs loaded after this + * call will, by default, allow the same domains as have been allowed by + * previous calls to this method. This behavior is controlled by the + * allowDomainsInNewRSLs property. + * + * @param domains One or more strings or URLRequest objects that name + * the domains from which you want to allow access. + * You can specify the special domain "*" to allow access from all domains. + * + * @see flash.system.Security#allowDomain() + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Flex 4.5 + */ + function allowDomain(... domains):void; + + /** + * Calls the Security.allowInsecureDomain() method for the + * SWF associated with this IFlexModuleFactory + * plus all the SWFs associated with RSLs preloaded by this + * IFlexModuleFactory. RSLs loaded after this call will, by default, + * allow the same domains as have been allowed by + * previous calls to this method. This behavior is controlled by the + * allowInsecureDomainsInNewRSLs property. + * + * @param domains One or more strings or URLRequest objects that name + * the domains from which you want to allow access. + * You can specify the special domain "*" to allow access from all domains. + * + * @see flash.system.Security#allowInsecureDomain() + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Flex 4.5 + */ + function allowInsecureDomain(... domains):void; + + /** + * A way to call a method in this IFlexModuleFactory's context + * + * @param fn The function or method to call. + * @param thisArg The this pointer for the function. + * @param argArray The arguments for the function. + * @param returns If true, the function returns a value. + * + * @return Whatever the function returns, if anything. + * + * @see Function.apply + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 3 + */ + function callInContext(fn:Function, thisArg:Object, + argArray:Array, returns:Boolean = true):*; + + /** + * A factory method that requests + * an instance of a definition known to the module. + * + *

You can provide an optional set of parameters to let + * building factories change what they create based + * on the input. + * Passing null indicates that the default + * definition is created, if possible.

+ * + * @param parameters An optional list of arguments. You can pass any number + * of arguments, which are then stored in an Array called parameters. + * + * @return An instance of the module, or null. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function create(... parameters):Object; + + /** + * Get the implementation for an interface. + * Similar to Singleton.getInstance() method, but per- + * IFlexModuleFactory. + * + * @param interfaceName The interface. + * + * @return The implementation for the interface. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + function getImplementation(interfaceName:String):Object; + + /** + * Returns a block of key/value pairs + * that hold static data known to the module. + * This method always succeeds, but can return an empty object. + * + * @return An object containing key/value pairs. Typically, this object + * contains information about the module or modules created by this + * factory; for example: + * + *
+     *  return {"description": "This module returns 42."};
+     *  
+ * + * Other common values in the returned object include the following: + *
    + *
  • fonts: A list of embedded font faces.
  • + *
  • rsls: A list of run-time shared libraries.
  • + *
  • mixins: A list of classes initialized at startup.
  • + *
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function info():Object; + + /** + * Register an implementation for an interface. + * Similar to the Singleton.registerClass() method, but per- + * IFlexModuleFactory, and takes an instance not a class. + * + * @param interfaceName The interface. + * + * @param impl The implementation. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + function registerImplementation(interfaceName:String, + impl:Object):void; + +} + +} diff --git a/src/mx/core/ILayoutDirectionElement.as b/src/mx/core/ILayoutDirectionElement.as new file mode 100644 index 00000000..56488517 --- /dev/null +++ b/src/mx/core/ILayoutDirectionElement.as @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// +package mx.core +{ + + /** + * The ILayoutDirectionElement interface defines the minimum properties and methods + * required for an Object to support the layoutDirection property. + * + * @see mx.core.LayoutDirection + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + public interface ILayoutDirectionElement + { + + /** + * Specifies the desired layout direction for an element: one of LayoutDirection.LTR + * (left to right), LayoutDirection.RTL (right to left), or null (inherit). + * + * This property is typically backed by an inheriting style. If null, + * the layoutDirection style will be set to undefined. + * + * Classes like GraphicElement, which implement ILayoutDirectionElement but do not + * support styles, must additionally support a null value for this property + * which means the layoutDirection must be inherited from its parent. + * + * @see mx.core.LayoutDirection + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function get layoutDirection():String; + + /** + * @private + */ + function set layoutDirection(value:String):void; + + /** + * An element must call this method when its layoutDirection changes or + * when its parent's layoutDirection changes. + * + * If they differ, this method is responsible for mirroring the element’s contents + * and for updating the element’s post-layout transform so that descendants inherit + * a mirrored coordinate system. IVisualElements typically implement + * mirroring by using postLayoutTransformOffsets to scale the X axis by -1 and + * to translate the x coordinate of the origin by the element's width. + * + * The net effect of this "mirror" transform is to reverse the direction + * in which the X axis increases without changing the element's location + * relative to its parent's origin. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + function invalidateLayoutDirection():void; + } +} diff --git a/src/mx/core/IMXMLObject.as b/src/mx/core/IMXMLObject.as new file mode 100644 index 00000000..2ee7153e --- /dev/null +++ b/src/mx/core/IMXMLObject.as @@ -0,0 +1,18 @@ +package mx.core +{ + public interface IMXMLObject + { + /** + * Called after the implementing object has been created and all + * component properties specified on the MXML tag have been initialized. + * + * @param document The MXML document that created this object. + * + * @param id The identifier used by document to refer + * to this object. + * If the object is a deep property on document, + * id is null. + */ + function initialized(document:Object, id:String):void; + } +} \ No newline at end of file diff --git a/src/mx/core/IPropertyChangeNotifier.as b/src/mx/core/IPropertyChangeNotifier.as new file mode 100644 index 00000000..77c55ec5 --- /dev/null +++ b/src/mx/core/IPropertyChangeNotifier.as @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +import flash.events.IEventDispatcher; + +/** + * The IPropertyChangeNotifier interface defines a marker + * interface. + * Classes that support this interface declare support for event propagation + * in a specialized manner. + * Classes that implement this interface must dispatch events for each property + * of this class and any nested classes publicly exposed as properties. + * For those properties that are anonymous (complex and not strongly typed), + * implementing classes provide custom support or directly use the + * ObjectProxy class. + * Implementors of this interface should use the + * PropertyChangeEvent.createUpdateEvent() method to construct an + * appropriate update event for dispatch. + * @example + *
+ *   
+ * function set myProperty(value:Object):void
+ * {
+ *    var oldValue:IPropertyChangeNotifier = _myProperty;
+ *    var newValue:IPropertyChangeNotifier = value;
+ *    
+ *    // Need to ensure to dispatch changes on the new property.
+ *    // Listeners use the source property to determine which object 
+ *    // actually originated the event.
+ *    // In their event handler code, they can tell if an event has been 
+ *    // propagated from deep within the object graph by comparing 
+ *    // event.target and event.source. If they are equal, then the property
+ *    // change is at the surface of the object. If they are not equal, the
+ *    // property change is somewhere deeper in the object graph.
+ *    newValue.addEventListener(
+ *                PropertyChangeEvent.PROPERTY_CHANGE, 
+ *                dispatchEvent);
+ * 
+ *    // need to stop listening for events from the old property
+ *    oldValue.removeEventListener(
+ *                PropertyChangeEvent.PROPERTY_CHANGE,
+ *                dispatchEvent);
+ * 
+ *    _myProperty = newValue;
+ * 
+ *    // now notify anyone that is listening
+ *    if (dispatcher.hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE))
+ *    {
+ *         var event:PropertyChangeEvent = 
+ *                         PropertyChangeEvent.createUpdateEvent(
+ *                                                       this,
+ *                                                       "myProperty",
+ *                                                       newValue,
+ *                                                       oldValue);
+ *        dispatchEvent(event);
+ *     }
+ *  }
+ * 
+ *      
+ *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IPropertyChangeNotifier extends IEventDispatcher, IUID +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + // Inherits uid property from IUID +} + +} diff --git a/src/mx/core/IStateClient.as b/src/mx/core/IStateClient.as new file mode 100644 index 00000000..6ff0651d --- /dev/null +++ b/src/mx/core/IStateClient.as @@ -0,0 +1,12 @@ +package mx.core +{ + public interface IStateClient + { + function get currentState():String; + + /** + * @private + */ + function set currentState(value:String):void; + } +} \ No newline at end of file diff --git a/src/mx/core/IStateClient2.as b/src/mx/core/IStateClient2.as new file mode 100644 index 00000000..12b66bcc --- /dev/null +++ b/src/mx/core/IStateClient2.as @@ -0,0 +1,31 @@ +package mx.core +{ + import flash.events.IEventDispatcher; + + public interface IStateClient2 extends IEventDispatcher, IStateClient + { + + [ArrayElementType("mx.states.State")] + function get states():Array; + + /** + * @private + */ + function set states(value:Array):void; + + + //---------------------------------- + // transitions + //---------------------------------- + + [ArrayElementType("mx.states.Transition")] + function get transitions():Array; + + /** + * @private + */ + function set transitions(value:Array):void; + + function hasState(stateName:String):Boolean + } +} \ No newline at end of file diff --git a/src/mx/core/ITransientDeferredInstance.as b/src/mx/core/ITransientDeferredInstance.as new file mode 100644 index 00000000..3695c4aa --- /dev/null +++ b/src/mx/core/ITransientDeferredInstance.as @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * The ITransientDeferredInstance interface extends IDeferredInstance and adds + * the ability for the user to reset the deferred instance factory to its + * initial state (usually this implies releasing any known references to the + * component, such as the setting the owning document property that refers to + * the instance to null). + * + * This additional capability is leveraged by the AddItems states override when + * the desired behavior is to destroy a state-specific element when a state + * no longer applies. + * + * The Flex compiler uses the same automatic coercion rules as with + * IDeferredInstance. + * + * @see mx.states.AddItems + * @see mx.core.IDeferredInstance + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ +public interface ITransientDeferredInstance extends IDeferredInstance +{ + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Resets the state of our factory to its initial state, clearing any + * references to the cached instance. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + function reset():void; +} + +} diff --git a/src/mx/core/IUID.as b/src/mx/core/IUID.as new file mode 100644 index 00000000..81696659 --- /dev/null +++ b/src/mx/core/IUID.as @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * The IUID interface defines the interface for objects that must have + * Unique Identifiers (UIDs) to uniquely identify the object. + * UIDs do not need to be universally unique for most uses in Flex. + * One exception is for messages send by data services. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IUID +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // uid + //---------------------------------- + + /** + * The unique identifier for this object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get uid():String; + + /** + * @private + */ + function set uid(value:String):void; +} + +} diff --git a/src/mx/core/RSLData.as b/src/mx/core/RSLData.as new file mode 100644 index 00000000..b472da58 --- /dev/null +++ b/src/mx/core/RSLData.as @@ -0,0 +1,279 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2010 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * A Class that describes configuration data for an RSL. + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public class RSLData +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param rslURL The location of the RSL. + * @param policyFileURL The location of the policy file url (optional). + * @param digest The digest of the RSL. This is null for an RSL without + * a digest. + * @param hashType The type of hash used to create the digest. The only + * supported value is SHA256.TYPE_ID. + * @param isSigned True if the RSL has been signed by Adobe, false + * otherwise. + * @param verifyDigest Detemines if the RSL's digest should be verified + * after it is loaded. + * @param applicationDomainTarget The application domain where the the + * RSL should be loaded. For valid values see the ApplicationDomainTarget + * enumeration. + * + * @see mx.core.ApplicationDomainTarget + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function RSLData(rslURL:String = null, + policyFileURL:String = null, + digest:String = null, + hashType:String = null, + isSigned:Boolean = false, + verifyDigest:Boolean = false, + applicationDomainTarget:String = "default") + { + super(); + + _rslURL = rslURL + _policyFileURL = policyFileURL; + _digest = digest; + _hashType = hashType; + _isSigned = isSigned; + _verifyDigest = verifyDigest; + _applicationDomainTarget = applicationDomainTarget; + _moduleFactory = moduleFactory; + + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // applicationDomainTarget + //---------------------------------- + + /** + * @private + */ + private var _applicationDomainTarget:String; + + /** + * The requested application domain to load the RSL into. + * For valid values see the ApplicationDomainTarget enumeration. + * + * @see mx.core.ApplicationDomainTarget + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function get applicationDomainTarget():String + { + return _applicationDomainTarget; + } + + //---------------------------------- + // digest + //---------------------------------- + + /** + * @private + */ + private var _digest:String; + + /** + * The digest of the RSL. This is null for an RSL without a digest. + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function get digest():String + { + return _digest; + } + + //---------------------------------- + // hash type + //---------------------------------- + + /** + * @private + */ + private var _hashType:String; + + /** + * The type of hash used to create the RSL digest. The only supported hash + * type is SHA256.TYPE_ID. + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function get hashType():String + { + return _hashType; + } + + //---------------------------------- + // isSigned + //---------------------------------- + + /** + * @private + */ + private var _isSigned:Boolean; + + /** + * True if the RSL has been signed by Adobe. False otherwise. + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function get isSigned():Boolean + { + return _isSigned; + } + + //---------------------------------- + // moduleFactory + //---------------------------------- + + /** + * @private + */ + private var _moduleFactory:IFlexModuleFactory; + + /** + * Non-null if this RSL should be loaded into an application + * domain other than the application domain associated with the + * module factory performing the load. If null, then load into + * the current application domain. + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function get moduleFactory():IFlexModuleFactory + { + return _moduleFactory; + } + + /** + * @private + */ + public function set moduleFactory(moduleFactory:IFlexModuleFactory):void + { + _moduleFactory = moduleFactory; + } + + //---------------------------------- + // policyFileURL + //---------------------------------- + + /** + * @private + */ + private var _policyFileURL:String; + + /** + * An URL that specifies the location of the policy file (optional). + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function get policyFileURL():String + { + return _policyFileURL; + } + + //---------------------------------- + // rslURL + //---------------------------------- + + /** + * @private + */ + private var _rslURL:String; + + /** + * The location of the RSL. The URL can be absolute or relative to the + * application or module. + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function get rslURL():String + { + return _rslURL; + } + + //---------------------------------- + // verifyDigest + //---------------------------------- + + /** + * @private + */ + private var _verifyDigest:Boolean; + + /** + * True if the digest must be verified before loading the RSL into memory. + * False allows the RSL to be loaded without verification. Signed RSLs + * are always verified regardless of the value. + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public function get verifyDigest():Boolean + { + return _verifyDigest; + } + +} +} \ No newline at end of file diff --git a/src/mx/core/Singleton.as b/src/mx/core/Singleton.as new file mode 100644 index 00000000..964095cd --- /dev/null +++ b/src/mx/core/Singleton.as @@ -0,0 +1,117 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +[ExcludeClass] + +/** + * @private + * This all-static class serves as a singleton registry. + * + * For example, pop-up management throughout a Flex application + * is provided by a single instance of the PopUpManagerImpl class, + * even when the main application loads modules and sub-applications + * each of which might have PopUpManagerImpl linked in. + * + * The factory class for a framework-based application + * or a module (i.e., SystemManager or FlexModuleFactory, + * both of which implements IFlexModuleFactory) calls + * the registerClass() method to populate the registry. + * + * Later, other classes call getInstance() to access + * the singleton instance. + * + * The registry policy is "first class in wins". + * For example, if the main application registers its + * PopUpManagerImpl, then a loaded SWF will use that one. + * However, if the main application doesn't contain + * a PopUpManagerImpl, then it registers null, + * and the first loaded SWF containing a PopUpManagerImpl + * will register that one. + */ +public class Singleton +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * A map of fully-qualified interface names, + * such as "mx.managers::IPopUpManager", + * to implementation classes which produce singleton instances, + * such as mx.managers.PopUpManagerImpl. + */ + private static var classMap:Object = {}; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Adds an interface-name-to-implementation-class mapping to the registry, + * if a class hasn't already been registered for the specified interface. + * The class must implement a getInstance() method which returns + * its singleton instance. + */ + public static function registerClass(interfaceName:String, + clazz:Class):void + { + var c:Class = classMap[interfaceName]; + if (!c) + classMap[interfaceName] = clazz; + } + + /** + * @private + * Returns the implementation class registered for the specified + * interface, or null if no class has been registered for that interface. + * + * This method should not be called at static initialization time, + * because the factory class may not have called registerClass() yet. + */ + public static function getClass(interfaceName:String):Class + { + return classMap[interfaceName]; + } + + /** + * @private + * Returns the singleton instance of the implementation class + * that was registered for the specified interface, + * by looking up the class in the registry + * and calling its getInstance() method. + * + * This method should not be called at static initialization time, + * because the factory class may not have called registerClass() yet. + */ + public static function getInstance(interfaceName:String):Object + { + var c:Class = classMap[interfaceName]; + if (!c) + { + throw new Error("No class registered for interface '" + + interfaceName + "'."); + } + return c["getInstance"](); + } +} + +} diff --git a/src/mx/core/SpriteAsset.as b/src/mx/core/SpriteAsset.as new file mode 100644 index 00000000..c6d26bbe --- /dev/null +++ b/src/mx/core/SpriteAsset.as @@ -0,0 +1,808 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +import flash.display.DisplayObjectContainer; +import flash.events.Event; +import flash.geom.Point; +import flash.system.ApplicationDomain; + +/** + * SpriteAsset is a subclass of the flash.display.Sprite class which + * represents vector graphic images that you embed in an application. + * It implements the IFlexDisplayObject interface, which makes it + * possible for an embedded vector graphic image to be displayed in an Image + * control, or to be used as a container background or a component skin. + * + *

The vector graphic image that you're embedding can be in an SVG file. + * You can also embed a sprite symbol that is in a SWF file produced + * by Flash. + * In both cases, the MXML compiler autogenerates a class that extends + * SpriteAsset to represent the embedded vector graphic image.

+ * + *

You don't generally have to use the SpriteAsset class directly + * when you write a Flex application. + * For example, you can embed a sprite symbol from a SWF file and display + * it in an Image control by writing the following:

+ * + *
+ *  <mx:Image id="logo" source="@Embed(source='Assets.swf', symbol='Logo')"/>
+ * + *

Or use it as the application's background image in CSS syntax + * by writing the following:

+ * + *
+ *  <fx:Style>
+ *      @namespace mx "library://ns.adobe.com/flex/mx"
+ *      mx|Application {
+ *          backgroundImage: Embed(source="Assets.swf", symbol='Logo')
+ *      }
+ *  <fx:Style/>
+ * + *

without having to understand that the MXML compiler has created + * a subclass of BitmapAsset for you.

+ * + *

However, it may be useful to understand what is happening + * at the ActionScript level. + * To embed a vector graphic image in ActionScript, you declare a variable + * of type Class, and put [Embed] metadata on it. + * For example, you embed a sprite symbol from a SWF file like this:

+ * + *
+ *  [Bindable]
+ *  [Embed(source="Assets.swf", symbol="Logo")]
+ *  private var logoClass:Class;
+ * + *

The MXML compiler notices that the Logo symbol in Assets.swf + * is a sprite, autogenerates a subclass of the SpriteAsset class + * to represent it, and sets your variable to be a reference to this + * autogenerated class. + * You can then use this class reference to create instances of the + * SpriteAsset using the new operator, and use APIs + * of the Sprite class on them:

+ * + *
+ *  var logo:SpriteAsset = SpriteAsset(new logoClass());
+ *  logo.rotation=45;
+ * + *

However, you rarely need to create SpriteAsset instances yourself + * because image-related properties and styles can simply be set to an + * image-producing class, and components will create image instances + * as necessary. + * For example, to display this vector graphic image in an Image control, + * you can set the Image's source property to + * logoClass. + * In MXML you could do this as follows:

+ * + *
+ *  <mx:Image id="logo" source="{logoClass}"/>
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class SpriteAsset extends FlexSprite + implements IFlexAsset, IFlexDisplayObject, IBorder, + ILayoutDirectionElement +{ + include "../core/Version.as"; + + // Softlink FlexVersion and MatrixUtil to remove dependencies of embeds on + // framework classes. This helps to reduce swf size in AS-only projects. + private static var FlexVersionClass:Class; + private static var MatrixUtilClass:Class; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function SpriteAsset() + { + super(); + + // Remember initial size as our measured size. + _measuredWidth = width; + _measuredHeight = height; + + if (FlexVersionClass == null) + { + var appDomain:ApplicationDomain = ApplicationDomain.currentDomain; + if (appDomain.hasDefinition("mx.core::FlexVersion")) + FlexVersionClass = Class(appDomain.getDefinition("mx.core::FlexVersion")); + } + + if (FlexVersionClass && FlexVersionClass["compatibilityVersion"] >= FlexVersionClass["VERSION_4_0"]) + this.addEventListener(Event.ADDED, addedHandler); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + // Softlink AdvancedLayoutFeatures to remove dependencies of embeds on + // framework classes. This helps to reduce swf size in AS-only projects. + private var layoutFeaturesClass:Class; + private var layoutFeatures:IAssetLayoutFeatures; + + //-------------------------------------------------------------------------- + // + // Overridden Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // x + //---------------------------------- + + /** + * @private + */ + override public function get x():Number + { + // TODO(hmuller): by default get x returns transform.matrix.tx rounded to the nearest 20th. + // should do the same here, if we're returning layoutFeatures.layoutX. + return (layoutFeatures == null) ? super.x : layoutFeatures.layoutX; + } + + /** + * @private + */ + override public function set x(value:Number):void + { + if (x == value) + return; + + if (layoutFeatures == null) + { + super.x = value; + } + else + { + layoutFeatures.layoutX = value; + validateTransformMatrix(); + } + } + + //---------------------------------- + // y + //---------------------------------- + + /** + * @private + */ + override public function get y():Number + { + return (layoutFeatures == null) ? super.y : layoutFeatures.layoutY; + } + + /** + * @private + */ + override public function set y(value:Number):void + { + if (y == value) + return; + + if (layoutFeatures == null) + { + super.y = value; + } + else + { + layoutFeatures.layoutY = value; + validateTransformMatrix(); + } + } + + //---------------------------------- + // z + //---------------------------------- + + /** + * @private + */ + override public function get z():Number + { + return (layoutFeatures == null) ? super.z : layoutFeatures.layoutZ; + } + + /** + * @private + */ + override public function set z(value:Number):void + { + if (z == value) + return; + + if (layoutFeatures == null) + { + super.z = value; + } + else + { + layoutFeatures.layoutZ = value; + validateTransformMatrix(); + } + } + + //---------------------------------- + // width + //---------------------------------- + + /** + * @private + */ + override public function get width():Number + { + if (layoutFeatures == null) + return super.width; + + // Return bounding box width in mirroring case + var p:Point; + if (MatrixUtilClass != null) + p = MatrixUtilClass["transformSize"](layoutFeatures.layoutWidth, _height, transform.matrix); + + return p ? p.x : super.width; + } + + /** + * @private + */ + override public function set width(value:Number):void + { + if (width == value) + return; + + if (layoutFeatures == null) + { + super.width = value; + } + else + { + layoutFeatures.layoutWidth = value; + // Calculate scaleX based on initial width. We set scaleX + // here because resizing a BitmapAsset normally would adjust + // the scale to match. + layoutFeatures.layoutScaleX = measuredWidth != 0 ? value / measuredWidth : 0; + validateTransformMatrix(); + } + } + + //---------------------------------- + // height + //---------------------------------- + + private var _height:Number; + + /** + * @private + */ + override public function get height():Number + { + + if (layoutFeatures == null) + return super.height; + + // Return bounding box height in mirroring case + var p:Point; + if (MatrixUtilClass != null) + p = MatrixUtilClass["transformSize"](layoutFeatures.layoutWidth, _height, transform.matrix); + + return p ? p.y : super.height; + } + + /** + * @private + */ + override public function set height(value:Number):void + { + if (height == value) + return; + + if (layoutFeatures == null) + { + super.height = value; + } + else + { + _height = value; + // Calculate scaleY based on initial height. We set scaleY + // here because resizing a BitmapAsset normally would adjust + // the scale to match. + layoutFeatures.layoutScaleY = measuredHeight != 0 ? value / measuredHeight : 0; + validateTransformMatrix(); + } + } + + //---------------------------------- + // rotation + //---------------------------------- + + /** + * @private + */ + override public function get rotationX():Number + { + return (layoutFeatures == null) ? super.rotationX : layoutFeatures.layoutRotationX; + } + + /** + * @private + */ + override public function set rotationX(value:Number):void + { + if (rotationX == value) + return; + + if (layoutFeatures == null) + { + super.rotationX = value; + } + else + { + layoutFeatures.layoutRotationX = value; + validateTransformMatrix(); + } + } + /** + * @private + */ + override public function get rotationY():Number + { + return (layoutFeatures == null) ? super.rotationY : layoutFeatures.layoutRotationY; + } + + /** + * @private + */ + override public function set rotationY(value:Number):void + { + if (rotationY == value) + return; + + if (layoutFeatures == null) + { + super.rotationY = value; + } + else + { + layoutFeatures.layoutRotationY = value; + validateTransformMatrix(); + } + } + + /** + * @private + */ + override public function get rotationZ():Number + { + return (layoutFeatures == null) ? super.rotationZ : layoutFeatures.layoutRotationZ; + } + + /** + * @private + */ + override public function set rotationZ(value:Number):void + { + if (rotationZ == value) + return; + + if (layoutFeatures == null) + { + super.rotationZ = value; + } + else + { + layoutFeatures.layoutRotationZ = value; + validateTransformMatrix(); + } + } + + /** + * @private + */ + override public function get rotation():Number + { + return (layoutFeatures == null) ? super.rotation : layoutFeatures.layoutRotationZ; + } + + /** + * @private + */ + override public function set rotation(value:Number):void + { + // TODO (klin): rotation actually affects width and height as well. + if (rotation == value) + return; + + if (layoutFeatures == null) + { + super.rotation = value; + } + else + { + layoutFeatures.layoutRotationZ = value; + validateTransformMatrix(); + } + } + + //---------------------------------- + // scaleX + //---------------------------------- + + /** + * @private + */ + override public function get scaleX():Number + { + return (layoutFeatures == null) ? super.scaleX : layoutFeatures.layoutScaleX; + } + + /** + * @private + */ + override public function set scaleX(value:Number):void + { + if (scaleX == value) + return; + + if (layoutFeatures == null) + { + super.scaleX = value; + } + else + { + layoutFeatures.layoutScaleX = value; + layoutFeatures.layoutWidth = Math.abs(value) * measuredWidth; + validateTransformMatrix(); + } + } + + //---------------------------------- + // scaleY + //---------------------------------- + + /** + * @private + */ + override public function get scaleY():Number + { + return (layoutFeatures == null) ? super.scaleY : layoutFeatures.layoutScaleY; + } + + /** + * @private + */ + override public function set scaleY(value:Number):void + { + if (scaleY == value) + return; + + if (layoutFeatures == null) + { + super.scaleY = value; + } + else + { + layoutFeatures.layoutScaleY = value; + _height = Math.abs(value) * measuredHeight; + validateTransformMatrix(); + } + } + + //---------------------------------- + // scaleZ + //---------------------------------- + + /** + * @private + */ + override public function get scaleZ():Number + { + return (layoutFeatures == null) ? super.scaleZ : layoutFeatures.layoutScaleZ; + } + + /** + * @private + */ + override public function set scaleZ(value:Number):void + { + if (scaleZ == value) + return; + + if (layoutFeatures == null) + { + super.scaleZ = value; + } + else + { + layoutFeatures.layoutScaleZ = value; + validateTransformMatrix(); + } + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // layoutDirection + //---------------------------------- + + // Use "ltr" instead of LayoutDirection.LTR to avoid depending + // on that framework class. + private var _layoutDirection:String = "ltr"; + + [Inspectable(category="General", enumeration="ltr,rtl")] + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + public function get layoutDirection():String + { + return _layoutDirection; + } + + public function set layoutDirection(value:String):void + { + if (value == _layoutDirection) + return; + + _layoutDirection = value; + invalidateLayoutDirection(); + } + + //---------------------------------- + // measuredHeight + //---------------------------------- + + /** + * @private + * Storage for the measuredHeight property. + */ + private var _measuredHeight:Number; + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get measuredHeight():Number + { + return _measuredHeight; + } + + //---------------------------------- + // measuredWidth + //---------------------------------- + + /** + * @private + * Storage for the measuredWidth property. + */ + private var _measuredWidth:Number; + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get measuredWidth():Number + { + return _measuredWidth; + } + + //---------------------------------- + // borderMetrics + //---------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get borderMetrics():EdgeMetrics + { + if (scale9Grid == null) + { + return EdgeMetrics.EMPTY; + } + else + { + return new EdgeMetrics(scale9Grid.left, + scale9Grid.top, + Math.ceil(measuredWidth - scale9Grid.right), + Math.ceil(measuredHeight - scale9Grid.bottom)); + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + public function invalidateLayoutDirection():void + { + var p:DisplayObjectContainer = parent; + + // We check the closest parent's layoutDirection property + // to create or destroy layoutFeatures if needed. + while (p) + { + if (p is ILayoutDirectionElement) + { + // mirror is true if our layoutDirection differs from our parent's. + var mirror:Boolean = _layoutDirection != null && + ILayoutDirectionElement(p).layoutDirection != null && + (_layoutDirection != ILayoutDirectionElement(p).layoutDirection); + + // If our layoutDirection is different from our parent's and if it used to + // be the same, create layoutFeatures to handle mirroring. + if (mirror && layoutFeatures == null) + { + initAdvancedLayoutFeatures(); + if (layoutFeatures != null) + { + layoutFeatures.mirror = mirror; + validateTransformMatrix(); + } + } + else if (!mirror && layoutFeatures) + { + // If our layoutDirection is not different from our parent's and if + // it used to be different, then recover our matrix and remove layoutFeatures. + layoutFeatures.mirror = mirror; + validateTransformMatrix(); + layoutFeatures = null; + } + + break; + } + + p = p.parent; + } + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function move(x:Number, y:Number):void + { + this.x = x; + this.y = y; + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function setActualSize(newWidth:Number, newHeight:Number):void + { + width = newWidth; + height = newHeight; + } + + /** + * @private + */ + private function addedHandler(event:Event):void + { + invalidateLayoutDirection(); + } + + /** + * @private + * Initializes AdvancedLayoutFeatures for this asset when mirroring. + */ + private function initAdvancedLayoutFeatures():void + { + // Get AdvancedLayoutFeatures if it exists. + if (layoutFeaturesClass == null) + { + var appDomain:ApplicationDomain = ApplicationDomain.currentDomain; + + if (appDomain.hasDefinition("mx.core::AdvancedLayoutFeatures")) + layoutFeaturesClass = Class(appDomain.getDefinition("mx.core::AdvancedLayoutFeatures")); + + // Get MatrixUtil class if it exists + if (MatrixUtilClass == null) + { + if (appDomain.hasDefinition("mx.utils::MatrixUtil")) + MatrixUtilClass = Class(appDomain.getDefinition("mx.utils::MatrixUtil")); + } + } + + if (layoutFeaturesClass != null) + { + var features:IAssetLayoutFeatures = new layoutFeaturesClass(); + + features.layoutScaleX = scaleX; + features.layoutScaleY = scaleY; + features.layoutScaleZ = scaleZ; + features.layoutRotationX = rotationX; + features.layoutRotationY = rotationY; + features.layoutRotationZ = rotation; + features.layoutX = x; + features.layoutY = y; + features.layoutZ = z; + features.layoutWidth = width; // for the mirror transform + _height = height; // for backing storage + layoutFeatures = features; + } + } + + /** + * @private + * Applies the transform matrix calculated by AdvancedLayoutFeatures + * so that this bitmap will not be mirrored if a parent is mirrored. + */ + private function validateTransformMatrix():void + { + if (layoutFeatures != null) + { + if (layoutFeatures.is3D) + super.transform.matrix3D = layoutFeatures.computedMatrix3D; + else + super.transform.matrix = layoutFeatures.computedMatrix; + } + } +} + +} diff --git a/src/mx/core/TextFieldFactory.as b/src/mx/core/TextFieldFactory.as new file mode 100644 index 00000000..35fb4197 --- /dev/null +++ b/src/mx/core/TextFieldFactory.as @@ -0,0 +1,193 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +import flash.text.TextField; +import flash.utils.Dictionary; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + * Singleton to create TextFields in the context of various ModuleFactories. + * One module factory will have at most one TextField created for it. + * The text fields are only used for measurement; + * they are not on the display list. + */ +public class TextFieldFactory implements ITextFieldFactory +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * + * This classes singleton. + */ + private static var instance:ITextFieldFactory; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public static function getInstance():ITextFieldFactory + { + if (!instance) + instance = new TextFieldFactory(); + + return instance; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Cache of TextFields. Limit of one per module factory. + * In this Dictionary, each key is a weak reference + * to an IFlexModuleFactory and each value is a Dictionary + * with a single entry (a TextField as a weak key). + */ + private var textFields:Dictionary = new Dictionary(true); + + /** + * @private + * Cache of FTETextFields. Limit of one per module factory. + * In this Dictionary, each key is a weak reference + * to an IFlexModuleFactory and each value is a Dictionary + * with a single entry (a FTETextField as a weak key). + */ + private var fteTextFields:Dictionary = new Dictionary(true); + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Creates an instance of TextField + * in the context of the specified IFlexModuleFactory. + * + * @param moduleFactory The IFlexModuleFactory requesting the TextField. + * + * @return A FTETextField created in the context + * of moduleFactory. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function createTextField(moduleFactory:IFlexModuleFactory):TextField + { + // Check to see if we already have a text field for this module factory. + var textField:TextField = null; + var textFieldDictionary:Dictionary = textFields[moduleFactory]; + + if (textFieldDictionary) + { + for (var iter:Object in textFieldDictionary) + { + textField = TextField(iter); + break; + } + } + if (!textField) + { + if (moduleFactory) + textField = TextField(moduleFactory.create("flash.text.TextField")); + else + textField = new TextField(); + + // The dictionary could be empty, but not null because entries in the dictionary + // could be garbage collected. + if (!textFieldDictionary) + textFieldDictionary = new Dictionary(true); + textFieldDictionary[textField] = 1; + textFields[moduleFactory] = textFieldDictionary; + } + + return textField; + } + + /** + * @private + * Creates an instance of FTETextField + * in the context of the specified module factory. + * + * @param moduleFactory The IFlexModuleFactory requesting the TextField. + * May not be null. + * + * @return A FTETextField created in the context + * of moduleFactory. + * The return value is loosely typed as Object + * to avoid linking in FTETextField (and therefore much of TLF). + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4 + */ + public function createFTETextField(moduleFactory:IFlexModuleFactory):Object + { + // Check to see if we already have a text field for this module factory. + var fteTextField:Object = null; + var fteTextFieldDictionary:Dictionary = fteTextFields[moduleFactory]; + + if (fteTextFieldDictionary) + { + for (var iter:Object in fteTextFieldDictionary) + { + fteTextField = iter; + break; + } + } + if (!fteTextField) + { + if (moduleFactory) + { + fteTextField = moduleFactory.create( + "mx.core.FTETextField"); + fteTextField.fontContext = moduleFactory; + } + + // The dictionary could be empty, but not null because entries in the dictionary + // could be garbage collected. + if (!fteTextFieldDictionary) + fteTextFieldDictionary = new Dictionary(true); + fteTextFieldDictionary[fteTextField] = 1; + fteTextFields[moduleFactory] = fteTextFieldDictionary; + } + + return fteTextField; + } +} + +} diff --git a/src/mx/core/Version.as b/src/mx/core/Version.as new file mode 100644 index 00000000..3c8ad583 --- /dev/null +++ b/src/mx/core/Version.as @@ -0,0 +1,18 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +import mx.core.mx_internal; + +/** + * @private + * Version string for this class. + */ +mx_internal static const VERSION:String = "4.6.0.23201"; diff --git a/src/mx/core/mx_internal.as b/src/mx/core/mx_internal.as new file mode 100644 index 00000000..38d38783 --- /dev/null +++ b/src/mx/core/mx_internal.as @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.core +{ + +/** + * This namespace is used for undocumented APIs -- usually implementation + * details -- which can't be private because they need to visible + * to other classes. + * APIs in this namespace are completely unsupported and are likely to + * change in future versions of Flex. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public namespace mx_internal = + "http://www.adobe.com/2006/flex/mx/internal"; +} diff --git a/src/mx/events/CollectionEvent.as b/src/mx/events/CollectionEvent.as new file mode 100644 index 00000000..02af6ea6 --- /dev/null +++ b/src/mx/events/CollectionEvent.as @@ -0,0 +1,275 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.events +{ + +import flash.events.Event; + +/** + * The mx.events.CollectionEvent class represents an event that is + * dispatched when the associated collection changes. + * + * @see FlexEvent#CURSOR_UPDATE + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class CollectionEvent extends Event +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * The CollectionEvent.COLLECTION_CHANGE constant defines the value of the + * type property of the event object for an event that is + * dispatched when a collection has changed. + * + *

The properties of the event object have the following values. + * Not all properties are meaningful for all kinds of events. + * See the detailed property descriptions for more information.

+ * + * + * + * + * + * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
itemsAn Array of objects with + * information about the items affected by the event. + * The contents of this field depend on the event kind; + * for details see the items property
kindThe kind of event. + * The valid values are defined in the CollectionEventKind + * class as constants.
locationLocation within the target collection + * of the item(s) specified in the items property.
oldLocationthe previous location in the collection + * of the item specified in the items property.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
typeCollectionEvent.COLLECTION_CHANGE
+ * + * @eventType collectionChange + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const COLLECTION_CHANGE:String = "collectionChange"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param type The event type; indicates the action that triggered 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 kind Indicates the kind of event that occured. + * The parameter value can be one of the values in the CollectionEventKind + * class, or null, which indicates that the kind is unknown. + * + * @param location When the kind is + * CollectionEventKind.ADD, + * CollectionEventKind.MOVE, + * CollectionEventKind.REMOVE, or + * CollectionEventKind.REPLACE, + * this value indicates at what location the item(s) specified + * in the items property can be found + * within the target collection. + * + * @param oldLocation When the kind is + * CollectionEventKind.MOVE, this value indicates + * the old location within the target collection + * of the item(s) specified in the items property. + * + * @param items Array of objects with information about the items + * affected by the event, as described in the items property. + * When the kind is CollectionEventKind.REFRESH + * or CollectionEventKind.RESET, this Array has zero length. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function CollectionEvent(type:String, bubbles:Boolean = false, + cancelable:Boolean = false, + kind:String = null, location:int = -1, + oldLocation:int = -1, items:Array = null) + { + super(type, bubbles, cancelable); + + this.kind = kind; + this.location = location; + this.oldLocation = oldLocation; + this.items = items ? items : []; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // kind + //---------------------------------- + + /** + * Indicates the kind of event that occurred. + * The property value can be one of the values in the + * CollectionEventKind class, + * or null, which indicates that the kind is unknown. + * + * @default null + * + * @see CollectionEventKind + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var kind:String; + + //---------------------------------- + // items + //---------------------------------- + + /** + * When the kind is CollectionEventKind.ADD + * or CollectionEventKind.REMOVE the items property + * is an Array of added/removed items. + * When the kind is CollectionEventKind.REPLACE + * or CollectionEventKind.UPDATE the items property + * is an Array of PropertyChangeEvent objects with information about the items + * affected by the event. + * When a value changes, query the newValue and + * oldValue fields of the PropertyChangeEvent objects + * to find out what the old and new values were. + * When the kind is CollectionEventKind.REFRESH + * or CollectionEventKind.RESET, this array has zero length. + * + * @default [ ] + * + * @see PropertyChangeEvent + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var items:Array; + + //---------------------------------- + // location + //---------------------------------- + + /** + * When the kind value is CollectionEventKind.ADD, + * CollectionEventKind.MOVE, + * CollectionEventKind.REMOVE, or + * CollectionEventKind.REPLACE, this property is the + * zero-base index in the collection of the item(s) specified in the + * items property. + * + * @see CollectionEventKind + * + * @default -1 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var location:int; + + //---------------------------------- + // oldLocation + //---------------------------------- + + /** + * When the kind value is CollectionEventKind.MOVE, + * this property is the zero-based index in the target collection of the + * previous location of the item(s) specified by the items property. + * + * @default -1 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var oldLocation:int; + + //-------------------------------------------------------------------------- + // + // Overridden methods: Object + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function toString():String + { + return formatToString("CollectionEvent", "kind", "location", + "oldLocation", "type", "bubbles", + "cancelable", "eventPhase"); + } + + //-------------------------------------------------------------------------- + // + // Overridden methods: Event + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function clone():Event + { + return new CollectionEvent(type, bubbles, cancelable, kind, location, oldLocation, items); + } +} + +} diff --git a/src/mx/events/CollectionEventKind.as b/src/mx/events/CollectionEventKind.as new file mode 100644 index 00000000..0950a25b --- /dev/null +++ b/src/mx/events/CollectionEventKind.as @@ -0,0 +1,135 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.events +{ + +/** + * The CollectionEventKind class contains constants for the valid values + * of the mx.events.CollectionEvent class kind property. + * These constants indicate the kind of change that was made to the collection. + * + * @see mx.events.CollectionEvent + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public final class CollectionEventKind +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * Indicates that the collection added an item or items. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ADD:String = "add"; + + /** + * Indicates that the item has moved from the position identified + * by the CollectionEvent oldLocation property to the + * position identified by the location property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const MOVE:String = "move"; + + /** + * Indicates that the collection applied a sort, a filter, or both. + * This change can potentially be easier to handle than a RESET. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const REFRESH:String = "refresh"; + + /** + * Indicates that the collection removed an item or items. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const REMOVE:String = "remove"; + + /** + * Indicates that the item at the position identified by the + * CollectionEvent location property has been replaced. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const REPLACE:String = "replace"; + + /** + * Indicates that the collection has internally expanded. + * This event kind occurs when a branch opens in a + * hierarchical collection, for example when a Tree control branch opens. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + mx_internal static const EXPAND:String = "expand"; + + /** + * Indicates that the collection has changed so drastically that + * a reset is required. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const RESET:String = "reset"; + + /** + * Indicates that one or more items were updated within the collection. + * The affected item(s) + * are stored in the items property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const UPDATE:String = "update"; +} + +} diff --git a/src/mx/events/FlexEvent.as b/src/mx/events/FlexEvent.as new file mode 100644 index 00000000..164d2365 --- /dev/null +++ b/src/mx/events/FlexEvent.as @@ -0,0 +1,1467 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.events +{ + +import flash.events.Event; + +/** + * The FlexEvent class represents the event object passed to + * the event listener for many Flex events. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class FlexEvent extends Event +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * The FlexEvent.ADD constant defines the value of the + * type property of the event object for an add event. + * + *

This event will only be dispatched when there are one or more relevant listeners + * attached to the dispatching object.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType add + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ADD:String = "add"; + + /** + * The FlexEvent.ADD_FOCUS_MANAGER constant defines the value of the + * type property of the event object for an addFocusManager event. + * This event is dispatched from an IFocusManagerContainer when its focusManager + * is assigned. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType addFocusManager + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const ADD_FOCUS_MANAGER:String = "addFocusManager"; + + /** + * The FlexEvent.APPLICATION_COMPLETE constant defines the value of the + * type property of the event object for a applicationComplete event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType applicationComplete + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const APPLICATION_COMPLETE:String = "applicationComplete"; + + /** + * The FlexEvent.BUTTON_DOWN constant defines the value of the + * type property of the event object for a buttonDown event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType buttonDown + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const BUTTON_DOWN:String = "buttonDown"; + + /** + * The FlexEvent.BACK_KEY_PRESSED constant defines the value of the + * type property of the event object for a backKeyPressed event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelabletrue
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType backKeyPressed + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public static const BACK_KEY_PRESSED:String = "backKeyPressed"; + + /** + * The FlexEvent.CHANGE_END constant defines the value of the + * type property of the event object for a changeEnd event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType changeEnd + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const CHANGE_END:String = "changeEnd"; + + /** + * The FlexEvent.CHANGE_START constant defines the value of the + * type property of the event object for a changeStart event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType changeStart + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const CHANGE_START:String = "changeStart"; + + /** + * The FlexEvent.CHANGING constant defines the value of the + * type property of the event object for a changing event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType changing + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const CHANGING:String = "changing"; + + /** + * The FlexEvent.CREATION_COMPLETE constant defines the value of the + * type property of the event object for a creationComplete event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType creationComplete + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const CREATION_COMPLETE:String = "creationComplete"; + + /** + * The FlexEvent.CONTENT_CREATION_COMPLETE constant defines the value of the + * type property of the event object for a contentCreationComplete event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType contentCreationComplete + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.1 + * @productversion Flex 4 + */ + public static const CONTENT_CREATION_COMPLETE:String = "contentCreationComplete"; + + + /** + * The FlexEvent.CURSOR_UPDATE constant defines the value of the + * type property of the event object for a cursorUpdate event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType cursorUpdate + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const CURSOR_UPDATE:String = "cursorUpdate"; + + /** + * The FlexEvent.DATA_CHANGE constant defines the value of the + * type property of the event object for a dataChange event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType dataChange + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const DATA_CHANGE:String = "dataChange"; + + /** + * The FlexEvent.ENTER constant defines the value of the + * type property of the event object for a enter event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType enter + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ENTER:String = "enter"; + + /** + * The FlexEvent.ENTER_FRAME constant defines the value of the + * type property of the event object for an Event.ENTER_FRAMER event. + * + * Adding a listener to ENTER_FRAME on the SystemManager will add a listener for + * the Event.ENTER_FRAME event on the stage, if access if allowed, + * or the SystemManager if access the the stage is not allowed. + * The listener should expect to receive Event.ENTER_FRAME events. + * + * @eventType flexEventEnterFrame + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ENTER_FRAME:String = "flexEventEnterFrame"; + + /** + * The FlexEvent.ENTER_STATE constant defines the value of the + * type property of the event object for a enterState event. + * + *

This event will only be dispatched when there are one or more relevant listeners + * attached to the dispatching object.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType enterState + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ENTER_STATE:String = "enterState"; + + /** + * The FlexEvent.EXIT_STATE constant defines the value of the + * type property of the event object for a exitState event. + * + *

This event will only be dispatched when there are one or more relevant listeners + * attached to the dispatching object.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType exitState + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const EXIT_STATE:String = "exitState"; + + /** + * The FlexEvent.FLEX_WINDOW_ACTIVATE constant defines the value of the + * type property of the event object for a flexWindowActivate + * event. + * + * Similar to the flash.events.AIREvent.WINDOW_ACTIVATE except it is dispatched + * in both Flash and AIR when a Flex window or popup is activated. This event is + * dispatched from the focusManager managing + * focus in that container. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType flexWindowActivate + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const FLEX_WINDOW_ACTIVATE:String = "flexWindowActivate"; + + /** + * The FlexEvent.FLEX_WINDOW_DEACTIVATE constant defines the value of the + * type property of the event object for a flexWindowDeactivate + * event. + * + * Similar to the flash.events.AIREvent.FLEX_WINDOW_DEACTIVATE except it is dispatched + * in both Flash and AIR when a Flex Window or popup is deactivated. This event is + * dispatched from the focusManager managing + * focus in that container. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType flexWindowDeactivate + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const FLEX_WINDOW_DEACTIVATE:String = "flexWindowDeactivate"; + + /** + * The FlexEvent.HIDE constant defines the value of the + * type property of the event object for a hide event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType hide + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const HIDE:String = "hide"; + + /** + * The FlexEvent.IDLE constant defines the value of the + * type property of the event object for a idle event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType idle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const IDLE:String = "idle"; + + /** + * Dispatched when a Flex application finishes initialization. + * You use this event when creating a custom download progress bar. + * + *

This event is always dispatched once by the Preloader class, + * and is the last event that the Preloader dispatches. + * When this event is dispatched, the event listener should + * dispatch an Event.COMPLETE event.

+ * + *

A download progress bar must dispatch a complete + * event after it has received an init_complete event. + * The complete event informs the Preloader that the + * download progress bar has completed all operations and can be dismissed.

+ * + *

A download progress bar can perform additional tasks, + * such as playing an animation, after receiving + * an init_complete event, and before dispatching + * the complete event. Dispatching the complete + * event should be the last action of the download progress bar.

+ * + *

The FlexEvent.INIT_COMPLETE constant defines the value of the + * type property of the event object for a initComplete event.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @see mx.preloaders.DownloadProgressBar + * @eventType initComplete + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const INIT_COMPLETE:String = "initComplete"; + + /** + * Dispatched when the Flex application completes an initialization phase, + * as defined by calls to the measure(), commitProperties(), + * or updateDisplayList() methods. + * This event describes the progress of the application in the initialization phase. + * You use this event when creating a custom download progress bar. + * + *

The FlexEvent.INIT_PROGRESS constant defines the value of the + * type property of the event object for a initProgress event.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @see mx.preloaders.DownloadProgressBar + * @eventType initProgress + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const INIT_PROGRESS:String = "initProgress"; + + /** + * The FlexEvent.INITIALIZE constant defines the value of the + * type property of the event object for a initialize event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType initialize + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const INITIALIZE:String = "initialize"; + + /** + * The FlexEvent.INVALID constant defines the value of the + * type property of the event object for a invalid event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType invalid + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const INVALID:String = "invalid"; + + /** + * The FlexEvent.LOADING constant defines the value of the + * type property of the event object for a loading event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType loading + * @see mx.modules.ModuleLoader + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const LOADING:String = "loading"; + + /** + * The FlexEvent.MENU_KEY_PRESSED constant defines the value of the + * type property of the event object for a menuKeyPressed event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelabletrue
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType menuKeyPressed + * + * @langversion 3.0 + * @playerversion Flash 10.1 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public static const MENU_KEY_PRESSED:String = "menuKeyPressed"; + + /** + * The FlexEvent.MUTED_CHANGE constant defines the value of the + * type property of the event object for a mutedChange event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType mutedChange + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const MUTED_CHANGE:String = "mutedChange"; + + /** + * The FlexEvent.NAVIGATOR_STATE_LOADING constant defines the value of the + * type property of the event object for a navigatorStateLoading event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelabletrue
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType applicationRestoring + * + * @langversion 3.0 + * @playerversion Flash 10.1 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public static const NAVIGATOR_STATE_LOADING:String = "navigatorStateLoading"; + + /** + * The FlexEvent.NAVIGATOR_STATE_SAVING constant defines the value of the + * type property of the event object for a navigatorStateSaving event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelabletrue
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType navigatorStateSaving + * + * @langversion 3.0 + * @playerversion Flash 10.1 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public static const NAVIGATOR_STATE_SAVING:String = "navigatorStateSaving"; + + /** + * @private + */ + public static const NEW_CHILD_APPLICATION:String = "newChildApplication"; + + /** + * The FlexEvent.PREINITIALIZE constant defines the value of the + * type property of the event object for a preinitialize event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType preinitialize + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const PREINITIALIZE:String = "preinitialize"; + + /** + * @private + */ + public static const PRELOADER_DONE:String = "preloaderDone"; + + /** + * @private + */ + public static const PRELOADER_DOC_FRAME_READY:String = "preloaderDocFrameReady"; + + /** + * The FlexEvent.READY constant defines the value of the + * type property of the event object for a ready + * event. This event is dispatched by the BitmapImage and spark Image + * classes to denote that the assigned image source has fully loaded. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
returnValuenull
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myImage.addEventListener() to register an event listener, + * myImage is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
TypeFlexEvent.READY
+ * + * @eventType ready + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.1 + * @productversion Flex 4.5 + */ + public static const READY:String = "ready"; + + /** + * The FlexEvent.RENDER constant defines the value of the + * type property of the event object for an Event.RENDER event. + * + * Adding a listener on the SystemManager will add a listener for FlexEvent.RENDER + * events on the stage or the SystemManager if the application does not have + * access to the stage. The listener will also generate an Event.RENDER event. The + * listener function should expect to receive Event.RENDER events. + * + * @eventType flexEventRender + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const RENDER:String = "flexEventRender"; + + /** + * The FlexEvent.REMOVE constant defines the value of the + * type property of the event object for an remove event. + * + *

This event will only be dispatched when there are one or more relevant listeners + * attached to the dispatching object.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType remove + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const REMOVE:String = "remove"; + + /** + * The FlexEvent.REPEAT constant defines the value of the + * type property of the event object for a repeat event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType repeat + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const REPEAT:String = "repeat"; + + /** + * The FlexEvent.REPEAT_END constant defines the value of the + * type property of the event object for a repeatEnd event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType repeatEnd + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const REPEAT_END:String = "repeatEnd"; + + /** + * The FlexEvent.REPEAT_START constant defines the value of the + * type property of the event object for a repeatStart event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType repeatStart + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const REPEAT_START:String = "repeatStart"; + + /** + * The FlexEvent.SELECTION_CHANGE constant defines the value of the + * type property of the event object for a selectionChange event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType deferredContentCreate + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const SELECTION_CHANGE:String = "selectionChange"; + + /** + * The FlexEvent.SHOW constant defines the value of the + * type property of the event object for a show event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType show + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const SHOW:String = "show"; + + /** + * The FlexEvent.STATE_CHANGE_COMPLETE constant defines the value of the + * type property of the event object for a stateChangeComplete event. + * + *

This event will only be dispatched when there are one or more relevant listeners + * attached to the dispatching object.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @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 FlexEvent.STATE_CHANGE_INTERRUPTED constant defines the value of the + * type property of the event object for a stateChangeInterrupted event. + * + *

This event will only be dispatched when there are one or more relevant listeners + * attached to the dispatching object.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType stateChangeInterrupted + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public static const STATE_CHANGE_INTERRUPTED:String = "stateChangeInterrupted"; + + /** + * The FlexEvent.TRANSFORM_CHANGE constant defines the value of the + * type property of the event object for a transformChange event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType transformChange + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const TRANSFORM_CHANGE:String = "transformChange"; + + /** + * The FlexEvent.TRANSITION_START constant defines the value of the + * type property of the event object for a transitionStart event. + * + *

This event will only be dispatched when there are one or more relevant listeners + * attached to the dispatching object.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType transitionStart + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public static const TRANSITION_START:String = "transitionStart"; + + /** + * The FlexEvent.TRANSITION_END constant defines the value of the + * type property of the event object for a transitionEnd event. + * + *

This event will only be dispatched when there are one or more relevant listeners + * attached to the dispatching object.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType transitionEnd + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + public static const TRANSITION_END:String = "transitionEnd"; + + /** + * The FlexEvent.UPDATE_COMPLETE constant defines the value of the + * type property of the event object for a updateComplete event. + * + *

This event will only be dispatched when there are one or more relevant listeners + * attached to the dispatching object.

+ * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType updateComplete + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const UPDATE_COMPLETE:String = "updateComplete"; + + /** + * The FlexEvent.URL_CHANGED constant defines the value of the + * type property of the event object for a urlChanged event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType urlChanged + * @see mx.modules.ModuleLoader + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const URL_CHANGED:String = "urlChanged"; + + /** + * The FlexEvent.VALID constant defines the value of the + * type property of the event object for a valid event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType valid + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const VALID:String = "valid"; + + /** + * The FlexEvent.VALUE_COMMIT constant defines the value of the + * type property of the event object for a valueCommit + * event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType valueCommit + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const VALUE_COMMIT:String = "valueCommit"; + + //-------------------------------------------------------------------------- + // + // 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. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function FlexEvent(type:String, bubbles:Boolean = false, + cancelable:Boolean = false) + { + super(type, bubbles, cancelable); + } + + //-------------------------------------------------------------------------- + // + // Overridden methods: Event + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function clone():Event + { + return new FlexEvent(type, bubbles, cancelable); + } +} + +} diff --git a/src/mx/events/ModuleEvent.as b/src/mx/events/ModuleEvent.as new file mode 100644 index 00000000..82fdf15f --- /dev/null +++ b/src/mx/events/ModuleEvent.as @@ -0,0 +1,313 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.events +{ + +import flash.events.Event; +import flash.events.ProgressEvent; +import mx.modules.IModuleInfo; + +/** + * The ModuleEvent class represents the event object passed to the event listener + * for events related to dynamically-loaded modules. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class ModuleEvent extends ProgressEvent +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * Dispatched when there is an error downloading the module. + * The ModuleEvent.ERROR constant defines the value of the + * type property of the event object for an error event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + * + * + *
PropertyValue
bubblesfalse
bytesLoadedEmpty
bytesTotalEmpty
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
errorTextThe error message.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType error + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ERROR:String = "error"; + + /** + * Dispatched when the module is in the process of downloading. This module is + * dispatched at regular intervals during the download process. + * The ModuleEvent.PROGRESS constant defines the value of the + * type property of the event object for a progress event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + * + * + *
PropertyValue
bubblesfalse
bytesLoadedThe number of bytes loaded.
bytesTotalThe total number of bytes to load.
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
errorTextEmpty
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType progress + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const PROGRESS:String = "progress"; + + /** + * Dispatched when the module has finished downloading. + * The ModuleEvent.READY constant defines the value of the + * type property of the event object for a complete event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + * + * + *
PropertyValue
bubblesfalse
bytesLoadedThe number of bytes loaded.
bytesTotalThe total number of bytes to load.
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
errorTextEmpty
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType ready + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const READY:String = "ready"; + + /** + * Dispatched when enough of a module has been downloaded that you can get information + * about the module. You do this by calling the IFlexModuleFactory.info() + * method on the module. + * The ModuleEvent.SETUP constant defines the value of the + * type property of the event object for a setup event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + * + * + *
PropertyValue
bubblesfalse
bytesLoadedEmpty
bytesTotalEmpty
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
errorTextAn error message.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType setup + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const SETUP:String = "setup"; + + /** + * Dispatched when the module is unloaded. + * + * The ModuleEvent.UNLOAD constant defines the value of the + * type property of the event object for an unload event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + * + * + *
PropertyValue
bubblesfalse
bytesLoadedEmpty
bytesTotalEmpty
cancelablefalse
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
errorTextAn error message.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType unload + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const UNLOAD:String = "unload"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param type The type of event. Possible values are: + *
    + *
  • "progress" (ModuleEvent.PROGRESS);
  • + *
  • "ready" (ModuleEvent.READY);
  • + *
  • "setup" (ModuleEvent.SETUP);
  • + *
  • "error" (ModuleEvent.ERROR);
  • + *
  • "unload" (ModuleEvent.UNLOAD);
  • + *
+ * + * @param bubbles Determines whether the Event object + * participates in the bubbling stage of the event flow. + * + * @param cancelable Determines whether the Event object can be cancelled + * during event propagation. + * + * @param bytesLoaded The number of bytes loaded + * at the time the listener processes the event. + * + * @param bytesTotal The total number of bytes + * that will be loaded if the loading process succeeds. + * + * @param errorText The error message when the event type + * is ModuleEvent.ERROR. + * + * @param module An instance of an interface for a particular module. . + * + * @tiptext Constructor for ModuleEvent objects. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function ModuleEvent(type:String, bubbles:Boolean = false, + cancelable:Boolean = false, + bytesLoaded:uint = 0, bytesTotal:uint = 0, + errorText:String = null, module:IModuleInfo = null) + { + super(type, bubbles, cancelable, bytesLoaded, bytesTotal); + + this.errorText = errorText; + this._module = module; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // errorText + //---------------------------------- + + /** + * The error message if the type is ModuleEvent.ERROR; + * otherwise, it is null. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var errorText:String; + + //---------------------------------- + // module + //---------------------------------- + + private var _module:IModuleInfo; + + /** + * The target, which is an instance of an + * interface for a particular module. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get module():IModuleInfo + { + if (_module) return _module; + return target as IModuleInfo; + } + + //-------------------------------------------------------------------------- + // + // Overridden properties: Event + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function clone():Event + { + return new ModuleEvent(type, bubbles, cancelable, + bytesLoaded, bytesTotal, errorText, module); + } +} + +} diff --git a/src/mx/events/PropertyChangeEvent.as b/src/mx/events/PropertyChangeEvent.as new file mode 100644 index 00000000..4e69c341 --- /dev/null +++ b/src/mx/events/PropertyChangeEvent.as @@ -0,0 +1,282 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.events +{ + +import flash.events.Event; +import mx.events.PropertyChangeEventKind; + +/** + * The PropertyChangeEvent class represents the event object + * passed to the event listener when one of the properties of + * an object has changed, and provides information about the change. + * This event is used by collection classes, and is the only way for + * collections to know that the data they represent has changed. + * This event is also used by the Flex data binding mechanism. + * + * @see PropertyChangeEventKind + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class PropertyChangeEvent extends Event +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + // Note: If the value for CHANGE changes, + // update mx.utils.ObjectProxy's Bindable metadata. + + /** + * The PropertyChangeEvent.PROPERTY_CHANGE constant defines the value of the + * type property of the event object for a PropertyChange event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + * + * + * + * + *
PropertyValue
bubblesDetermined by the constructor; defaults to false.
cancelableDetermined by the constructor; defaults to false.
kindThe kind of change; PropertyChangeEventKind.UPDATE + * or PropertyChangeEventKind.DELETE.
oldValueThe original property value.
newValueThe new property value, if any.
propertyThe property that changed.
sourceThe object that contains the property that changed.
currentTargetThe Object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of the currentTarget.
targetThe Object that dispatched the event; + * it is not always the Object listening for the event. + * Use the currentTarget property to always access the + * Object listening for the event.
+ * + * @eventType propertyChange + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const PROPERTY_CHANGE:String = "propertyChange"; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Returns a new PropertyChangeEvent of kind + * PropertyChangeEventKind.UPDATE + * with the specified properties. + * This is a convenience method. + * + * @param source The object where the change occured. + * + * @param property A String, QName, or int + * specifying the property that changed, + * + * @param oldValue The value of the property before the change. + * + * @param newValue The value of the property after the change. + * + * @return A newly constructed PropertyChangeEvent + * with the specified properties. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function createUpdateEvent( + source:Object, + property:Object, + oldValue:Object, + newValue:Object):PropertyChangeEvent + { + var event:PropertyChangeEvent = + new PropertyChangeEvent(PROPERTY_CHANGE); + + event.kind = PropertyChangeEventKind.UPDATE; + event.oldValue = oldValue; + event.newValue = newValue; + event.source = source; + event.property = property; + + return event; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param type The event type; indicates the action that triggered 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 kind Specifies the kind of change. + * The possible values are PropertyChangeEventKind.UPDATE, + * PropertyChangeEventKind.DELETE, and null. + * + * @param property A String, QName, or int + * specifying the property that changed. + * + * @param oldValue The value of the property before the change. + * + * @param newValue The value of the property after the change. + * + * @param source The object that the change occured on. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function PropertyChangeEvent(type:String, bubbles:Boolean = false, + cancelable:Boolean = false, + kind:String = null, + property:Object = null, + oldValue:Object = null, + newValue:Object = null, + source:Object = null) + { + super(type, bubbles, cancelable); + + this.kind = kind; + this.property = property; + this.oldValue = oldValue; + this.newValue = newValue; + this.source = source; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // kind + //---------------------------------- + + /** + * Specifies the kind of change. + * The possible values are PropertyChangeEventKind.UPDATE, + * PropertyChangeEventKind.DELETE, and null. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var kind:String; + + //---------------------------------- + // newValue + //---------------------------------- + + /** + * The value of the property after the change. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var newValue:Object; + + //---------------------------------- + // oldValue + //---------------------------------- + + /** + * The value of the property before the change. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var oldValue:Object; + + //---------------------------------- + // property + //---------------------------------- + + /** + * A String, QName, or int specifying the property that changed. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var property:Object; + + //---------------------------------- + // source + //---------------------------------- + + /** + * The object that the change occured on. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var source:Object; + + //-------------------------------------------------------------------------- + // + // Class methods: Event + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function clone():Event + { + return new PropertyChangeEvent(type, bubbles, cancelable, kind, + property, oldValue, newValue, source); + } +} + +} diff --git a/src/mx/events/PropertyChangeEventKind.as b/src/mx/events/PropertyChangeEventKind.as new file mode 100644 index 00000000..9f584e6b --- /dev/null +++ b/src/mx/events/PropertyChangeEventKind.as @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.events +{ + +/** + * The PropertyChangeEventKind class defines the constant values + * for the kind property of the PropertyChangeEvent class. + * + * @see mx.events.PropertyChangeEvent + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public final class PropertyChangeEventKind +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * Indicates that the value of the property changed. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const UPDATE:String = "update"; + + /** + * Indicates that the property was deleted from the object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const DELETE:String = "delete"; +} + +} diff --git a/src/mx/events/ResourceEvent.as b/src/mx/events/ResourceEvent.as new file mode 100644 index 00000000..993fc730 --- /dev/null +++ b/src/mx/events/ResourceEvent.as @@ -0,0 +1,216 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.events +{ + +import flash.events.Event; +import flash.events.ProgressEvent; + +/** + * The ResourceEvent class represents an Event object that is dispatched + * when the ResourceManager loads the resource bundles in a resource module + * by calling the loadResourceModule() method. + * + * @see mx.resources.ResourceManager + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class ResourceEvent extends ProgressEvent +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * Dispatched when the resource module SWF file has finished loading. + * The ResourceEvent.COMPLETE constant defines the value of the + * type property of the event object for a complete event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + *
PropertyValue
bubblesfalse
cancelablefalse
currentTargetThe object that defines the + * event listener that handles the event. For example, if you use + * myButton.addEventListener() to register an event listener, + * myButton is the value of currentTarget.
errorTextEmpty
targetThe object that dispatched the event; + * it is not always the object listening for the event. + * Use the currentTarget property to always access the + * object that listens for the event.
+ * + * @eventType complete + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const COMPLETE:String = "complete"; + + /** + * Dispatched when there is an error loading the resource module SWF file. + * The ResourceEvent.ERROR constant defines the value of the + * type property of the event object for a error event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + * An error message. + * + *
PropertyValue
bubblesfalse
bytesLoadedEmpty
bytesTotalEmpty
cancelablefalse
currentTargetThe object that defines the + * event listener that handles the event. For example, if you use the + * myButton.addEventListener() method to register an event listener, + * myButton is the value of currentTarget.
errorText
targetThe object that dispatched the event; + * it is not always the object that is listening for the event. + * Use the currentTarget property to always access the + * object that listens for the event.
+ * + * @eventType error + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ERROR:String = "error"; + + /** + * Dispatched when the resource module SWF file is loading. + * The ResourceEvent.PROGRESS constant defines the value of the + * type property of the event object for a progress event. + * + *

The properties of the event object have the following values:

+ * + * + * + * + * + * + * + * Empty + * + *
PropertyValue
bubblesfalse
bytesLoadedThe number of bytes loaded.
bytesTotalThe total number of bytes to load.
cancelablefalse
currentTargetThe object that defines the + * event listener that handles the event. For example, if you use the + * myButton.addEventListener() method to register an event listener, + * myButton is the value of currentTarget.
errorText
targetThe object that dispatched the event; + * it is not always the object that listens for the event. + * Use the currentTarget property to always access the + * object that is listening for the event.
+ * + * @eventType progress + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const PROGRESS:String = "progress"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param type The value of the type property of the event object. Possible values are: + *
    + *
  • "progress" (ResourceEvent.PROGRESS)
  • + *
  • "complete" (ResourceEvent.COMPLETE)
  • + *
  • "error" (ResourceEvent.ERROR)
  • + *
+ * + * @param bubbles Determines whether the Event object + * participates in the bubbling stage of the event flow. + * + * @param cancelable Determines whether the Event object can be cancelled. + * + * @param bytesLoaded The number of bytes loaded + * at the time the listener processes the event. + * + * @param bytesTotal The total number of bytes + * that will ultimately be loaded if the loading process succeeds. + * + * @param errorText The error message of the error + * when type is ResourceEvent.ERROR. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function ResourceEvent(type:String, bubbles:Boolean = false, + cancelable:Boolean = false, + bytesLoaded:uint = 0, bytesTotal:uint = 0, + errorText:String = null) + { + super(type, bubbles, cancelable, bytesLoaded, bytesTotal); + + this.errorText = errorText; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // errorText + //---------------------------------- + + /** + * The error message if the type is ERROR; + * otherwise, it is null. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var errorText:String; + + //-------------------------------------------------------------------------- + // + // Overridden properties: Event + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function clone():Event + { + return new ResourceEvent(type, bubbles, cancelable, + bytesLoaded, bytesTotal, errorText); + } +} + +} diff --git a/src/mx/geom/CompoundTransform.as b/src/mx/geom/CompoundTransform.as new file mode 100644 index 00000000..ea436cb7 --- /dev/null +++ b/src/mx/geom/CompoundTransform.as @@ -0,0 +1,759 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// +package mx.geom +{ + //import __AS3__.vec.Vector; + + import flash.events.Event; + import flash.geom.Matrix; + import flash.geom.Matrix3D; + import flash.geom.Point; + import flash.geom.Vector3D; + + import mx.core.AdvancedLayoutFeatures; + import mx.utils.MatrixUtil; + + /** + * A CompoundTransform represents a 2D or 3D matrix transform. A compound transform represents a matrix that can be queried or set either as a 2D matrix, + * a 3D matrix, or as individual convenience transform properties such as x, y, scaleX, rotationZ, etc. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public class CompoundTransform + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function CompoundTransform() + { + } + + + + /** + * @private + * storage for transform properties. These values are concatenated together with the layout properties to + * form the actual computed matrix used to render the object. + */ + private var _rotationX:Number = 0; + private var _rotationY:Number = 0; + private var _rotationZ:Number = 0; + private var _scaleX:Number = 1; + private var _scaleY:Number = 1; + private var _scaleZ:Number = 1; + private var _x:Number = 0; + private var _y:Number = 0; + private var _z:Number = 0; + + private var _transformX:Number = 0; + private var _transformY:Number = 0; + private var _transformZ:Number = 0; + + + /** + * @private + * slots for the 2D and 3D matrix transforms. Note that + * these are only allocated and computed on demand -- many component instances will never use a 3D + * matrix, for example. + */ + private var _matrix:Matrix; + private var _matrix3D:Matrix3D; + + + /** + * @private + * bit field flags for indicating which transforms are valid -- the layout properties, the matrices, + * and the 3D matrices. Since developers can set any of the three programmatically, the last one set + * will always be valid, and the others will be invalid until validated on demand. + */ + private static const MATRIX_VALID:uint = 0x20; + private static const MATRIX3D_VALID:uint = 0x40; + private static const PROPERTIES_VALID:uint = 0x80; + + + /** + * @private + * flags for tracking whether the transform is 3D. A transform is 3D if any of the 3D properties -- rotationX/Y, scaleZ, or z -- are set. + */ + private static const IS_3D:uint = 0x200; + private static const M3D_FLAGS_VALID:uint = 0x400; + + /** + * @private + * constants to indicate which form of a transform -- the properties, matrix, or matrix3D -- is + * 'the source of truth.' + */ + public static const SOURCE_PROPERTIES:uint = 1; + public static const SOURCE_MATRIX:uint = 2; + public static const SOURCE_MATRIX3D:uint = 3; + + /** + * @private + * indicates the 'source of truth' for the transform. + */ + public var sourceOfTruth:uint = SOURCE_PROPERTIES; + + /** + * @private + * general storage for all of ur flags. + */ + private var _flags:uint = PROPERTIES_VALID; + + /** + * @private + * flags that get passed to the invalidate method indicating why the invalidation is happening. + */ + private static const INVALIDATE_FROM_NONE:uint = 0; + private static const INVALIDATE_FROM_PROPERTY:uint = 4; + private static const INVALIDATE_FROM_MATRIX:uint = 5; + private static const INVALIDATE_FROM_MATRIX3D:uint = 6; + + /** + * @private + * static data used by utility methods below + */ + private static var decomposition:Vector. = new Vector.(); + decomposition.push(0); + decomposition.push(0); + decomposition.push(0); + decomposition.push(0); + decomposition.push(0); + + private static const RADIANS_PER_DEGREES:Number = Math.PI / 180; + + //---------------------------------------------------------------------------- + + /** + * The x value of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set x(value:Number):void + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _x) + return; + translateBy(value-_x,0,0); + invalidate(INVALIDATE_FROM_NONE,false); + } + /** + * @private + */ + public function get x():Number + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + return _x; + } + + /** + * The y value of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set y(value:Number):void + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _y) + return; + translateBy(0,value-_y,0); + invalidate(INVALIDATE_FROM_NONE,false); + } + + /** + * @private + */ + public function get y():Number + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + return _y; + } + + /** + * The z value of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set z(value:Number):void + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _z) + return; + translateBy(0,0,value-_z); + invalidate(INVALIDATE_FROM_NONE,true); + } + + /** + * @private + */ + public function get z():Number + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + return _z; + } + + //------------------------------------------------------------------------------ + + + /** + * The rotationX, in degrees, of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set rotationX(value:Number):void + { + // clamp the rotation value between -180 and 180. This is what + // the Flash player does, so let's mimic it here too. + value = MatrixUtil.clampRotation(value); + + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _rotationX) + return; + _rotationX = value; + invalidate(INVALIDATE_FROM_PROPERTY,true); + } + + /** + * @private + */ + public function get rotationX():Number + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + return _rotationX; + } + + /** + * The rotationY, in degrees, of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set rotationY(value:Number):void + { + // clamp the rotation value between -180 and 180. This is what + // the Flash player does, so let's mimic it here too. + value = MatrixUtil.clampRotation(value); + + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _rotationY) + return; + _rotationY = value; + invalidate(INVALIDATE_FROM_PROPERTY,true); + } + + /** + * @private + */ + public function get rotationY():Number + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + return _rotationY; + } + + /** + * The rotationZ, in degrees, of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set rotationZ(value:Number):void + { + // clamp the rotation value between -180 and 180. This is what + // the Flash player does, so let's mimic it here too. + value = MatrixUtil.clampRotation(value); + + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _rotationZ) + return; + _rotationZ = value; + invalidate(INVALIDATE_FROM_PROPERTY,false); + } + + /** + * @private + */ + public function get rotationZ():Number + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + return _rotationZ; + } + + //------------------------------------------------------------------------------ + + + /** + * The scaleX of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set scaleX(value:Number):void + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _scaleX) + return; + _scaleX = value; + invalidate(INVALIDATE_FROM_PROPERTY,false); + } + + /** + * @private + */ + public function get scaleX():Number + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + return _scaleX; + } + + /** + * The scaleY of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set scaleY(value:Number):void + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _scaleY) + return; + _scaleY = value; + invalidate(INVALIDATE_FROM_PROPERTY,false); + } + + /** + * @private + */ + public function get scaleY():Number + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + return _scaleY; + } + + + /** + * The scaleZ of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set scaleZ(value:Number):void + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _scaleZ) + return; + _scaleZ = value; + invalidate(INVALIDATE_FROM_PROPERTY,true); + } + + /** + * @private + */ + public function get scaleZ():Number + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + return _scaleZ; + } + + + /** + * @private + * returns true if the transform has 3D values. + */ + public function get is3D():Boolean + { + if ((_flags & M3D_FLAGS_VALID) == 0) + update3DFlags(); + return ((_flags & IS_3D) != 0); + } + + //------------------------------------------------------------------------------ + /** + * The x value of the transform center. The transform center is kept fixed as rotation and scale are applied. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set transformX(value:Number):void + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _transformX) + return; + _transformX = value; + invalidate(INVALIDATE_FROM_PROPERTY,true); + } + + /** + * @private + */ + public function get transformX():Number + { + return _transformX; + } + + //------------------------------------------------------------------------------ + /** + * The y value of the tansform center. The transform center is kept fixed as rotation and scale are applied. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set transformY(value:Number):void + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _transformY) + return; + _transformY = value; + invalidate(INVALIDATE_FROM_PROPERTY,true); + } + + /** + * @private + */ + public function get transformY():Number + { + return _transformY; + } + //------------------------------------------------------------------------------ + /** + * The z value of the tansform center. The transform center is kept fixed as rotation and scale are applied. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set transformZ(value:Number):void + { + if ((_flags & PROPERTIES_VALID) == false) validatePropertiesFromMatrix(); + if (value == _transformZ) + return; + _transformZ = value; + invalidate(INVALIDATE_FROM_PROPERTY,true); + } + + /** + * @private + */ + public function get transformZ():Number + { + return _transformZ; + } + + //------------------------------------------------------------------------------ + + /** + * @private + * invalidates our various cached values. Any change to the CompoundTransform object that affects + * the various transforms should call this function. + * @param reason - the code indicating what changes to cause the invalidation. + * @param affects3D - a flag indicating whether the change affects the 2D/3D nature of the various transforms. + * @param dispatchChangeEvent - if true, the CompoundTransform will dispatch a change indicating that its underlying transforms + * have been modified. + */ + private function invalidate(reason:uint,affects3D:Boolean):void + { + //race("invalidating: " + reason); + switch(reason) + { + case INVALIDATE_FROM_PROPERTY: + sourceOfTruth = SOURCE_PROPERTIES; + _flags |= PROPERTIES_VALID; + _flags &= ~MATRIX_VALID; + _flags &= ~MATRIX3D_VALID; + break; + case INVALIDATE_FROM_MATRIX: + sourceOfTruth = SOURCE_MATRIX; + _flags |= MATRIX_VALID; + _flags &= ~PROPERTIES_VALID; + _flags &= ~MATRIX3D_VALID; + break; + case INVALIDATE_FROM_MATRIX3D: + sourceOfTruth = SOURCE_MATRIX3D; + _flags |= MATRIX3D_VALID; + _flags &= ~PROPERTIES_VALID; + _flags &= ~MATRIX_VALID; + break; + } + if (affects3D) + _flags &= ~M3D_FLAGS_VALID; + + } + + private static const EPSILON:Number = .001; + /** + * @private + * updates the flags that indicate whether the layout, offset, and/or computed transforms are 3D in nature. + * Since the user can set either the individual transform properties or the matrices directly, we compute these + * flags based on what the current 'source of truth' is for each of these values. + */ + private function update3DFlags():void + { + if ((_flags & M3D_FLAGS_VALID) == 0) + { + var matrixIs3D:Boolean = false; + + switch(sourceOfTruth) + { + case SOURCE_PROPERTIES: + matrixIs3D = ( // note that rotationZ is the same as rotation, and not a 3D affecting + (Math.abs(_scaleZ-1) > EPSILON) || // property. + ((Math.abs(_rotationX)+EPSILON)%360) > 2*EPSILON || + ((Math.abs(_rotationY)+EPSILON)%360) > 2*EPSILON || + Math.abs(_z) > EPSILON + ); + break; + case SOURCE_MATRIX: + matrixIs3D = false; + break; + case SOURCE_MATRIX3D: + var rawData:Vector. = _matrix3D.rawData; + matrixIs3D = (rawData[2] != 0 || // rotation y + rawData[6] != 0 || // rotation x + rawData[8] !=0 || // rotation y + rawData[10] != 1 || // scalez / rotation x / rotation y + rawData[14] != 0); // translation z + break; + } + + if (matrixIs3D) + _flags |= IS_3D; + else + _flags &= ~IS_3D; + + _flags |= M3D_FLAGS_VALID; + } + } + + + /** + * Applies the delta to the transform's translation component. Unlike setting the x, y, or z properties directly, + * this method can be safely called without changing the transform's concept of 'the source of truth'. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function translateBy(x:Number,y:Number,z:Number = 0):void + { + if (_flags & MATRIX_VALID) + { + _matrix.tx += x; + _matrix.ty += y; + } + if (_flags & PROPERTIES_VALID) + { + _x += x; + _y += y; + _z += z; + } + if (_flags & MATRIX3D_VALID) + { + var data:Vector. = _matrix3D.rawData; + data[12] += x; + data[13] += y; + data[14] += z; + _matrix3D.rawData = data; + } + invalidate(INVALIDATE_FROM_NONE,z != 0); + } + + + /** + * The 2D matrix either set directly by the user, or composed by combining the transform center, scale, rotation + * and translation, in that order. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get matrix():Matrix + { + + if (_flags & MATRIX_VALID) + return _matrix; + + if ((_flags & PROPERTIES_VALID) == false) + validatePropertiesFromMatrix(); + + var m:Matrix = _matrix; + if (m == null) + m = _matrix = new Matrix(); + else + m.identity(); + + AdvancedLayoutFeatures.build2DMatrix(m,_transformX,_transformY, + _scaleX,_scaleY, + _rotationZ, + _x,_y); + _flags |= MATRIX_VALID; + return m; + } + + /** + * @private + */ + public function set matrix(v:Matrix):void + { + if (_matrix== null) + { + _matrix = v.clone(); + } + else + { + _matrix.identity(); + _matrix.concat(v); + } + invalidate(INVALIDATE_FROM_MATRIX,false); + } + + /** + * @private + * decomposes the offset transform matrices down into the convenience offset properties. Note that this is not + * a bi-directional transformation -- it is possible to create a matrix that can't be fully represented in the + * convenience properties. This function will pull from the matrix or matrix3D values, depending on which was most + * recently set + */ + private function validatePropertiesFromMatrix():void + { + if (sourceOfTruth == SOURCE_MATRIX3D) + { + var result:Vector. = _matrix3D.decompose(); + _rotationX = result[1].x / RADIANS_PER_DEGREES; + _rotationY = result[1].y / RADIANS_PER_DEGREES; + _rotationZ = result[1].z / RADIANS_PER_DEGREES; + _scaleX = result[2].x; + _scaleY = result[2].y; + _scaleZ = result[2].z; + + if (_transformX != 0 || _transformY != 0 || _transformZ != 0) + { + var postTransformTCenter:Vector3D = _matrix3D.transformVector(new Vector3D(_transformX,_transformY,_transformZ)); + _x = postTransformTCenter.x - _transformX; + _y = postTransformTCenter.y - _transformY; + _z = postTransformTCenter.z - _transformZ; + } + else + { + _x = result[0].x; + _y = result[0].y; + _z = result[0].z; + } + } + else if (sourceOfTruth == SOURCE_MATRIX) + { + MatrixUtil.decomposeMatrix(decomposition,_matrix,_transformX,_transformY); + _x = decomposition[0]; + _y = decomposition[1]; + _z = 0; + _rotationX = 0; + _rotationY = 0; + _rotationZ = decomposition[2]; + _scaleX = decomposition[3]; + _scaleY = decomposition[4]; + _scaleZ = 1; + } + _flags |= PROPERTIES_VALID; + + } + + + + /** + * The 3D matrix either set directly by the user, or composed by combining the transform center, scale, rotation + * and translation, in that order. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get matrix3D():Matrix3D + { + if (_flags & MATRIX3D_VALID) + return _matrix3D; + + if ((_flags & PROPERTIES_VALID) == false) + validatePropertiesFromMatrix(); + + var m:Matrix3D = _matrix3D; + if (m == null) + m = _matrix3D = new Matrix3D(); + else + m.identity(); + + AdvancedLayoutFeatures.build3DMatrix(m,transformX,transformY,transformZ, + _scaleX,_scaleY,_scaleZ, + _rotationX,_rotationY,_rotationZ, + _x,_y,_z); + _flags |= MATRIX3D_VALID; + return m; + + } + + /** + * @private + */ + public function set matrix3D(v:Matrix3D):void + { + if (_matrix3D == null) + { + _matrix3D = v.clone(); + } + else + { + _matrix3D.identity(); + _matrix3D.append(v); + } + invalidate(INVALIDATE_FROM_MATRIX3D,true); + } + +} +} diff --git a/src/mx/geom/TransformOffsets.as b/src/mx/geom/TransformOffsets.as new file mode 100644 index 00000000..bf8878f3 --- /dev/null +++ b/src/mx/geom/TransformOffsets.as @@ -0,0 +1,368 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// +package mx.geom +{ + import flash.geom.Matrix; + import flash.geom.Matrix3D; + //import __AS3__.vec.Vector; + import flash.geom.Vector3D; + import flash.events.EventDispatcher; + import flash.events.Event; + import mx.core.mx_internal; + import mx.core.AdvancedLayoutFeatures; + import flash.geom.Point; + use namespace mx_internal; + + /** + * A CompoundTransform represents a 2D or 3D matrix transform. It can be used in the postLayoutTransformOffsets property on a UIComponent or GraphicElement. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public class TransformOffsets extends EventDispatcher + { + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function TransformOffsets() + { + } + + + + /** + * @private + * storage for transform properties. These values are concatenated together with the layout properties to + * form the actual computed matrix used to render the object. + */ + private var _rotationX:Number = 0; + private var _rotationY:Number = 0; + private var _rotationZ:Number = 0; + private var _scaleX:Number = 1; + private var _scaleY:Number = 1; + private var _scaleZ:Number = 1; + private var _x:Number = 0; + private var _y:Number = 0; + private var _z:Number = 0; + + /** + * @private + * flags for tracking whether the transform is 3D. A transform is 3D if any of the 3D properties -- rotationX/Y, scaleZ, or z -- are set. + */ + private static const IS_3D:uint = 0x200; + private static const M3D_FLAGS_VALID:uint = 0x400; + + /** + * @private + * general storage for all of our flags. + */ + private var _flags:uint = 0; + + /** + * @private + */ + mx_internal var owner:AdvancedLayoutFeatures; + //---------------------------------------------------------------------------- + + /** + * the x value added to the transform + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set x(value:Number):void + { + if (value == _x) + return; + _x = value; + invalidate(false); + } + /** + * @private + */ + public function get x():Number + { + return _x; + } + + /** + * the y value added to the transform + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set y(value:Number):void + { + if (value == _y) + return; + _y = value; + invalidate(false); + } + + /** + * @private + */ + public function get y():Number + { + return _y; + } + + /** + * the z value added to the transform + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set z(value:Number):void + { + if (value == _z) + return; + _z = value; + invalidate(true); + } + + /** + * @private + */ + public function get z():Number + { + return _z; + } + + //------------------------------------------------------------------------------ + + + /** + * the rotationX, in degrees, added to the transform + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set rotationX(value:Number):void + { + if (value == _rotationX) + return; + _rotationX = value; + invalidate(true); + } + + /** + * @private + */ + public function get rotationX():Number + { + return _rotationX; + } + + /** + * the rotationY, in degrees, added to the transform + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set rotationY(value:Number):void + { + if (value == _rotationY) + return; + _rotationY = value; + invalidate(true); + } + + /** + * @private + */ + public function get rotationY():Number + { + return _rotationY; + } + + /** + * the rotationZ, in degrees, added to the transform + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set rotationZ(value:Number):void + { + if (value == _rotationZ) + return; + _rotationZ = value; + invalidate(false); + } + + /** + * @private + */ + public function get rotationZ():Number + { + return _rotationZ; + } + + //------------------------------------------------------------------------------ + + + /** + * the multiplier applied to the scaleX of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set scaleX(value:Number):void + { + if (value == _scaleX) + return; + _scaleX = value; + invalidate(false); + } + + /** + * @private + */ + public function get scaleX():Number + { + return _scaleX; + } + + /** + * the multiplier applied to the scaleY of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set scaleY(value:Number):void + { + if (value == _scaleY) + return; + _scaleY = value; + invalidate(false); + } + + /** + * @private + */ + public function get scaleY():Number + { + return _scaleY; + } + + + /** + * the multiplier applied to the scaleZ of the transform. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function set scaleZ(value:Number):void + { + if (value == _scaleZ) + return; + _scaleZ = value; + invalidate(true); + } + + /** + * @private + */ + public function get scaleZ():Number + { + return _scaleZ; + } + + + /** + * @private + * returns true if the transform has 3D values. + */ + mx_internal function get is3D():Boolean + { + if ((_flags & M3D_FLAGS_VALID) == 0) + update3DFlags(); + return ((_flags & IS_3D) != 0); + } + + + //------------------------------------------------------------------------------ + + /** + * @private + * invalidates our various cached values. Any change to the CompoundTransform object that affects + * the various transforms should call this function. + * @param reason - the code indicating what changes to cause the invalidation. + * @param affects3D - a flag indicating whether the change affects the 2D/3D nature of the various transforms. + * @param dispatchChangeEvent - if true, the CompoundTransform will dispatch a change indicating that its underlying transforms + * have been modified. + */ + private function invalidate(affects3D:Boolean,dispatchChangeEvent:Boolean = true):void + { + if (affects3D) + _flags &= ~M3D_FLAGS_VALID; + + if (dispatchChangeEvent) + dispatchEvent(new Event(Event.CHANGE)); + } + + private static const EPSILON:Number = .001; + /** + * @private + * updates the flags that indicate whether the layout, offset, and/or computed transforms are 3D in nature. + * Since the user can set either the individual transform properties or the matrices directly, we compute these + * flags based on what the current 'source of truth' is for each of these values. + */ + private function update3DFlags():void + { + if ((_flags & M3D_FLAGS_VALID) == 0) + { + var matrixIs3D:Boolean = ( // note that rotationZ is the same as rotation, and not a 3D affecting + (Math.abs(_scaleZ-1) > EPSILON) || // property. + ((Math.abs(_rotationX)+EPSILON)%360) > 2*EPSILON || + ((Math.abs(_rotationY)+EPSILON)%360) > 2*EPSILON || + Math.abs(_z) > EPSILON + ); + if (matrixIs3D) + _flags |= IS_3D; + else + _flags &= ~IS_3D; + _flags |= M3D_FLAGS_VALID; + } + } + +} +} diff --git a/src/mx/graphics/BitmapFill.as b/src/mx/graphics/BitmapFill.as new file mode 100644 index 00000000..d6284e11 --- /dev/null +++ b/src/mx/graphics/BitmapFill.as @@ -0,0 +1,1059 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.display.Bitmap; +import flash.display.BitmapData; +import flash.display.DisplayObject; +import flash.display.Graphics; +import flash.events.Event; +import flash.events.EventDispatcher; +import flash.events.IOErrorEvent; +import flash.events.SecurityErrorEvent; +import flash.geom.ColorTransform; +import flash.geom.Matrix; +import flash.geom.Point; +import flash.geom.Rectangle; +import flash.net.URLRequest; +import flash.system.LoaderContext; + +import mx.events.PropertyChangeEvent; +import mx.geom.CompoundTransform; +import mx.utils.MatrixUtil; + + + +/** + * Defines a set of values used to fill an area on screen + * with a bitmap or other DisplayObject. + * + * @see mx.graphics.IFill + * @see flash.display.Bitmap + * @see flash.display.BitmapData + * @see flash.display.DisplayObject + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class BitmapFill extends EventDispatcher implements IFill +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function BitmapFill() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + private static const RADIANS_PER_DEGREES:Number = Math.PI / 180; + private static var transformMatrix:Matrix = new Matrix(); + private var nonRepeatAlphaSource:BitmapData; + private var _bitmapData:BitmapData; + + private var regenerateNonRepeatSource:Boolean = true; + private var lastBoundsWidth:Number = 0; + private var lastBoundsHeight:Number = 0; + private var applyAlphaMultiplier:Boolean = false; + private var nonRepeatSourceCreated:Boolean = false; + private var bitmapDataCreated:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // alpha + //---------------------------------- + + private var _alpha:Number = 1; + + /** + * The transparency of a fill. + * Possible values are 0.0 (invisible) through 1.0 (opaque). + * + * @default 1.0. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get alpha():Number + { + return _alpha; + } + + /** + * @private + */ + public function set alpha(value:Number):void + { + if (_alpha == value) + return; + + var oldValue:Number = _alpha; + + _alpha = value; + + applyAlphaMultiplier = true; + + if (_bitmapData && !_bitmapData.transparent && _alpha < 1 && oldValue == 1) + { + // If alpha is not opaque, then reapply the source because we might need + // to clone it. + var s:Object = _source; + _source = null; + source = s; + } + + dispatchFillChangedEvent("alpha", oldValue, value); + } + + //---------------------------------- + // compoundTransform + //---------------------------------- + + protected var compoundTransform:CompoundTransform; + + //---------------------------------- + // matrix + //---------------------------------- + + [Inspectable(category="General")] + + /** + * An array of values used for matrix transformation. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get matrix():Matrix + { + return compoundTransform ? compoundTransform.matrix : null; + } + + /** + * @private + */ + public function set matrix(value:Matrix):void + { + var oldValue:Matrix = matrix; + + var oldX:Number = x; + var oldY:Number = y; + var oldRotation:Number = rotation; + var oldScaleX:Number = scaleX; + var oldScaleY:Number = scaleY; + + if (value == null) + { + compoundTransform = null; + } + else + { + // Create the transform if none exists. + if (compoundTransform == null) + compoundTransform = new CompoundTransform(); + compoundTransform.matrix = value; // CompoundTransform will create a clone + + dispatchFillChangedEvent("x", oldX, compoundTransform.x); + dispatchFillChangedEvent("y", oldY, compoundTransform.y); + dispatchFillChangedEvent("scaleX", oldScaleX, compoundTransform.scaleX); + dispatchFillChangedEvent("scaleY", oldScaleY, compoundTransform.scaleY); + dispatchFillChangedEvent("rotation", oldRotation, compoundTransform.rotationZ); + } + } + + //---------------------------------- + // originX + //---------------------------------- + + private var _originX:Number = 0; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + [Deprecated(replacement="transformX", since="4.0")] + + /** + * The horizontal origin for the bitmap fill. + * The bitmap fill is offset so that this point appears at the origin. + * Scaling and rotation of the bitmap are performed around this point. + * + * @default 0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get originX():Number + { + return transformX; + } + + public function set originX(value:Number):void + { + transformX = value; + } + + //---------------------------------- + // originY + //---------------------------------- + + private var _originY:Number = 0; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + [Deprecated(replacement="transformY", since="4.0")] + + /** + * The vertical origin for the bitmap fill. + * The bitmap fill is offset so that this point appears at the origin. + * Scaling and rotation of the bitmap are performed around this point. + * + * @default 0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get originY():Number + { + return transformY; + } + + public function set originY(value:Number):void + { + transformY = value; + } + + //---------------------------------- + // offsetX + //---------------------------------- + + [Bindable("propertyChange")] + [Inspectable(category="General")] + [Deprecated(replacement="x", since="4.0")] + + /** + * How far the bitmap is horizontally offset from the origin. + * This adjustment is performed after rotation and scaling. + * + * @default 0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get offsetX():Number + { + return isNaN(x) ? 0 : x; + } + + public function set offsetX(value:Number):void + { + var oldValue:Number = isNaN(x) ? 0 : x; // Avoid warning since the offsetY getter is deprecated + x = value; + dispatchFillChangedEvent("offsetX", oldValue, value); + } + + //---------------------------------- + // offsetY + //---------------------------------- + + [Bindable("propertyChange")] + [Inspectable(category="General")] + [Deprecated(replacement="y", since="4.0")] + + /** + * How far the bitmap is vertically offset from the origin. + * This adjustment is performed after rotation and scaling. + * + * @default 0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get offsetY():Number + { + return isNaN(y) ? 0 : y; + } + + public function set offsetY(value:Number):void + { + var oldValue:Number = isNaN(y) ? 0 : y; // Avoid warning since the offsetY getter is deprecated + y = value; + dispatchFillChangedEvent("offsetY", oldValue, value); + } + + //---------------------------------- + // repeat + //---------------------------------- + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + [Deprecated(replacement="fillMode", since="4.0")] + + /** + * Whether the bitmap is repeated to fill the area. + * Set to true to cause the fill to tile outward + * to the edges of the filled region. + * Set to false to end the fill at the edge of the region. + * + * @default true + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get repeat():Boolean + { + return _fillMode == BitmapFillMode.REPEAT; + } + + public function set repeat(value:Boolean):void + { + var oldValue:Boolean = (_fillMode == BitmapFillMode.REPEAT); + if (value != oldValue) + { + //Setting repeat just sets fillMode to repeat + fillMode = value ? BitmapFillMode.REPEAT : BitmapFillMode.SCALE; + dispatchFillChangedEvent("repeat", oldValue, value); + } + } + + //---------------------------------- + // fillMode + //---------------------------------- + + /** + * @private + */ + protected var _fillMode:String = BitmapFillMode.SCALE; + + [Inspectable(category="General", enumeration="clip,repeat,scale", defaultValue="scale")] + + /** + * Determines how the bitmap fills in the dimensions. If you set the value + * of this property in a tag, use the string (such as "repeat"). If you set the value of + * this property in ActionScript, use the constant (such as BitmapFillMode.CLIP). + * + * When set to BitmapFillMode.CLIP ("clip"), the bitmap + * ends at the edge of the region. + * + * When set to BitmapFillMode.REPEAT ("repeat"), the bitmap + * repeats to fill the region. + * + * When set to BitmapFillMode.SCALE ("scale"), the bitmap + * stretches to fill the region. + * + * @default BitmapFillMode.SCALE + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public function get fillMode():String + { + return _fillMode; + } + + /** + * @private + */ + public function set fillMode(value:String):void + { + var oldValue:String = _fillMode; + if (value != _fillMode) + { + _fillMode = value; + dispatchFillChangedEvent("fillMode", oldValue, value); + } + } + + //---------------------------------- + // rotation + //---------------------------------- + + private var _rotation:Number = 0; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The number of degrees to rotate the bitmap. + * Valid values range from 0.0 to 360.0. + * + * @default 0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get rotation():Number + { + return compoundTransform ? compoundTransform.rotationZ : _rotation; + } + + public function set rotation(value:Number):void + { + if (value != rotation) + { + var oldValue:Number = rotation; + + if (compoundTransform) + compoundTransform.rotationZ = value; + else + _rotation = value; + dispatchFillChangedEvent("rotation", oldValue, value); + } + } + + //---------------------------------- + // scaleX + //---------------------------------- + + private var _scaleX:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The percent to horizontally scale the bitmap when filling, + * from 0.0 to 1.0. + * If 1.0, the bitmap is filled at its natural size. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleX():Number + { + return compoundTransform ? compoundTransform.scaleX : _scaleX; + } + + /** + * @private + */ + public function set scaleX(value:Number):void + { + if (value != scaleX) + { + var oldValue:Number = scaleX; + + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.scaleX = value; + } + else + { + _scaleX = value; + } + dispatchFillChangedEvent("scaleX", oldValue, value); + } + } + + //---------------------------------- + // scaleY + //---------------------------------- + + private var _scaleY:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The percent to vertically scale the bitmap when filling, + * from 0.0 to 1.0. + * If 1.0, the bitmap is filled at its natural size. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleY():Number + { + return compoundTransform ? compoundTransform.scaleY : _scaleY; + } + + /** + * @private + */ + public function set scaleY(value:Number):void + { + if (value != scaleY) + { + var oldValue:Number = scaleY; + + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.scaleY = value; + } + else + { + _scaleY = value; + } + dispatchFillChangedEvent("scaleY", oldValue, value); + } + } + + //---------------------------------- + // source + //---------------------------------- + + private var _source:Object; + + [Inspectable(category="General")] + + /** + * The source used for the bitmap fill. + * The fill can render from various graphical sources, + * including the following: + *
    + *
  • A Bitmap or BitmapData instance.
  • + *
  • A class representing a subclass of DisplayObject. + * The BitmapFill instantiates the class + * and creates a bitmap rendering of it.
  • + *
  • An instance of a DisplayObject. + * The BitmapFill copies it into a Bitmap for filling.
  • + *
+ * + * @default null + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get source():Object + { + return _source; + } + + /** + * @private + */ + public function set source(value:Object):void + { + if (value != _source) + { + var tmpSprite:DisplayObject; + var oldValue:Object = _source; + + var bitmapData:BitmapData; + var bitmapCreated:Boolean = false; + + if (value is Class) + { + var cls:Class = Class(value); + value = new cls(); + bitmapCreated = true; + } + + if (value is BitmapData) + { + bitmapData = BitmapData(value); + } + else if (value is Bitmap) + { + bitmapData = value.bitmapData; + } + else if (value is DisplayObject) + { + tmpSprite = value as DisplayObject; + } + else if (value == null) + { + // This will set source to null + } + else + { + return; + } + + if (!bitmapData && tmpSprite) + { + bitmapData = new BitmapData(tmpSprite.width, tmpSprite.height, true, 0); + bitmapData.draw(tmpSprite, new Matrix()); + bitmapCreated = true; + } + + // If the bitmapData isn't transparent (ex. JPEG) and alpha != 1, + // then copy it into a transparent bitmapData + if (bitmapData && !bitmapData.transparent && alpha != 1) + { + var transparentBitmap:BitmapData = new BitmapData(bitmapData.width, bitmapData.height, true); + transparentBitmap.draw(bitmapData); + bitmapCreated = true; + bitmapData = transparentBitmap; + } + + _source = value; + setBitmapData(bitmapData, bitmapCreated); + + dispatchFillChangedEvent("source", oldValue, value); + } + } + + //---------------------------------- + // smooth + //---------------------------------- + + private var _smooth:Boolean = false; + + [Inspectable(category="General")] + [Bindable("propertyChange")] + + /** + * A flag indicating whether to smooth the bitmap data + * when filling with it. + * + * @default false + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get smooth():Boolean + { + return _smooth; + } + + public function set smooth(value:Boolean):void + { + var oldValue:Boolean = _smooth; + if (value != oldValue) + { + _smooth = value; + dispatchFillChangedEvent("smooth", oldValue, value); + } + } + + //---------------------------------- + // transformX + //---------------------------------- + + private var _transformX:Number = 0; + + [Inspectable(category="General")] + + /** + * The x position transform point of the fill. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get transformX():Number + { + return compoundTransform ? compoundTransform.transformX : _transformX; + } + + /** + * @private + */ + public function set transformX(value:Number):void + { + if (transformX == value) + return; + + var oldValue:Number = transformX; + + if (compoundTransform) + compoundTransform.transformX = value; + else + _transformX = value; + + dispatchFillChangedEvent("transformX", oldValue, value); + } + + //---------------------------------- + // transformY + //---------------------------------- + + private var _transformY:Number = 0; + + [Inspectable(category="General")] + + /** + * The y position transform point of the fill. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get transformY():Number + { + return compoundTransform ? compoundTransform.transformY : _transformY; + } + + /** + * @private + */ + public function set transformY(value:Number):void + { + if (transformY == value) + return; + + var oldValue:Number = transformY; + + if (compoundTransform) + compoundTransform.transformY = value; + else + _transformY = value; + + dispatchFillChangedEvent("transformY", oldValue, value); + } + + + //---------------------------------- + // x + //---------------------------------- + + private var _x:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The distance by which to translate each point along the x axis. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get x():Number + { + return compoundTransform ? compoundTransform.x : _x; + } + + /** + * @private + */ + public function set x(value:Number):void + { + var oldValue:Number = x; + if (value != oldValue) + { + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.x = value; + } + else + { + _x = value; + } + dispatchFillChangedEvent("x", oldValue, value); + } + } + + //---------------------------------- + // y + //---------------------------------- + + private var _y:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The distance by which to translate each point along the y axis. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get y():Number + { + return compoundTransform ? compoundTransform.y : _y; + } + + /** + * @private + */ + public function set y(value:Number):void + { + var oldValue:Number = y; + if (value != oldValue) + { + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.y = value; + } + else + { + _y = value; + } + + dispatchFillChangedEvent("y", oldValue, value); + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public function begin(target:Graphics, targetBounds:Rectangle, targetOrigin:Point):void + { + var sourceAsBitmapData:BitmapData = _bitmapData; + + if (!sourceAsBitmapData) + return; + + var repeatFill:Boolean = (fillMode == BitmapFillMode.REPEAT); + + // If we need to apply the alpha, we need to make another clone. So dispose of the old one. + if (nonRepeatAlphaSource && applyAlphaMultiplier) + { + nonRepeatAlphaSource.dispose(); + nonRepeatAlphaSource = null; + } + + if (compoundTransform) + { + transformMatrix = compoundTransform.matrix; + transformMatrix.translate(targetOrigin.x, targetOrigin.y); + } + else + { + // Calculate default scaleX, scaleY + var defaultScaleX:Number = scaleX; + var defaultScaleY:Number = scaleY; + + // If fillMode is scale then scale to fill the content area + if (fillMode == BitmapFillMode.SCALE) + { + // calculate defaultScaleX only if explicit scaleX is not specified + if (isNaN(scaleX) && sourceAsBitmapData.width > 0) + defaultScaleX = targetBounds.width / sourceAsBitmapData.width; + + // calculate defaultScaleY if it's not already specified + if (isNaN(scaleY) && sourceAsBitmapData.height > 0) + defaultScaleY = targetBounds.height / sourceAsBitmapData.height; + } + + if (isNaN(defaultScaleX)) + defaultScaleX = 1; + if (isNaN(defaultScaleY)) + defaultScaleY = 1; + + // Calculate default x, y + var regX:Number = !isNaN(x) ? x + targetOrigin.x : targetBounds.left; + var regY:Number = !isNaN(y) ? y + targetOrigin.y : targetBounds.top; + + transformMatrix.identity(); + transformMatrix.translate(-transformX, -transformY); + transformMatrix.scale(defaultScaleX, defaultScaleY); + transformMatrix.rotate(rotation * RADIANS_PER_DEGREES); + transformMatrix.translate(regX + transformX, regY + transformY); + } + + // If repeat is true, fillMode is repeat, or if the source bitmap size + // equals or exceeds the targetBounds, just use the source bitmap + if (repeatFill || + (MatrixUtil.isDeltaIdentity(transformMatrix) && + transformMatrix.tx == targetBounds.left && + transformMatrix.ty == targetBounds.top && + targetBounds.width <= sourceAsBitmapData.width && + targetBounds.height <= sourceAsBitmapData.height)) + { + if (nonRepeatAlphaSource && nonRepeatSourceCreated) + { + nonRepeatAlphaSource.dispose(); + nonRepeatAlphaSource = null; + applyAlphaMultiplier = alpha != 1; + } + + nonRepeatSourceCreated = false; + } + else if (fillMode == BitmapFillMode.CLIP) + { + // Regenerate the nonRepeatSource if it wasn't previously created or if the bounds + // dimensions have changed. + if (regenerateNonRepeatSource || + lastBoundsWidth != targetBounds.width || + lastBoundsHeight != targetBounds.height) + { + // Release the old bitmap data + if (nonRepeatAlphaSource) + nonRepeatAlphaSource.dispose(); + + var bitmapTopLeft:Point = new Point(); + // We want the top left corner of the bitmap to be at (0,0) when we copy it. + // Save the translation and reapply it after the we have copied the bitmap + var tx:Number = transformMatrix.tx; + var ty:Number = transformMatrix.ty; + + transformMatrix.tx = 0; + transformMatrix.ty = 0; + + // Get the bounds of the transformed bitmap (minus translation) + var bitmapSize:Point = MatrixUtil.transformBounds( + sourceAsBitmapData.width, sourceAsBitmapData.height, + transformMatrix, + bitmapTopLeft); + + // Get the size of the bitmap using the bounds + // Pad the new bitmap size so that the borders are empty + var newW:Number = Math.ceil(bitmapSize.x) + 2; + var newY:Number = Math.ceil(bitmapSize.y) + 2; + + // Translate a rotated bitmap to ensure that the top left post-transformed corner is at (1,1) + transformMatrix.translate(1 - bitmapTopLeft.x, 1 - bitmapTopLeft.y); + + // Draw the transformed bitmapData into a new bitmapData that is the size of the bounds + // This will prevent the edge pixels getting repeated to fill the empty space + nonRepeatAlphaSource = new BitmapData(newW, newY, true, 0xFFFFFF); + nonRepeatAlphaSource.draw(sourceAsBitmapData, transformMatrix, null, null, null, smooth); + + // The transform matrix has already been applied to the source, so just use identity + // for the beginBitmapFill call + transformMatrix.identity(); + // We need to restore both the matrix translation and the rotation translation + transformMatrix.translate(tx + bitmapTopLeft.x - 1, ty + bitmapTopLeft.y - 1); + // Save off the bounds so we can compare it the next time this function is called + lastBoundsWidth = targetBounds.width; + lastBoundsHeight = targetBounds.height; + + nonRepeatSourceCreated = true; + + // Reapply the alpha if alpha is not 1. + applyAlphaMultiplier = alpha != 1; + } + } + + // Apply the alpha to a clone of the source. We don't want to modify the actual source because applying the alpha + // will modify the source and we have no way to restore the source back its original alpha value. + if (applyAlphaMultiplier) + { + // Clone the bitmapData if we didn't already make a copy for CLIP mode + if (!nonRepeatAlphaSource) + nonRepeatAlphaSource = sourceAsBitmapData.clone(); + + var ct:ColorTransform = new ColorTransform(); + ct.alphaMultiplier = alpha; + + nonRepeatAlphaSource.colorTransform(new Rectangle(0, 0, nonRepeatAlphaSource.width, nonRepeatAlphaSource.height), ct); + applyAlphaMultiplier = false; + } + + // If we have a nonRepeatAlphaSource, then use it. Otherwise, we just use the source. + if (nonRepeatAlphaSource) + sourceAsBitmapData = nonRepeatAlphaSource; + + target.beginBitmapFill(sourceAsBitmapData, transformMatrix, repeatFill, smooth); + } + + /** + * @private + */ + public function end(target:Graphics):void + { + target.endFill(); + } + + /** + * @private + */ + private function dispatchFillChangedEvent(prop:String, oldValue:*, + value:*):void + { + dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, prop, + oldValue, value)); + regenerateNonRepeatSource = true; + } + + /** + * @private + * Utility function that sets the underlying bitmapData property. + */ + private function setBitmapData(bitmapData:BitmapData, internallyCreated:Boolean = false):void + { + // Clear previous bitmapData + if (_bitmapData) + { + if (bitmapDataCreated) // Dispose the bitmap if we created it + _bitmapData.dispose(); + _bitmapData = null; + } + + bitmapDataCreated = internallyCreated; + applyAlphaMultiplier = alpha != 1; + _bitmapData = bitmapData; + + dispatchFillChangedEvent("bitmapData", null, null); + } +} + +} diff --git a/src/mx/graphics/BitmapFillMode.as b/src/mx/graphics/BitmapFillMode.as new file mode 100644 index 00000000..2c35f81b --- /dev/null +++ b/src/mx/graphics/BitmapFillMode.as @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +/** + * An enum of the resize modes that determine how a BitmapImage + * fills in the dimensions specified by the layout system. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ +public final class BitmapFillMode +{ + /** + * The bitmap ends at the edge of the region. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const CLIP:String = "clip"; + + /** + * The bitmap is repeated to fill the region. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const REPEAT:String = "repeat"; + + /** + * The bitmap fill stretches to fill the region. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const SCALE:String = "scale"; +} + +} diff --git a/src/mx/graphics/GradientBase.as b/src/mx/graphics/GradientBase.as new file mode 100644 index 00000000..f3a4ef7f --- /dev/null +++ b/src/mx/graphics/GradientBase.as @@ -0,0 +1,610 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.events.Event; +import flash.events.EventDispatcher; +import flash.geom.Matrix; + +import mx.core.mx_internal; +import mx.events.PropertyChangeEvent; +import mx.geom.CompoundTransform; + +use namespace mx_internal; + +[DefaultProperty("entries")] + +/** + * The GradientBase class is the base class for + * LinearGradient, LinearGradientStroke, and RadialGradient. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class GradientBase extends EventDispatcher +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function GradientBase() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + mx_internal var colors:Array /* of uint */ = []; + + /** + * @private + */ + mx_internal var ratios:Array /* of Number */ = []; + + /** + * @private + */ + mx_internal var alphas:Array /* of Number */ = []; + + //-------------------------------------------------------------------------- + // + // Class Properties + // + //-------------------------------------------------------------------------- + + + /** + * Value of the width and height of the untransformed gradient + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static const GRADIENT_DIMENSION:Number = 1638.4; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // angle + //---------------------------------- + + /** + * @private + * Storage for the angle property. + */ + mx_internal var _angle:Number; + + [Inspectable(category="General")] + [Deprecated(replacement="rotation")] + /** + * By default, the LinearGradientStroke defines a transition + * from left to right across the control. + * Use the angle property to control the transition direction. + * For example, a value of 180.0 causes the transition + * to occur from right to left, rather than from left to right. + * + * @default 0.0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get angle():Number + { + return _angle / Math.PI * 180; + } + + /** + * @private + */ + public function set angle(value:Number):void + { + var oldValue:Number = _angle; + _angle = value / 180 * Math.PI; + + dispatchGradientChangedEvent("angle", oldValue, _angle); + } + + //---------------------------------- + // compoundTransform + //---------------------------------- + + /** + * Holds the matrix and the convenience transform properties (x, y, and rotation). + * The compoundTransform is only created when the matrix property is set. + */ + protected var compoundTransform:CompoundTransform; + + //---------------------------------- + // entries + //---------------------------------- + + /** + * @private + * Storage for the entries property. + */ + private var _entries:Array = []; + + [Bindable("propertyChange")] + [Inspectable(category="General", arrayType="mx.graphics.GradientEntry")] + + /** + * An Array of GradientEntry objects + * defining the fill patterns for the gradient fill. + * + * @default [] + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get entries():Array + { + return _entries; + } + + /** + * @private + */ + public function set entries(value:Array):void + { + var oldValue:Array = _entries; + _entries = value; + + processEntries(); + + dispatchGradientChangedEvent("entries", oldValue, value); + } + + //---------------------------------- + // interpolationMethod + //---------------------------------- + + /** + * @private + * Storage for the interpolationMethod property. + */ + private var _interpolationMethod:String = "rgb"; + + [Inspectable(category="General", enumeration="rgb,linearRGB", defaultValue="rgb")] + + /** + * A value from the InterpolationMethod class + * that specifies which interpolation method to use. + * + *

Valid values are InterpolationMethod.LINEAR_RGB + * and InterpolationMethod.RGB.

+ * + * @default InterpolationMethod.RGB + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get interpolationMethod():String + { + return _interpolationMethod; + } + + /** + * @private + */ + public function set interpolationMethod(value:String):void + { + var oldValue:String = _interpolationMethod; + if (value != oldValue) + { + _interpolationMethod = value; + + dispatchGradientChangedEvent("interpolationMethod", oldValue, value); + } + } + + //---------------------------------- + // matrix + //---------------------------------- + + /** + * @private + * Storage for the matrix property. + */ + private var _matrix:Matrix; + + [Inspectable(category="General")] + + /** + * An array of values used for matrix transformation. + * + *

The gradient scaleX and scaleY properties represent pixels while the Matrix scale properties represent multipliers. + * Thus they are not compatible. + * Another difference is the most of the transform properties (x, y, scaleX, and scaleY) + * support NaN values while the matrix does not. A NaN value means that the gradient will choose an appropriate value.

+ * + *

The scaleX and scaleY properties can not be represented by the matrix. + * Once the matrix is set, scaleX and scaleY can no longer be set. + * Also, x and y can not be set to NaN. + * The matrix can be set back to null which also resets all of the convenience transform properties back to their default values.

+ * + *

If the matrix is set, then the gradient draw logic will scale the gradient to fit the bounds of the graphic element. + * It will then position the gradient in the upper left corner of the graphic element. + * Finally, it will apply the matrix transformations.

+ + *

By default, the LinearGradientStroke defines a transition + * from left to right across the control. + * Use the rotation property to control the transition direction. + * For example, a value of 180.0 causes the transition + * to occur from right to left, rather than from left to right.

+ * + * @default null + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get matrix():Matrix + { + return compoundTransform ? compoundTransform.matrix : null; + } + + /** + * @private + */ + public function set matrix(value:Matrix):void + { + var oldValue:Matrix = matrix; + + var oldX:Number = x; + var oldY:Number = y; + var oldRotation:Number = rotation; + + if (value == null) + { + compoundTransform = null; + x = NaN; + y = NaN; + rotation = 0; + } + else + { + // Create the transform if none exists. + if (compoundTransform == null) + compoundTransform = new CompoundTransform(); + compoundTransform.matrix = value; // CompoundTransform will create a clone + + dispatchGradientChangedEvent("x", oldX, compoundTransform.x); + dispatchGradientChangedEvent("y", oldY, compoundTransform.y); + dispatchGradientChangedEvent("rotation", oldRotation, compoundTransform.rotationZ); + } + } + + //---------------------------------- + // rotation + //---------------------------------- + + /** + * @private + * Storage for the rotation property. + */ + private var _rotation:Number = 0.0; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * By default, the LinearGradientStroke defines a transition + * from left to right across the control. + * Use the rotation property to control the transition direction. + * For example, a value of 180.0 causes the transition + * to occur from right to left, rather than from left to right. + * + * @default 0.0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get rotation():Number + { + return compoundTransform ? compoundTransform.rotationZ : _rotation; + } + + /** + * @private + */ + public function set rotation(value:Number):void + { + if (value != rotation) + { + var oldValue:Number = rotation; + + if (compoundTransform) + compoundTransform.rotationZ = value; + else + _rotation = value; + dispatchGradientChangedEvent("rotation", oldValue, value); + } + } + + //---------------------------------- + // spreadMethod + //---------------------------------- + + /** + * @private + * Storage for the spreadMethod property. + */ + private var _spreadMethod:String = "pad"; + + [Bindable("propertyChange")] + [Inspectable(category="General", enumeration="pad,reflect,repeat", defaultValue="pad")] + + /** + * A value from the SpreadMethod class + * that specifies which spread method to use. + * + *

Valid values are SpreadMethod.PAD, + * SpreadMethod.REFLECT, + * and SpreadMethod.REPEAT.

+ * + * @default SpreadMethod.PAD + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get spreadMethod():String + { + return _spreadMethod; + } + + /** + * @private + */ + public function set spreadMethod(value:String):void + { + var oldValue:String = _spreadMethod; + if (value != oldValue) + { + _spreadMethod = value; + dispatchGradientChangedEvent("spreadMethod", oldValue, value); + } + } + + //---------------------------------- + // x + //---------------------------------- + + private var _x:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The distance by which to translate each point along the x axis. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get x():Number + { + return compoundTransform ? compoundTransform.x : _x; + } + + /** + * @private + */ + public function set x(value:Number):void + { + var oldValue:Number = x; + if (value != oldValue) + { + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.x = value; + } + else + { + _x = value; + } + dispatchGradientChangedEvent("x", oldValue, value); + } + } + + //---------------------------------- + // y + //---------------------------------- + + private var _y:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The distance by which to translate each point along the y axis. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get y():Number + { + return compoundTransform ? compoundTransform.y : _y; + } + + /** + * @private + */ + public function set y(value:Number):void + { + var oldValue:Number = y; + if (value != oldValue) + { + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.y = value; + } + else + { + _y = value; + } + + dispatchGradientChangedEvent("y", oldValue, value); + } + } + + mx_internal function get rotationInRadians():Number + { + return rotation / 180 * Math.PI; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Extract the gradient information in the public entries + * Array into the internal colors, ratios, + * and alphas arrays. + */ + private function processEntries():void + { + colors = []; + ratios = []; + alphas = []; + + if (!_entries || _entries.length == 0) + return; + + var ratioConvert:Number = 255; + + var i:int; + + var n:int = _entries.length; + for (i = 0; i < n; i++) + { + var e:GradientEntry = _entries[i]; + e.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, + entry_propertyChangeHandler, false, 0, true); + colors.push(e.color); + alphas.push(e.alpha); + ratios.push(e.ratio * ratioConvert); + } + + if (isNaN(ratios[0])) + ratios[0] = 0; + + if (isNaN(ratios[n - 1])) + ratios[n - 1] = 255; + + i = 1; + + while (true) + { + while (i < n && !isNaN(ratios[i])) + { + i++; + } + + if (i == n) + break; + + var start:int = i - 1; + + while (i < n && isNaN(ratios[i])) + { + i++; + } + + var br:Number = ratios[start]; + var tr:Number = ratios[i]; + + for (var j:int = 1; j < i - start; j++) + { + ratios[j] = br + j * (tr - br) / (i - start); + } + } + } + + /** + * Dispatch a gradientChanged event. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + mx_internal function dispatchGradientChangedEvent(prop:String, + oldValue:*, value:*):void + { + dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, prop, + oldValue, value)); + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function entry_propertyChangeHandler(event:Event):void + { + processEntries(); + + dispatchGradientChangedEvent("entries", entries, entries); + } +} + +} diff --git a/src/mx/graphics/GradientEntry.as b/src/mx/graphics/GradientEntry.as new file mode 100644 index 00000000..7a442f4f --- /dev/null +++ b/src/mx/graphics/GradientEntry.as @@ -0,0 +1,222 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2004-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.events.Event; +import flash.events.EventDispatcher; +import mx.events.PropertyChangeEvent; + +/** + * The GradientEntry class defines the objects + * that control a transition as part of a gradient fill. + * You use this class with the LinearGradient and RadialGradient classes + * to define a gradient fill. + * + * @mxml + * + *

The <mx:GradientEntry> tag inherits all the tag attributes + * of its superclass, and adds the following tag attributes:

+ * + *
+ *  <mx:GradientEntry
+ *    Properties
+ *    alpha="1.0"
+ *    color="0x000000"
+ *    ratio="-1"
+ *  />
+ *  
+ * + * @see mx.graphics.LinearGradient + * @see mx.graphics.RadialGradient + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class GradientEntry extends EventDispatcher +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param color The color for this gradient entry. + * The default value is 0x000000 (black). + * + * @param ratio Where in the graphical element the associated color is + * sampled at 100%. + * Flex uniformly spaces any GradientEntries + * with missing ratio values. + * The default value is NaN. + * + * @param alpha The alpha value for this entry in the gradient. + * This parameter is optional. The default value is 1.0. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function GradientEntry(color:uint = 0x000000, + ratio:Number = NaN, + alpha:Number = 1.0) + { + super(); + + this.color = color; + this.ratio = ratio; + this.alpha = alpha; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // alpha + //---------------------------------- + + private var _alpha:Number = 1.0; + + [Bindable("propertyChange")] + [Inspectable(category="General", defaultValue="1", minValue="0.0", maxValue="1.0")] + + /** + * The transparency of a gradient fill. + * Possible values are 0.0 (invisible) through 1.0 (opaque). + * + * @default 1.0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get alpha():Number + { + return _alpha; + } + + public function set alpha(value:Number):void + { + var oldValue:Number = _alpha; + if (value != oldValue) + { + _alpha = value; + dispatchEntryChangedEvent("alpha", oldValue, value); + } + } + + //---------------------------------- + // color + //---------------------------------- + + private var _color:uint; + + [Bindable("propertyChange")] + [Inspectable(category="General", format="Color")] + + /** + * The color value for a gradient fill. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get color():uint + { + return _color; + } + + public function set color(value:uint):void + { + var oldValue:uint = _color; + if (value != oldValue) + { + _color = value; + dispatchEntryChangedEvent("color", oldValue, value); + } + } + + //---------------------------------- + // ratio + //---------------------------------- + + private var _ratio:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General", minValue="0.0", maxValue="1.0")] + + /** + * Where in the graphical element, as a percentage from 0.0 to 1.0, + * Flex samples the associated color at 100%. + * For example, a ratio of 0.33 means Flex begins the transition + * to that color 33% of the way through the graphical element. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get ratio():Number + { + return _ratio; + } + + public function set ratio(value:Number):void + { + var oldValue:Number = _ratio; + if (value != oldValue) + { + _ratio = value; + dispatchEntryChangedEvent("ratio", oldValue, value); + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function dispatchEntryChangedEvent(prop:String, + oldValue:*, value:*):void + { + if (hasEventListener("propertyChange")) + dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, prop, + oldValue, value)); + } +} + +} diff --git a/src/mx/graphics/GradientStroke.as b/src/mx/graphics/GradientStroke.as new file mode 100644 index 00000000..2212eab0 --- /dev/null +++ b/src/mx/graphics/GradientStroke.as @@ -0,0 +1,488 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ +import flash.display.CapsStyle; +import flash.display.Graphics; +import flash.display.GraphicsGradientFill; +import flash.display.GraphicsStroke; +import flash.display.JointStyle; +import flash.geom.Point; +import flash.geom.Rectangle; + +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * The GradientStroke class lets you specify a gradient filled stroke. + * You use the GradientStroke class, along with the GradientEntry class, + * to define a gradient stroke. + * + * @see mx.graphics.Stroke + * @see mx.graphics.GradientEntry + * @see flash.display.Graphics + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class GradientStroke extends GradientBase implements IStroke +{ + /** + * Constructor. + * + * @param weight Specifies the line weight, in pixels. + * This parameter is optional, + * with a default value of 1. + * + * @param pixelHinting A Boolean value that specifies + * whether to hint strokes to full pixels. + * This affects both the position of anchors of a curve + * and the line stroke size itself. + * With pixelHinting set to true, + * Flash Player and AIR hint line widths to full pixel widths. + * With pixelHinting set to false, + * disjoints can appear for curves and straight lines. + * This parameter is optional, + * with a default value of false. + * + * @param scaleMode A value from the LineScaleMode class + * that specifies which scale mode to use. + * Valid values are LineScaleMode.HORIZONTAL, + * LineScaleMode.NONE, LineScaleMode.NORMAL, + * and LineScaleMode.VERTICAL. + * This parameter is optional, + * with a default value of LineScaleMode.NORMAL. + * + * @param caps A value from the CapsStyle class + * that specifies the type of caps at the end of lines. + * Valid values are CapsStyle.NONE, + * CapsStyle.ROUND, and CapsStyle.SQUARE. + * A null value is equivalent to + * CapsStyle.ROUND. + * This parameter is optional, + * with a default value of CapsStyle.ROUND. + * + * @param joints A value from the JointStyle class + * that specifies the type of joint appearance used at angles. + * Valid values are JointStyle.BEVEL, + * JointStyle.MITER, and JointStyle.ROUND. + * A null value is equivalent to + * JointStyle.ROUND. + * This parameter is optional, + * with a default value of JointStyle.ROUND. + * + * @param miterLimit A number that indicates the limit + * at which a miter is cut off. + * Valid values range from 1 to 255 + * (and values outside of that range are rounded to 1 or 255). + * This value is only used if the jointStyle property + * is set to miter. + * The miterLimit value represents the length that a miter + * can extend beyond the point at which the lines meet to form a joint. + * The value expresses a factor of the line thickness. + * For example, with a miterLimit factor of 2.5 and a + * thickness of 10 pixels, the miter is cut off at 25 pixels. + * This parameter is optional, + * with a default value of 3. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function GradientStroke(weight:Number = 1, + pixelHinting:Boolean = false, + scaleMode:String = "normal", + caps:String = "round", + joints:String = "round", + miterLimit:Number = 3) + { + super(); + + this.weight = weight; + this.pixelHinting = pixelHinting; + this.scaleMode = scaleMode; + this.caps = caps; + this.joints = joints; + this.miterLimit = miterLimit; + } + + //---------------------------------- + // caps + //---------------------------------- + + /** + * @private + * Storage for the caps property. + */ + private var _caps:String = CapsStyle.ROUND; + + [Bindable("propertyChange")] + [Inspectable(category="General", enumeration="round,square,none", defaultValue="round")] + + /** + * Specifies the appearance of the ends of lines. + * + *

Valid values are CapsStyle.NONE, + * CapsStyle.ROUND, and CapsStyle.SQUARE. + * A null value is equivalent to + * CapsStyle.ROUND.

+ * + * @default CapsStyle.ROUND + * + * @see flash.display.CapStyle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get caps():String + { + return _caps; + } + + /** + * @private + */ + public function set caps(value:String):void + { + var oldValue:String = _caps; + if (value != oldValue) + { + _caps = value; + + dispatchGradientChangedEvent("caps", oldValue, value); + } + } + + //---------------------------------- + // joints + //---------------------------------- + + /** + * @private + * Storage for the joints property. + */ + private var _joints:String = JointStyle.ROUND; + + [Bindable("propertyChange")] + [Inspectable(category="General", enumeration="round,bevel,miter", defaultValue="round")] + + /** + * A value from the JointStyle class that specifies the type + * of joint appearance used at angles. + * + *

Valid values are JointStyle.BEVEL, + * JointStyle.MITER, and JointStyle.ROUND. + * A null value is equivalent to + * JointStyle.ROUND.

+ * + * @default JointStyle.ROUND + * + * @see flash.display.JointStyle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get joints():String + { + return _joints; + } + + /** + * @private + */ + public function set joints(value:String):void + { + var oldValue:String = _joints; + if (value != oldValue) + { + _joints = value; + + dispatchGradientChangedEvent("joints", oldValue, value); + } + } + + //---------------------------------- + // miterLimit + //---------------------------------- + + /** + * @private + * Storage for the miterLimit property. + */ + private var _miterLimit:Number = 3; + + [Bindable("propertyChange")] + [Inspectable(category="General", minValue="0.0", maxValue="255.0")] + + /** + * A number that indicates the limit at which a miter is cut off. + * + *

Valid values range from 0 to 255 + * (and values outside of that range are rounded to 0 or 255).

+ * + *

This value is only used if the jointStyle property + * is set to JointStyle.MITER.

+ * + *

The value of the miterLimit property represents the length that a miter + * can extend beyond the point at which the lines meet to form a joint. + * The value expresses a factor of the line thickness. + * For example, with a miterLimit factor of 2.5 + * and a thickness of 10 pixels, + * the miter is cut off at 25 pixels.

+ * + * @default 3 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get miterLimit():Number + { + return _miterLimit; + } + + /** + * @private + */ + public function set miterLimit(value:Number):void + { + var oldValue:Number = _miterLimit; + if (value != oldValue) + { + _miterLimit = value; + + dispatchGradientChangedEvent("miterLimit", oldValue, value); + } + } + + //---------------------------------- + // pixelHinting + //---------------------------------- + + /** + * @private + * Storage for the pixelHinting property. + */ + private var _pixelHinting:Boolean = false; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * A Boolean value that specifies whether to hint strokes to full pixels. + * + *

This affects both the position of anchors of a curve + * and the line stroke size itself.

+ * + *

With pixelHinting set to true, + * Flash Player and AIR hint line widths to full pixel widths. + * With pixelHinting set to false, + * disjoints can appear for curves and straight lines.

+ * + * @default false + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get pixelHinting():Boolean + { + return _pixelHinting; + } + + /** + * @private + */ + public function set pixelHinting(value:Boolean):void + { + var oldValue:Boolean = _pixelHinting; + if (value != oldValue) + { + _pixelHinting = value; + + dispatchGradientChangedEvent("pixelHinting", oldValue, value); + } + } + + //---------------------------------- + // scaleMode + //---------------------------------- + + /** + * @private + * Storage for the scaleMode property. + */ + private var _scaleMode:String = "normal"; + + [Bindable("propertyChange")] + [Inspectable(category="General", enumeration="normal,vertical,horizontal,none", defaultValue="normal")] + + /** + * Specifies which scale mode to use. Value valids are: + * + *
    + *
  • + * LineScaleMode.NORMAL— + * Always scale the line thickness when the object is scaled (the default). + *
  • + *
  • + * LineScaleMode.NONE— + * Never scale the line thickness. + *
  • + *
  • + * LineScaleMode.VERTICAL— + * Do not scale the line thickness if the object is scaled vertically + * only. + *
  • + *
  • + * LineScaleMode.HORIZONTAL— + * Do not scale the line thickness if the object is scaled horizontally + * only. + *
  • + *
+ * + * @default LineScaleMode.NORMAL + * + * @see flash.display.LineScaleMode + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleMode():String + { + return _scaleMode; + } + + /** + * @private + */ + public function set scaleMode(value:String):void + { + var oldValue:String = _scaleMode; + if (value != oldValue) + { + _scaleMode = value; + + dispatchGradientChangedEvent("scaleMode", oldValue, value); + } + } + + //---------------------------------- + // weight + //---------------------------------- + + /** + * @private + * Storage for the weight property. + */ + private var _weight:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General", minValue="0.0")] + + /** + * The stroke weight, in pixels. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get weight():Number + { + return _weight; + } + + /** + * @private + */ + public function set weight(value:Number):void + { + var oldValue:Number = _weight; + if (value != oldValue) + { + _weight = value; + + dispatchGradientChangedEvent("weight", oldValue, value); + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4 + */ + public function apply(g:Graphics, targetBounds:Rectangle, targetOrigin:Point):void + { + // Sub-classes must implement + } + + + /** + * @inheritDoc + */ + public function createGraphicsStroke(targetBounds:Rectangle, targetOrigin:Point):GraphicsStroke + { + // Construct a new GraphicsStroke object and set all of + // its properties to match the gradient stroke's + // properties + var graphicsStroke:GraphicsStroke = new GraphicsStroke(); + graphicsStroke.thickness = weight; + graphicsStroke.miterLimit = miterLimit; + graphicsStroke.pixelHinting = pixelHinting; + graphicsStroke.scaleMode = scaleMode; + + // There is a bug in Drawing API-2 where if no caps is + // specified, a value of 'none' is used instead of 'round' + graphicsStroke.caps = (!caps) ? CapsStyle.ROUND : caps; + + // Create the GraphicsGradientFill matching the + // gradient stroke's properties and set that as the + // fill for the GraphicsStroke object + var graphicsGradientFill:GraphicsGradientFill = + new GraphicsGradientFill(); + + graphicsGradientFill.colors = colors; + graphicsGradientFill.alphas = alphas; + graphicsGradientFill.ratios = ratios; + graphicsGradientFill.spreadMethod = spreadMethod; + graphicsGradientFill.interpolationMethod = interpolationMethod; + + graphicsStroke.fill = graphicsGradientFill; + + return graphicsStroke; + } + +} + +} diff --git a/src/mx/graphics/IFill.as b/src/mx/graphics/IFill.as new file mode 100644 index 00000000..c7c2cbc9 --- /dev/null +++ b/src/mx/graphics/IFill.as @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2004-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.display.Graphics; +import flash.geom.Point; +import flash.geom.Rectangle; + +/** + * Defines the interface that classes + * that perform a fill must implement. + * + * @see mx.graphics.LinearGradient + * @see mx.graphics.RadialGradient + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IFill +{ + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Starts the fill. + * + * @param target The target Graphics object that is being filled. + * + * @param targetBounds The Rectangle object that defines the size of the fill + * inside the target. + * If the dimensions of the Rectangle are larger than the dimensions + * of the target, the fill is clipped. + * If the dimensions of the Rectangle are smaller than the dimensions + * of the target, the fill expands to fill the entire + * target. + * + * @param targetOrigin The Point that defines the origin (0,0) of the shape in the + * coordinate system of target. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function begin(target:Graphics, targetBounds:Rectangle, targetOrigin:Point):void; + + /** + * Ends the fill. + * + * @param target The Graphics object that is being filled. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function end(target:Graphics):void; +} + +} diff --git a/src/mx/graphics/IStroke.as b/src/mx/graphics/IStroke.as new file mode 100644 index 00000000..582a326b --- /dev/null +++ b/src/mx/graphics/IStroke.as @@ -0,0 +1,175 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.display.Graphics; +import flash.display.GraphicsStroke; +import flash.geom.Point; +import flash.geom.Rectangle; + +/** + * Defines the interface that classes that define a line must implement. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IStroke +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // weight + //---------------------------------- + + /** + * The line weight, in pixels. + * For many chart lines, the default value is 1 pixel. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get weight():Number; + + /** + * @private + */ + function set weight(value:Number):void; + + //---------------------------------- + // scaleMode + //---------------------------------- + + /** + * A value from the LineScaleMode class + * that specifies which scale mode to use. + * Value valids are: + * + *
    + *
  • + * LineScaleMode.NORMAL— + * Always scale the line thickness when the object is scaled (the default). + *
  • + *
  • + * LineScaleMode.NONE— + * Never scale the line thickness. + *
  • + *
  • + * LineScaleMode.VERTICAL— + * Do not scale the line thickness if the object is scaled vertically + * only. + *
  • + *
  • + * LineScaleMode.HORIZONTAL— + * Do not scale the line thickness if the object is scaled horizontally + * only. + *
  • + *
+ * + * @default LineScaleMode.NORMAL + * + * @see flash.display.LineScaleMode + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get scaleMode():String; + + //---------------------------------- + // miterLimit + //---------------------------------- + + /** + * Indicates the limit at which a miter is cut off. + * Valid values range from 0 to 255. + * + * @default 3 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get miterLimit():Number; + + //---------------------------------- + // joints + //---------------------------------- + + /** + * Specifies the appearance of line intersections used at angles. + * Valid values are JointStyle.ROUND, JointStyle.MITER, + * and JointStyle.BEVEL. + * + * @see flash.display.JoingStyle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get joints():String; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Applies the properties to the specified Graphics object. + * + * @param graphics The Graphics object to apply the properties to. + * + * @param targetBounds The bounds of the shape that the stroke is applied to. + * + * @param targetOrigin The Point that defines the origin (0,0) of the shape in the + * coordinate system of target. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function apply(graphics:Graphics, targetBounds:Rectangle, targetOrigin:Point):void; + + /** + * Generates a GraphicsStroke object representing + * this stroke. + * + * @param targetBounds The stroke's bounding box. + * + * @param targetOrigin The Point that defines the origin (0,0) of the shape in the + * coordinate system of target. + * + * @return The Drawing API-2 GraphicsStroke object representing + * this stroke. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + function createGraphicsStroke(targetBounds:Rectangle, targetOrigin:Point):GraphicsStroke; +} + +} diff --git a/src/mx/graphics/LinearGradient.as b/src/mx/graphics/LinearGradient.as new file mode 100644 index 00000000..6d462712 --- /dev/null +++ b/src/mx/graphics/LinearGradient.as @@ -0,0 +1,342 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2004-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.display.GradientType; +import flash.display.Graphics; +import flash.geom.Matrix; +import flash.geom.Point; +import flash.geom.Rectangle; + +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * The LinearGradient class lets you specify the fill of a graphical element, + * where a gradient specifies a gradual color transition in the fill color. + * You add a series of GradientEntry objects + * to the LinearGradient object's entries Array + * to define the colors that make up the gradient fill. + * + *

In MXML, you define a LinearGradient by adding a series + * of GradientEntry objects, as the following example shows: + *

+ *  <mx:fill>
+ *  	<mx:LinearGradient>
+ *  		<mx:entries>
+ *  			<mx:GradientEntry color="0xC5C551" ratio="0.00" alpha="0.5"/>
+ *  			<mx:GradientEntry color="0xFEFE24" ratio="0.33" alpha="0.5"/>
+ *  			<mx:GradientEntry color="0xECEC21" ratio="0.66" alpha="0.5"/>
+ *  		</mx:entries>
+ *  	</mx:LinearGradient>
+ *  </mx:fill>
+ *  
+ *

+ * + *

You can also define a LinearGradient as a fill for any graphic element + * in ActionScript, as the following example shows: + *

+ *  
+ *  <?xml version="1.0"?>
+ *  <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
+ *  	<mx:Script>
+ *  	import flash.display.Graphics;
+ *  	import flash.geom.Rectangle;
+ *  	import mx.graphics.GradientEntry;
+ *  	import mx.graphics.LinearGradient;
+ *  
+ *  	private function init():void
+ *      {
+ *  		var w:Number = 200;
+ *  		var h:Number = 200;
+ *  
+ *  		var s:Sprite = new Sprite();
+ *  		// Add the new Sprite to the display list.
+ *  		rawChildren.addChild(s);	
+ *  
+ *  		var g:Graphics = s.graphics;
+ *  		g.lineStyle(1, 0x33CCFF, 1.0);
+ *  
+ *  		var fill:LinearGradient = new LinearGradient();
+ *  		
+ *  		var g1:GradientEntry = new GradientEntry(0xFFCC66, 0.00, 0.5);
+ *  		var g2:GradientEntry = new GradientEntry(0x000000, 0.33, 0.5);
+ *  		var g3:GradientEntry = new GradientEntry(0x99FF33, 0.66, 0.5);
+ *    		
+ *   		fill.entries = [ g1, g2, g3 ];
+ *  		fill.angle = 240;
+ *  
+ *   		// Draw a box and fill it with the LinearGradient.
+ *  		g.moveTo(0, 0);
+ *  		fill.begin(g, new Rectangle(0, 0, w, h));
+ *  		g.lineTo(w, 0);
+ *  		g.lineTo(w, h);
+ *  		g.lineTo(0, h);
+ *  		g.lineTo(0, 0);		
+ *  		fill.end(g);
+ *  	}
+ *  	</mx:Script>
+ *  </mx:Application>
+ *  
+ *

+ * + * @mxml + * + *

The <mx:LinearGradient> tag + * inherits all the tag attributes of its superclass, + * and adds the following tag attributes:

+ * + *
+ *  <mx:LinearGradient
+ *    Properties
+ *    angle="0"
+ *  />
+ *  
+ * + * @see mx.graphics.GradientEntry + * @see mx.graphics.RadialGradient + * @see mx.graphics.IFill + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class LinearGradient extends GradientBase implements IFill +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function LinearGradient() + { + super(); + } + + /** + * @private + */ + private static var commonMatrix:Matrix = new Matrix(); + + //-------------------------------------------------------------------------- + // + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // matrix + //---------------------------------- + + /** + * @private + */ + override public function set matrix(value:Matrix):void + { + scaleX = NaN; + super.matrix = value; + } + + //---------------------------------- + // scaleX + //---------------------------------- + + private var _scaleX:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The horizontal scale of the gradient transform, which defines the width of the (unrotated) gradient + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleX():Number + { + return compoundTransform ? compoundTransform.scaleX : _scaleX; + } + + /** + * @private + */ + public function set scaleX(value:Number):void + { + if (value != scaleX) + { + var oldValue:Number = scaleX; + + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.scaleX = value; + } + else + { + _scaleX = value; + } + dispatchGradientChangedEvent("scaleX", oldValue, value); + } + } + + //-------------------------------------------------------------------------- + // + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function begin(target:Graphics, targetBounds:Rectangle, targetOrigin:Point):void + { + commonMatrix.identity(); + + if (!compoundTransform) + { + var tx:Number = x; + var ty:Number = y; + var length:Number = scaleX; + + if (isNaN(length)) + { + // Figure out the two sides + if (rotation % 90 != 0) + { + // Normalize angles with absolute value > 360 + var normalizedAngle:Number = rotation % 360; + // Normalize negative angles + if (normalizedAngle < 0) + normalizedAngle += 360; + + // Angles wrap at 180 + normalizedAngle %= 180; + + // Angles > 90 get mirrored + if (normalizedAngle > 90) + normalizedAngle = 180 - normalizedAngle; + + var side:Number = targetBounds.width; + // Get the hypotenuse of the largest triangle that can fit in the bounds + var hypotenuse:Number = Math.sqrt(targetBounds.width * targetBounds.width + targetBounds.height * targetBounds.height); + // Get the angle of that largest triangle + var hypotenuseAngle:Number = Math.acos(targetBounds.width / hypotenuse) * 180 / Math.PI; + + // If the angle is larger than the hypotenuse angle, then use the height + // as the adjacent side of the triangle + if (normalizedAngle > hypotenuseAngle) + { + normalizedAngle = 90 - normalizedAngle; + side = targetBounds.height; + } + + // Solve for the hypotenuse given an adjacent side and an angle. + length = side / Math.cos(normalizedAngle / 180 * Math.PI); + } + else + { + // Use either width or height based on the rotation + length = (rotation % 180) == 0 ? targetBounds.width : targetBounds.height; + } + } + + // If only x or y is defined, force the other to be set to 0 + if (!isNaN(tx) && isNaN(ty)) + ty = 0; + else if (isNaN(tx) && !isNaN(ty)) + tx = 0; + + // If x and y are specified, then move the gradient so that the + // top left corner is at 0,0 + if (!isNaN(tx) && !isNaN(ty)) + commonMatrix.translate(GRADIENT_DIMENSION / 2, GRADIENT_DIMENSION / 2); // 1638.4 / 2 + + // Force the length to a absolute minimum of 2. Values of 0, 1, or -1 have undesired behavior + if (length >= 0 && length < 2) + length = 2; + else if (length < 0 && length > -2) + length = -2; + + // Scale the gradient in the x direction. The natural size is 1638.4px. No need + // to scale the y direction because it is infinite + commonMatrix.scale (length / GRADIENT_DIMENSION, 1 / GRADIENT_DIMENSION); + + commonMatrix.rotate (!isNaN(_angle) ? _angle : rotationInRadians); + if (isNaN(tx)) + tx = targetBounds.left + targetBounds.width / 2; + else + tx += targetOrigin.x; + if (isNaN(ty)) + ty = targetBounds.top + targetBounds.height / 2; + else + ty += targetOrigin.y; + commonMatrix.translate(tx, ty); + } + else + { + commonMatrix.translate(GRADIENT_DIMENSION / 2, GRADIENT_DIMENSION / 2); + commonMatrix.scale(1 / GRADIENT_DIMENSION, 1 / GRADIENT_DIMENSION); + commonMatrix.concat(compoundTransform.matrix); + commonMatrix.translate(targetOrigin.x, targetOrigin.y); + } + + target.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, + commonMatrix, spreadMethod, interpolationMethod); + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function end(target:Graphics):void + { + target.endFill(); + } +} + +} diff --git a/src/mx/graphics/LinearGradientStroke.as b/src/mx/graphics/LinearGradientStroke.as new file mode 100644 index 00000000..0361990f --- /dev/null +++ b/src/mx/graphics/LinearGradientStroke.as @@ -0,0 +1,346 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.display.GradientType; +import flash.display.Graphics; +import flash.display.GraphicsGradientFill; +import flash.display.GraphicsStroke; +import flash.geom.Matrix; +import flash.geom.Point; +import flash.geom.Rectangle; + +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * The LinearGradientStroke class lets you specify a gradient filled stroke. + * You use the LinearGradientStroke class, along with the GradientEntry class, + * to define a gradient stroke. + * + * @see mx.graphics.Stroke + * @see mx.graphics.GradientEntry + * @see mx.graphics.RadialGradient + * @see flash.display.Graphics + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class LinearGradientStroke extends GradientStroke +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param weight Specifies the line weight, in pixels. + * This parameter is optional, + * with a default value of 1. + * + * @param pixelHinting A Boolean value that specifies + * whether to hint strokes to full pixels. + * This affects both the position of anchors of a curve + * and the line stroke size itself. + * With pixelHinting set to true, + * Flash Player and AIR hint line widths to full pixel widths. + * With pixelHinting set to false, + * disjoints can appear for curves and straight lines. + * This parameter is optional, + * with a default value of false. + * + * @param scaleMode A value from the LineScaleMode class + * that specifies which scale mode to use. + * Valid values are LineScaleMode.HORIZONTAL, + * LineScaleMode.NONE, LineScaleMode.NORMAL, + * and LineScaleMode.VERTICAL. + * This parameter is optional, + * with a default value of LineScaleMode.NONE. + * + * @param caps A value from the CapsStyle class + * that specifies the type of caps at the end of lines. + * Valid values are CapsStyle.NONE, + * CapsStyle.ROUND, and CapsStyle.SQUARE. + * A null value is equivalent to + * CapsStyle.ROUND. + * This parameter is optional, + * with a default value of CapsStyle.ROUND. + * + * @param joints A value from the JointStyle class + * that specifies the type of joint appearance used at angles. + * Valid values are JointStyle.BEVEL, + * JointStyle.MITER, and JointStyle.ROUND. + * A null value is equivalent to + * JointStyle.ROUND. + * This parameter is optional, + * with a default value of JointStyle.ROUND. + * + * @param miterLimit A number that indicates the limit + * at which a miter is cut off. + * Valid values range from 1 to 255 + * (and values outside of that range are rounded to 1 or 255). + * This value is only used if the jointStyle property + * is set to miter. + * The miterLimit value represents the length that a miter + * can extend beyond the point at which the lines meet to form a joint. + * The value expresses a factor of the line thickness. + * For example, with a miterLimit factor of 2.5 and a + * thickness of 10 pixels, the miter is cut off at 25 pixels. + * This parameter is optional, + * with a default value of 3. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function LinearGradientStroke(weight:Number = 1, + pixelHinting:Boolean = false, + scaleMode:String = "normal", + caps:String = "round", + joints:String = "round", + miterLimit:Number = 3) + { + super(weight, pixelHinting, scaleMode, caps, joints, miterLimit); + } + + /** + * @private + */ + private static var commonMatrix:Matrix = new Matrix(); + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // matrix + //---------------------------------- + + /** + * @private + */ + override public function set matrix(value:Matrix):void + { + scaleX = NaN; + super.matrix = value; + } + + //---------------------------------- + // scaleX + //---------------------------------- + + private var _scaleX:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The horizontal scale of the gradient transform, which defines the width of the (unrotated) gradient + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleX():Number + { + return compoundTransform ? compoundTransform.scaleX : _scaleX; + } + + /** + * @private + */ + public function set scaleX(value:Number):void + { + if (value != scaleX) + { + var oldValue:Number = scaleX; + + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.scaleX = value; + } + else + { + _scaleX = value; + } + dispatchGradientChangedEvent("scaleX", oldValue, value); + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @inheritDoc + */ + override public function apply(graphics:Graphics, targetBounds:Rectangle, targetOrigin:Point):void + { + commonMatrix.identity(); + + graphics.lineStyle(weight, 0, 1, pixelHinting, scaleMode, + caps, joints, miterLimit); + + if (targetBounds) + calculateTransformationMatrix(targetBounds, commonMatrix, targetOrigin); + + graphics.lineGradientStyle(GradientType.LINEAR, colors, + alphas, ratios, + commonMatrix, spreadMethod, + interpolationMethod); + } + + /** + * @private + */ + override public function createGraphicsStroke(targetBounds:Rectangle, targetOrigin:Point):GraphicsStroke + { + // The parent class sets the gradient stroke properties common to + // LinearGradientStroke and RadialGradientStroke + var graphicsStroke:GraphicsStroke = super.createGraphicsStroke(targetBounds, targetOrigin); + + if (graphicsStroke) + { + // Set other properties specific to this LinearGradientStroke + GraphicsGradientFill(graphicsStroke.fill).type = GradientType.LINEAR; + calculateTransformationMatrix(targetBounds, commonMatrix, targetOrigin); + GraphicsGradientFill(graphicsStroke.fill).matrix = commonMatrix; + } + + return graphicsStroke; + } + + /** + * @private + * Calculates this LinearGradientStroke's transformation matrix. + */ + private function calculateTransformationMatrix(targetBounds:Rectangle, matrix:Matrix, targetOrigin:Point):void + { + matrix.identity(); + + if (!compoundTransform) + { + var tx:Number = x; + var ty:Number = y; + var length:Number = scaleX; + + if (isNaN(length)) + { + // Figure out the two sides + if (rotation % 90 != 0) + { + // Normalize angles with absolute value > 360 + var normalizedAngle:Number = rotation % 360; + // Normalize negative angles + if (normalizedAngle < 0) + normalizedAngle += 360; + + // Angles wrap at 180 + normalizedAngle %= 180; + + // Angles > 90 get mirrored + if (normalizedAngle > 90) + normalizedAngle = 180 - normalizedAngle; + + var side:Number = targetBounds.width; + // Get the hypotenuse of the largest triangle that can fit in the bounds + var hypotenuse:Number = Math.sqrt(targetBounds.width * targetBounds.width + targetBounds.height * targetBounds.height); + // Get the angle of that largest triangle + var hypotenuseAngle:Number = Math.acos(targetBounds.width / hypotenuse) * 180 / Math.PI; + + // If the angle is larger than the hypotenuse angle, then use the height + // as the adjacent side of the triangle + if (normalizedAngle > hypotenuseAngle) + { + normalizedAngle = 90 - normalizedAngle; + side = targetBounds.height; + } + + // Solve for the hypotenuse given an adjacent side and an angle. + length = side / Math.cos(normalizedAngle / 180 * Math.PI); + } + else + { + // Use either width or height based on the rotation + length = (rotation % 180) == 0 ? targetBounds.width : targetBounds.height; + } + } + + // If only x or y is defined, force the other to be set to 0 + if (!isNaN(tx) && isNaN(ty)) + ty = 0; + else if (isNaN(tx) && !isNaN(ty)) + tx = 0; + + // If x and y are specified, then move the gradient so that the + // top left corner is at 0,0 + if (!isNaN(tx) && !isNaN(ty)) + matrix.translate(GRADIENT_DIMENSION / 2, GRADIENT_DIMENSION / 2); // 1638.4 / 2 + + // Force the length to a absolute minimum of 2. Values of 0, 1, or -1 have undesired behavior + if (length >= 0 && length < 2) + length = 2; + else if (length < 0 && length > -2) + length = -2; + + // Scale the gradient in the x direction. The natural size is 1638.4px. No need + // to scale the y direction because it is infinite + matrix.scale (length / GRADIENT_DIMENSION, 1 / GRADIENT_DIMENSION); + + matrix.rotate (!isNaN(_angle) ? _angle : rotationInRadians); + if (isNaN(tx)) + tx = targetBounds.left + targetBounds.width / 2; + else + tx += targetOrigin.x; + if (isNaN(ty)) + ty = targetBounds.top + targetBounds.height / 2; + else + ty += targetOrigin.y; + matrix.translate(tx, ty); + } + else + { + matrix.translate(GRADIENT_DIMENSION / 2, GRADIENT_DIMENSION / 2); + matrix.scale(1 / GRADIENT_DIMENSION, 1 / GRADIENT_DIMENSION); + matrix.concat(compoundTransform.matrix); + matrix.translate(targetOrigin.x, targetOrigin.y); + } + } + +} + +} diff --git a/src/mx/graphics/RadialGradient.as b/src/mx/graphics/RadialGradient.as new file mode 100644 index 00000000..2c2840d1 --- /dev/null +++ b/src/mx/graphics/RadialGradient.as @@ -0,0 +1,385 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2004-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.display.GradientType; +import flash.display.Graphics; +import flash.geom.Matrix; +import flash.geom.Point; +import flash.geom.Rectangle; + +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * The RadialGradient class lets you specify a gradual color transition + * in the fill color. + * A radial gradient defines a fill pattern + * that radiates out from the center of a graphical element. + * You add a series of GradientEntry objects + * to the RadialGradient object's entries Array + * to define the colors that make up the gradient fill. + * + *

In MXML, you define a RadialGradient by adding a series + * of GradientEntry objects, as the following example shows: + *

+ *  <mx:fill>
+ *      <mx:RadialGradient>
+ *          <mx:entries>
+ *              <mx:GradientEntry color="0xC5C551" ratio="0.00" alpha="0.5"/>
+ *              <mx:GradientEntry color="0xFEFE24" ratio="0.33" alpha="0.5"/>
+ *              <mx:GradientEntry color="0xECEC21" ratio="0.66" alpha="0.5"/>
+ *          </mx:entries>
+ *      </mx:RadialGradient>
+ *  </mx:fill>
+ *  
+ *

+ * + *

You can also define a RadialGradient as a fill for any graphic element + * in ActionScript, as the following example shows: + *

+ *  
+ *  <?xml version="1.0"?>
+ *  <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
+ *      <mx:Script>
+ *      import flash.display.Graphics;
+ *      import flash.geom.Rectangle;
+ *      import mx.graphics.GradientEntry;
+ *      import mx.graphics.RadialGradient;
+ *  
+ *      private function init():void
+ *      {
+ *          var w:Number = 200;
+ *          var h:Number = 200;
+ *  
+ *          var s:Sprite = new Sprite();
+ *          // Add the new Sprite to the display list.
+ *          rawChildren.addChild(s);    
+ *  
+ *          var g:Graphics = s.graphics;
+ *          g.lineStyle(1, 0x33CCFF, 1.0);
+ *  
+ *          var fill:RadialGradient = new RadialGradient();
+ *          
+ *          var g1:GradientEntry = new GradientEntry(0xFFCC66, 0.00, 0.5);
+ *          var g2:GradientEntry = new GradientEntry(0x000000, 0.33, 0.5);
+ *          var g3:GradientEntry = new GradientEntry(0x99FF33, 0.66, 0.5);
+ *          
+ *          fill.entries = [ g1, g2, g3 ];
+ *  
+ *          // Set focal point to upper left corner.
+ *          fill.angle = 45;
+ *          fill.focalPointRatio = -0.8;
+ *  
+ *          // Draw a box and fill it with the RadialGradient.
+ *          g.moveTo(0, 0);
+ *          fill.begin(g,new Rectangle(0, 0, w, h));
+ *          g.lineTo(w, 0);
+ *          g.lineTo(w, h);
+ *          g.lineTo(0, h);
+ *          g.lineTo(0, 0);      
+ *          fill.end(g);
+ *      }
+ *      </mx:Script>
+ *  </mx:Application>
+ *  
+ *

+ * + * @mxml + * + *

The <mx:RadialGradient> tag + * inherits all the tag attributes of its superclass, + * and adds the following tag attributes:

+ * + *
+ *  <mx:RadialGradient
+ *    Properties
+ *    angle="0"
+ *    focalPointRatio="0"
+ *  />
+ *  
+ * + * @see mx.graphics.GradientEntry + * @see mx.graphics.LinearGradient + * @see mx.graphics.IFill + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class RadialGradient extends GradientBase implements IFill +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function RadialGradient() + { + super(); + } + + /** + * @private + */ + private static var commonMatrix:Matrix = new Matrix(); + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // focalPointRatio + //---------------------------------- + + /** + * @private + * Storage for the focalPointRatio property. + */ + private var _focalPointRatio:Number = 0.0; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * Sets the location of the start of the radial fill. + * + *

Valid values are from -1.0 to 1.0. + * A value of -1.0 sets the focal point + * (or, start of the gradient fill) + * on the left of the bounding Rectangle. + * A value of 1.0 sets the focal point + * on the right of the bounding Rectangle. + * + *

If you use this property in conjunction + * with the angle property, + * this value specifies the degree of distance + * from the center that the focal point occurs. + * For example, with an angle of 45 + * and focalPointRatio of 0.25, + * the focal point is slightly lower and to the right of center. + * If you set focalPointRatio to 0, + * the focal point is in the middle of the bounding Rectangle.

+ * If you set focalPointRatio to 1, + * the focal point is all the way to the bottom right corner + * of the bounding Rectangle.

+ * + * @default 0.0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get focalPointRatio():Number + { + return _focalPointRatio; + } + + /** + * @private + */ + public function set focalPointRatio(value:Number):void + { + var oldValue:Number = _focalPointRatio; + if (value != oldValue) + { + _focalPointRatio = value; + + dispatchGradientChangedEvent("focalPointRatio", oldValue, value); + } + } + + //---------------------------------- + // matrix + //---------------------------------- + + /** + * @private + */ + override public function set matrix(value:Matrix):void + { + scaleX = NaN; + scaleY = NaN; + super.matrix = value; + } + + //---------------------------------- + // scaleX + //---------------------------------- + + private var _scaleX:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The horizontal scale of the gradient transform, which defines the width of the (unrotated) gradient + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleX():Number + { + return compoundTransform ? compoundTransform.scaleX : _scaleX; + } + + /** + * @private + */ + public function set scaleX(value:Number):void + { + if (value != scaleX) + { + var oldValue:Number = scaleX; + + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.scaleX = value; + } + else + { + _scaleX = value; + } + dispatchGradientChangedEvent("scaleX", oldValue, value); + } + } + + //---------------------------------- + // scaleY + //---------------------------------- + + private var _scaleY:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The vertical scale of the gradient transform, which defines the height of the (unrotated) gradient + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleY():Number + { + return compoundTransform ? compoundTransform.scaleY : _scaleY; + } + + /** + * @private + */ + public function set scaleY(value:Number):void + { + if (value != scaleY) + { + var oldValue:Number = scaleY; + + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.scaleY = value; + } + else + { + _scaleY = value; + } + dispatchGradientChangedEvent("scaleY", oldValue, value); + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function begin(target:Graphics, targetBounds:Rectangle, targetOrigin:Point):void + { + var w:Number = !isNaN(scaleX) ? scaleX : targetBounds.width; + var h:Number = !isNaN(scaleY) ? scaleY : targetBounds.height; + var regX:Number = !isNaN(x) ? x + targetOrigin.x : targetBounds.left + targetBounds.width / 2; + var regY:Number = !isNaN(y) ? y + targetOrigin.y : targetBounds.top + targetBounds.height / 2; + + commonMatrix.identity(); + + if (!compoundTransform) + { + commonMatrix.scale (w / GRADIENT_DIMENSION, h / GRADIENT_DIMENSION); + commonMatrix.rotate(!isNaN(_angle) ? _angle : rotationInRadians); + commonMatrix.translate(regX, regY); + } + else + { + commonMatrix.scale(1 / GRADIENT_DIMENSION, 1 / GRADIENT_DIMENSION); + commonMatrix.concat(compoundTransform.matrix); + commonMatrix.translate(targetOrigin.x, targetOrigin.y); + } + + target.beginGradientFill(GradientType.RADIAL, colors, alphas, ratios, + commonMatrix, spreadMethod, interpolationMethod, focalPointRatio); + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function end(target:Graphics):void + { + target.endFill(); + } + +} + +} diff --git a/src/mx/graphics/RadialGradientStroke.as b/src/mx/graphics/RadialGradientStroke.as new file mode 100644 index 00000000..cf8f9cf9 --- /dev/null +++ b/src/mx/graphics/RadialGradientStroke.as @@ -0,0 +1,365 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.display.GradientType; +import flash.display.Graphics; +import flash.display.GraphicsGradientFill; +import flash.display.GraphicsStroke; +import flash.geom.Matrix; +import flash.geom.Point; +import flash.geom.Rectangle; + +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * The RadialGradientStroke class lets you specify a gradient filled stroke. + * You use the RadialGradientStroke class, along with the GradientEntry class, + * to define a gradient stroke. + * + * @see mx.graphics.Stroke + * @see mx.graphics.GradientEntry + * @see mx.graphics.RadialGradient + * @see flash.display.Graphics + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class RadialGradientStroke extends GradientStroke +{ + /** + * Constructor. + * + * @param weight Specifies the line weight, in pixels. + * This parameter is optional, + * with a default value of 1. + * + * @param pixelHinting A Boolean value that specifies + * whether to hint strokes to full pixels. + * This affects both the position of anchors of a curve + * and the line stroke size itself. + * With pixelHinting set to true, + * Flash Player and AIR hint line widths to full pixel widths. + * With pixelHinting set to false, + * disjoints can appear for curves and straight lines. + * This parameter is optional, + * with a default value of false. + * + * @param scaleMode A value from the LineScaleMode class + * that specifies which scale mode to use. + * Valid values are LineScaleMode.HORIZONTAL, + * LineScaleMode.NONE, LineScaleMode.NORMAL, + * and LineScaleMode.VERTICAL. + * This parameter is optional, + * with a default value of LineScaleMode.NORMAL. + * + * @param caps A value from the CapsStyle class + * that specifies the type of caps at the end of lines. + * Valid values are CapsStyle.NONE, + * CapsStyle.ROUND, and CapsStyle.SQUARE. + * A null value is equivalent to + * CapsStyle.ROUND. + * This parameter is optional, + * with a default value of CapsStyle.ROUND. + * + * @param joints A value from the JointStyle class + * that specifies the type of joint appearance used at angles. + * Valid values are JointStyle.BEVEL, + * JointStyle.MITER, and JointStyle.ROUND. + * A null value is equivalent to + * JointStyle.ROUND. + * This parameter is optional, + * with a default value of JointStyle.ROUND. + * + * @param miterLimit A number that indicates the limit + * at which a miter is cut off. + * Valid values range from 1 to 255 + * (and values outside of that range are rounded to 1 or 255). + * This value is only used if the jointStyle property + * is set to miter. + * The miterLimit value represents the length that a miter + * can extend beyond the point at which the lines meet to form a joint. + * The value expresses a factor of the line thickness. + * For example, with a miterLimit factor of 2.5 and a + * thickness of 10 pixels, the miter is cut off at 25 pixels. + * This parameter is optional, + * with a default value of 3. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function RadialGradientStroke(weight:Number = 1, + pixelHinting:Boolean = false, + scaleMode:String = "normal", + caps:String = "round", + joints:String = "round", + miterLimit:Number = 3) + { + super(weight, pixelHinting, scaleMode, caps, joints, miterLimit); + } + + /** + * @private + */ + private static var commonMatrix:Matrix = new Matrix(); + + //---------------------------------- + // focalPointRatio + //---------------------------------- + + /** + * @private + * Storage for the focalPointRatio property. + */ + private var _focalPointRatio:Number = 0.0; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * Sets the location of the start of the radial fill. + * + *

Valid values are from -1.0 to 1.0. + * A value of -1.0 sets the focal point + * (or, start of the gradient fill) + * on the left of the bounding Rectangle. + * A value of 1.0 sets the focal point + * on the right of the bounding Rectangle. + * + *

If you use this property in conjunction + * with the angle property, + * this value specifies the degree of distance + * from the center that the focal point occurs. + * For example, with an angle of 45 + * and focalPointRatio of 0.25, + * the focal point is slightly lower and to the right of center. + * If you set focalPointRatio to 0, + * the focal point is in the middle of the bounding Rectangle.

+ * If you set focalPointRatio to 1, + * the focal point is all the way to the bottom right corner + * of the bounding Rectangle.

+ * + * @default 0.0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get focalPointRatio():Number + { + return _focalPointRatio; + } + + /** + * @private + */ + public function set focalPointRatio(value:Number):void + { + var oldValue:Number = _focalPointRatio; + if (value != oldValue) + { + _focalPointRatio = value; + + dispatchGradientChangedEvent("focalPointRatio", + oldValue, value); + } + } + + //---------------------------------- + + // matrix + //---------------------------------- + + /** + * @private + */ + override public function set matrix(value:Matrix):void + { + scaleX = NaN; + scaleY = NaN; + super.matrix = value; + } + + //---------------------------------- + // scaleX + //---------------------------------- + + private var _scaleX:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The horizontal scale of the gradient transform, which defines the width of the (unrotated) gradient + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleX():Number + { + return compoundTransform ? compoundTransform.scaleX : _scaleX; + } + + /** + * @private + */ + public function set scaleX(value:Number):void + { + if (value != scaleX) + { + var oldValue:Number = scaleX; + + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.scaleX = value; + } + else + { + _scaleX = value; + } + dispatchGradientChangedEvent("scaleX", oldValue, value); + } + } + + //---------------------------------- + // scaleY + //---------------------------------- + + private var _scaleY:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The vertical scale of the gradient transform, which defines the height of the (unrotated) gradient + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleY():Number + { + return compoundTransform ? compoundTransform.scaleY : _scaleY; + } + + /** + * @private + */ + public function set scaleY(value:Number):void + { + if (value != scaleY) + { + var oldValue:Number = scaleY; + + if (compoundTransform) + { + // If we have a compoundTransform, only non-NaN values are allowed + if (!isNaN(value)) + compoundTransform.scaleY = value; + } + else + { + _scaleY = value; + } + dispatchGradientChangedEvent("scaleY", oldValue, value); + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function apply(graphics:Graphics, targetBounds:Rectangle, targetOrigin:Point):void + { + commonMatrix.identity(); + + graphics.lineStyle(weight, 0, 1, pixelHinting, scaleMode, + caps, joints, miterLimit); + + if (targetBounds) + calculateTransformationMatrix(targetBounds, commonMatrix, targetOrigin); + + graphics.lineGradientStyle(GradientType.RADIAL, colors, + alphas, ratios, commonMatrix, + spreadMethod, interpolationMethod, + focalPointRatio); + } + + /** + * @private + */ + override public function createGraphicsStroke(targetBounds:Rectangle, targetOrigin:Point):GraphicsStroke + { + // The parent class sets the gradient stroke properties common to + // LinearGradientStroke and RadialGradientStroke + var graphicsStroke:GraphicsStroke = super.createGraphicsStroke(targetBounds, targetOrigin); + + if (graphicsStroke) + { + // Set other properties specific to this RadialGradientStroke + GraphicsGradientFill(graphicsStroke.fill).type = GradientType.RADIAL; + calculateTransformationMatrix(targetBounds, commonMatrix, targetOrigin); + GraphicsGradientFill(graphicsStroke.fill).matrix = commonMatrix; + GraphicsGradientFill(graphicsStroke.fill).focalPointRatio = focalPointRatio; + + } + + return graphicsStroke; + } + + /** + * @private + * Calculates this RadialGradientStroke's transformation matrix + */ + private function calculateTransformationMatrix(targetBounds:Rectangle, matrix:Matrix, targetOrigin:Point):void + { + matrix.identity(); + + if (!compoundTransform) + { + var w:Number = !isNaN(scaleX) ? scaleX : targetBounds.width; + var h:Number = !isNaN(scaleY) ? scaleY : targetBounds.height; + var regX:Number = !isNaN(x) ? x + targetOrigin.x : targetBounds.left + targetBounds.width / 2; + var regY:Number = !isNaN(y) ? y + targetOrigin.y : targetBounds.top + targetBounds.height / 2; + + matrix.scale (w / GRADIENT_DIMENSION, h / GRADIENT_DIMENSION); + matrix.rotate(!isNaN(_angle) ? _angle : rotationInRadians); + matrix.translate(regX, regY); + } + else + { + matrix.scale(1 / GRADIENT_DIMENSION, 1 / GRADIENT_DIMENSION); + matrix.concat(compoundTransform.matrix); + matrix.translate(targetOrigin.x, targetOrigin.y); + } + } + +} +} diff --git a/src/mx/graphics/SolidColor.as b/src/mx/graphics/SolidColor.as new file mode 100644 index 00000000..1db5fb27 --- /dev/null +++ b/src/mx/graphics/SolidColor.as @@ -0,0 +1,194 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2004-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.display.Graphics; +import flash.events.EventDispatcher; +import flash.geom.Point; +import flash.geom.Rectangle; + +import mx.events.PropertyChangeEvent; + +[DefaultProperty("color")] + +/** + * Defines a representation for a color, + * including a color and an alpha value. + * + * @see mx.graphics.IFill + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class SolidColor extends EventDispatcher implements IFill +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param color Specifies the color. + * The default value is 0x000000 (black). + * + * @param alpha Specifies the level of transparency. + * Valid values range from 0.0 (completely transparent) + * to 1.0 (completely opaque). + * The default value is 1.0. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function SolidColor(color:uint = 0x000000, alpha:Number = 1.0) + { + super(); + + this.color = color; + this.alpha = alpha; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // alpha + //---------------------------------- + + private var _alpha:Number = 1.0; + + [Bindable("propertyChange")] + [Inspectable(category="General", minValue="0.0", maxValue="1.0")] + + /** + * The transparency of a color. + * Possible values are 0.0 (invisible) through 1.0 (opaque). + * + * @default 1.0 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get alpha():Number + { + return _alpha; + } + + public function set alpha(value:Number):void + { + var oldValue:Number = _alpha; + if (value != oldValue) + { + _alpha = value; + dispatchFillChangedEvent("alpha", oldValue, value); + } + } + + //---------------------------------- + // color + //---------------------------------- + + private var _color:uint = 0x000000; + + [Bindable("propertyChange")] + [Inspectable(category="General", format="Color")] + + /** + * A color value. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get color():uint + { + return _color; + } + + public function set color(value:uint):void + { + var oldValue:uint = _color; + if (value != oldValue) + { + _color = value; + dispatchFillChangedEvent("color", oldValue, value); + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function begin(target:Graphics, targetBounds:Rectangle, targetOrigin:Point):void + { + target.beginFill(color, alpha); + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function end(target:Graphics):void + { + target.endFill(); + } + + /** + * @private + */ + private function dispatchFillChangedEvent(prop:String, oldValue:*, + value:*):void + { + if (hasEventListener("propertyChange")) + dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, prop, + oldValue, value)); + } +} + +} diff --git a/src/mx/graphics/SolidColorStroke.as b/src/mx/graphics/SolidColorStroke.as new file mode 100644 index 00000000..c4a4116c --- /dev/null +++ b/src/mx/graphics/SolidColorStroke.as @@ -0,0 +1,537 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2004-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics +{ + +import flash.display.CapsStyle; +import flash.display.Graphics; +import flash.display.GraphicsSolidFill; +import flash.display.GraphicsStroke; +import flash.display.JointStyle; +import flash.events.EventDispatcher; +import flash.geom.Point; +import flash.geom.Rectangle; + +import mx.events.PropertyChangeEvent; + +/** + * The SolidColorStroke class defines the properties for a line. + * + * You can define a SolidColorStroke object in MXML, but you must attach that SolidColorStroke to + * another object for it to appear in your application. The following example + * defines two SolidColorStroke objects and then uses them in the horizontalAxisRenderer + * of a LineChart control: + * + *
+ *  ...
+ *  <mx:SolidColorStroke id="ticks" color="0xFF0000" weight="1"/>
+ *  <mx:SolidColorStroke id="mticks" color="0x0000FF" weight="1"/>
+ *  
+ *  <mx:LineChart id="mychart" dataProvider="{ndxa}">
+ *  	<mx:horizontalAxisRenderer>
+ *  		<mx:AxisRenderer placement="bottom" canDropLabels="true">
+ *  			<mx:tickStroke>{ticks}</mx:tickStroke>
+ *  			<mx:minorTickStroke>{mticks}</mx:minorTickStroke>
+ *  		</mx:AxisRenderer>
+ *  	</mx:horizontalAxisRenderer>
+ *  </LineChart>
+ *  ...
+ *  
+ * + * @mxml + * + *

The <mx:SolidColorStroke> tag inherits all the tag attributes + * of its superclass, and adds the following tag attributes:

+ * + *
+ *  <mx:SolidColorStroke
+ *    Properties
+ *    alpha="1.0"
+ *    caps="round|none|square"
+ *    color="0x000000"
+ *    joints="round|bevel|miter"
+ *    miterLimit="3"
+ *    pixelHinting="false|true"
+ *    scaleMode="normal|none|horizontal|vertical"
+ *    weight="1 (in most cases)"
+ *  />
+ *  
+ * + * @see flash.display.Graphics + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class SolidColorStroke extends EventDispatcher implements IStroke +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param color Specifies the line color. + * The default value is 0x000000 (black). + * + * @param weight Specifies the line weight, in pixels. + * The default value is 1. + * + * @param alpha Specifies the alpha value in the range 0.0 to 1.0. + * The default value is 1.0 (opaque). + * + * @param pixelHinting Specifies whether to hint strokes to full pixels. + * This value affects both the position of anchors of a curve + * and the line stroke size itself. + * The default value is false. + * + * @param scaleMode A value from the LineScaleMode class + * that specifies which scale mode to use. + * Valid values are LineScaleMode.HORIZONTAL, + * LineScaleMode.NONE, LineScaleMode.NORMAL, + * and LineScaleMode.VERTICAL. + * This parameter is optional, + * with a default value of LineScaleMode.NORMAL. + * + * @param caps Specifies the type of caps at the end of lines. + * Valid values are CapsStyle.ROUND, CapsStyle.SQUARE, + * and CapsStyle.NONE. + * The default value is CapsStyle.ROUND. + * + * @param joints Specifies the type of joint appearance used at angles. + * Valid values are JointStyle.ROUND, JointStyle.MITER, + * and JointStyle.BEVEL. + * The default value is JointStyle.ROUND. + * + * @param miterLimit Indicates the limit at which a miter is cut off. + * Valid values range from 0 to 255. + * The default value is 3. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function SolidColorStroke(color:uint = 0x000000, + weight:Number = 1, + alpha:Number = 1.0, + pixelHinting:Boolean = false, + scaleMode:String = "normal", + caps:String = "round", + joints:String = "round", + miterLimit:Number = 3) + { + super(); + + this.color = color; + this._weight = weight; + this.alpha = alpha; + this.pixelHinting = pixelHinting; + this.scaleMode = scaleMode; + this.caps = caps; + this.joints = joints; + this.miterLimit = miterLimit; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // alpha + //---------------------------------- + + private var _alpha:Number = 0.0; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * The transparency of a line. + * Possible values are 0.0 (invisible) through 1.0 (opaque). + * + * @default 1.0. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get alpha():Number + { + return _alpha; + } + + /** + * @private + */ + public function set alpha(value:Number):void + { + var oldValue:Number = _alpha; + if (value != oldValue) + { + _alpha = value; + dispatchStrokeChangedEvent("alpha", oldValue, value); + } + } + + //---------------------------------- + // caps + //---------------------------------- + + private var _caps:String = CapsStyle.ROUND; + + [Bindable("propertyChange")] + [Inspectable(category="General", enumeration="round,square,none", defaultValue="round")] + + /** + * Specifies the type of caps at the end of lines. + * Valid values are: CapsStyle.ROUND, CapsStyle.SQUARE, + * and CapsStyle.NONE. + * + * @default CapsStyle.ROUND + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get caps():String + { + return _caps; + } + + public function set caps(value:String):void + { + var oldValue:String = _caps; + if (value != oldValue) + { + _caps = value; + dispatchStrokeChangedEvent("caps", oldValue, value); + } + } + + //---------------------------------- + // color + //---------------------------------- + + private var _color:uint = 0x000000; + + [Bindable("propertyChange")] + [Inspectable(category="General", format="Color")] + + /** + * The line color. + * + * @default 0x000000 (black). + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get color():uint + { + return _color; + } + + public function set color(value:uint):void + { + var oldValue:uint = _color; + if (value != oldValue) + { + _color = value; + dispatchStrokeChangedEvent("color", oldValue, value); + } + } + + //---------------------------------- + // joints + //---------------------------------- + + private var _joints:String = JointStyle.ROUND; + + [Bindable("propertyChange")] + [Inspectable(category="General", enumeration="round,bevel,miter", defaultValue="round")] + + /** + * Specifies the type of joint appearance used at angles. + * Valid values are JointStyle.ROUND, JointStyle.MITER, + * and JointStyle.BEVEL. + * + * @default JointStyle.ROUND + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get joints():String + { + return _joints; + } + + public function set joints(value:String):void + { + var oldValue:String = _joints; + if (value != oldValue) + { + _joints = value; + dispatchStrokeChangedEvent("joints", oldValue, value); + } + } + + //---------------------------------- + // miterLimit + //---------------------------------- + + private var _miterLimit:Number = 3; + + [Bindable("propertyChange")] + [Inspectable(category="General", minValue="0.0", maxValue="255.0")] + + /** + * Indicates the limit at which a miter is cut off. + * Valid values range from 0 to 255. + * + * @default 3 + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get miterLimit():Number + { + return _miterLimit; + } + + public function set miterLimit(value:Number):void + { + var oldValue:Number = _miterLimit; + if (value != oldValue) + { + _miterLimit = value; + dispatchStrokeChangedEvent("miterLimit", oldValue, value); + } + } + + //---------------------------------- + // pixelHinting + //---------------------------------- + + private var _pixelHinting:Boolean = false; + + [Bindable("propertyChange")] + [Inspectable(category="General")] + + /** + * Specifies whether to hint strokes to full pixels. + * This value affects both the position of anchors of a curve + * and the line stroke size itself. + * + * @default false + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get pixelHinting():Boolean + { + return _pixelHinting; + } + + public function set pixelHinting(value:Boolean):void + { + var oldValue:Boolean = _pixelHinting; + if (value != oldValue) + { + _pixelHinting = value; + dispatchStrokeChangedEvent("pixelHinting", oldValue, value); + } + } + + //---------------------------------- + // scaleMode + //---------------------------------- + + private var _scaleMode:String = "normal"; + + [Bindable("propertyChange")] + [Inspectable(category="General", enumeration="normal,vertical,horizontal,none", defaultValue="normal")] + + /** + * A value from the LineScaleMode class + * that specifies which scale mode to use. + * Value valids are: + * + *
    + *
  • + * LineScaleMode.NORMAL— + * Always scale the line thickness when the object is scaled (the default). + *
  • + *
  • + * LineScaleMode.NONE— + * Never scale the line thickness. + *
  • + *
  • + * LineScaleMode.VERTICAL— + * Do not scale the line thickness if the object is scaled vertically + * only. + *
  • + *
  • + * LineScaleMode.HORIZONTAL— + * Do not scale the line thickness if the object is scaled horizontally + * only. + *
  • + *
+ * + * @default LineScaleMode.NORMAL + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get scaleMode():String + { + return _scaleMode; + } + + public function set scaleMode(value:String):void + { + var oldValue:String = _scaleMode; + if (value != oldValue) + { + _scaleMode = value; + dispatchStrokeChangedEvent("scaleMode", oldValue, value); + } + } + + //---------------------------------- + // weight + //---------------------------------- + + /** + * @private + * Storage for the weight property. + */ + private var _weight:Number; + + [Bindable("propertyChange")] + [Inspectable(category="General", minValue="0.0")] + + /** + * The line weight, in pixels. + * For many charts, the default value is 1 pixel. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get weight():Number + { + return _weight; + } + + /** + * @private + */ + public function set weight(value:Number):void + { + var oldValue:Number = _weight; + if (value != oldValue) + { + _weight = value; + dispatchStrokeChangedEvent("weight", oldValue, value); + } + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function apply(graphics:Graphics, targetBounds:Rectangle, targetOrigin:Point):void + { + graphics.lineStyle(weight, color, alpha, pixelHinting, + scaleMode, caps, joints, miterLimit); + } + + /** + * @inheritDoc + */ + public function createGraphicsStroke(targetBounds:Rectangle, targetOrigin:Point):GraphicsStroke + { + // Construct a new GraphicsStroke object and set all of + // its properties to match the SolidColorStroke's + // properties + var graphicsStroke:GraphicsStroke = new GraphicsStroke(); + graphicsStroke.thickness = weight; + graphicsStroke.miterLimit = miterLimit; + graphicsStroke.pixelHinting = pixelHinting; + graphicsStroke.scaleMode = scaleMode; + + // There is a bug in Drawing API-2 where if no caps is + // specified, a value of 'none' is used instead of 'round' + graphicsStroke.caps = (!caps) ? CapsStyle.ROUND : caps; + + // Give the GraphicsStroke a GraphicsSolidFill corresponding to the + // SolidColorStroke's color and alpha values + var graphicsSolidFill:GraphicsSolidFill = new GraphicsSolidFill(); + graphicsSolidFill.color = color; + graphicsSolidFill.alpha = alpha; + graphicsStroke.fill = graphicsSolidFill; + + return graphicsStroke; + } + + /** + * @private + */ + private function dispatchStrokeChangedEvent(prop:String, oldValue:*, + value:*):void + { + if (hasEventListener("propertyChange")) + dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, prop, + oldValue, value)); + } +} + +} diff --git a/src/mx/graphics/codec/IImageEncoder.as b/src/mx/graphics/codec/IImageEncoder.as new file mode 100644 index 00000000..35eecd2e --- /dev/null +++ b/src/mx/graphics/codec/IImageEncoder.as @@ -0,0 +1,106 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics.codec +{ + +import flash.display.BitmapData; +import flash.utils.ByteArray; + +/** + * The IImageEncoder interface defines the interface + * that image encoders implement to take BitmapData objects, + * or ByteArrays containing raw ARGB pixels, as input + * and convert them to popular image formats such as PNG or JPEG. + * + * @see PNGEncoder + * @see JPEGEncoder + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IImageEncoder +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // contentType + //---------------------------------- + + /** + * The MIME type for the image format that this encoder produces. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get contentType():String; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Encodes a BitmapData object as a ByteArray. + * + * @param bitmapData The input BitmapData object. + * + * @return Returns a ByteArray object containing encoded image data. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function encode(bitmapData:BitmapData):ByteArray; + + /** + * Encodes a ByteArray object containing raw pixels + * in 32-bit ARGB (Alpha, Red, Green, Blue) format + * as a new ByteArray object containing encoded image data. + * The original ByteArray is left unchanged. + * + * @param byteArray The input ByteArray object containing raw pixels. + * This ByteArray should contain + * 4 * width * height bytes. + * Each pixel is represented by 4 bytes, in the order ARGB. + * The first four bytes represent the top-left pixel of the image. + * The next four bytes represent the pixel to its right, etc. + * Each row follows the previous one without any padding. + * + * @param width The width of the input image, in pixels. + * + * @param height The height of the input image, in pixels. + * + * @param transparent If false, + * alpha channel information is ignored. + * + * @return Returns a ByteArray object containing encoded image data. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function encodeByteArray(byteArray:ByteArray, width:int, height:int, + transparent:Boolean = true):ByteArray; +} + +} diff --git a/src/mx/graphics/codec/JPEGEncoder.as b/src/mx/graphics/codec/JPEGEncoder.as new file mode 100644 index 00000000..9eb64f3c --- /dev/null +++ b/src/mx/graphics/codec/JPEGEncoder.as @@ -0,0 +1,1070 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics.codec +{ + +import flash.display.BitmapData; +import flash.utils.ByteArray; + +/** + * The JPEGEncoder class converts raw bitmap images into encoded + * images using Joint Photographic Experts Group (JPEG) compression. + * + * For information about the JPEG algorithm, see the document + * http://www.opennet.ru/docs/formats/jpeg.txt by Cristi Cuturicu. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class JPEGEncoder implements IImageEncoder +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private static const CONTENT_TYPE:String = "image/jpeg"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param quality A value between 0.0 and 100.0. + * The smaller the quality value, + * the smaller the file size of the resultant image. + * The value does not affect the encoding speed. + *. Note that even though this value is a number between 0.0 and 100.0, + * it does not represent a percentage. + * The default value is 50.0. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function JPEGEncoder(quality:Number = 50.0) + { + super(); + + if (quality <= 0.0) + quality = 1.0; + + if (quality > 100.0) + quality = 100.0; + + var sf:int = 0; + if (quality < 50.0) + sf = int(5000 / quality); + else + sf = int(200 - quality * 2); + + // Create tables + initHuffmanTbl(); + initCategoryNumber(); + initQuantTables(sf); + } + + //-------------------------------------------------------------------------- + // + // Constants + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private const std_dc_luminance_nrcodes:Array = + [ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 ]; + + /** + * @private + */ + private const std_dc_luminance_values:Array = + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; + + /** + * @private + */ + private const std_dc_chrominance_nrcodes:Array = + [ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ]; + + /** + * @private + */ + private const std_dc_chrominance_values:Array = + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; + + /** + * @private + */ + private const std_ac_luminance_nrcodes:Array = + [ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7D ]; + + /** + * @private + */ + private const std_ac_luminance_values:Array = + [ + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, + 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, + 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, + 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, + 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, + 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, + 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA + ]; + + /** + * @private + */ + private const std_ac_chrominance_nrcodes:Array = + [ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 ]; + + /** + * @private + */ + private const std_ac_chrominance_values:Array = + [ + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, + 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, + 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, + 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, + 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, + 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, + 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, + 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, + 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA + ]; + + /** + * @private + */ + private const ZigZag:Array = + [ + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 + ]; + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Initialized by initHuffmanTbl() in constructor. + */ + private var YDC_HT:Array; + + /** + * @private + * Initialized by initHuffmanTbl() in constructor. + */ + private var UVDC_HT:Array; + + /** + * @private + * Initialized by initHuffmanTbl() in constructor. + */ + private var YAC_HT:Array; + + /** + * @private + * Initialized by initHuffmanTbl() in constructor. + */ + private var UVAC_HT:Array; + + /** + * @private + * Initialized by initCategoryNumber() in constructor. + */ + private var category:Array = new Array(65535); + + /** + * @private + * Initialized by initCategoryNumber() in constructor. + */ + private var bitcode:Array = new Array(65535); + + /** + * @private + * Initialized by initQuantTables() in constructor. + */ + private var YTable:Array = new Array(64); + + /** + * @private + * Initialized by initQuantTables() in constructor. + */ + private var UVTable:Array = new Array(64); + + /** + * @private + * Initialized by initQuantTables() in constructor. + */ + private var fdtbl_Y:Array = new Array(64); + + /** + * @private + * Initialized by initQuantTables() in constructor. + */ + private var fdtbl_UV:Array = new Array(64); + + /** + * @private + * The output ByteArray containing the encoded image data. + */ + private var byteout:ByteArray; + + /** + * @private + */ + private var bytenew:int = 0; + + /** + * @private + */ + private var bytepos:int = 7; + + /** + * @private + */ + private var DU:Array = new Array(64); + + /** + * @private + */ + private var YDU:Array = new Array(64); + + /** + * @private + */ + private var UDU:Array = new Array(64); + + /** + * @private + */ + private var VDU:Array = new Array(64); + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // contentType + //---------------------------------- + + /** + * The MIME type for the JPEG encoded image. + * The value is "image/jpeg". + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get contentType():String + { + return CONTENT_TYPE; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Converts the pixels of BitmapData object + * to a JPEG-encoded ByteArray object. + * + * @param bitmapData The input BitmapData object. + * + * @return Returns a ByteArray object containing JPEG-encoded image data. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function encode(bitmapData:BitmapData):ByteArray + { + return internalEncode(bitmapData, bitmapData.width, bitmapData.height, + bitmapData.transparent); + } + + /** + * Converts a ByteArray object containing raw pixels + * in 32-bit ARGB (Alpha, Red, Green, Blue) format + * to a new JPEG-encoded ByteArray object. + * The original ByteArray is left unchanged. + * Transparency is not supported; however you still must represent + * each pixel as four bytes in ARGB format. + * + * @param byteArray The input ByteArray object containing raw pixels. + * This ByteArray should contain + * 4 * width * height bytes. + * Each pixel is represented by 4 bytes, in the order ARGB. + * The first four bytes represent the top-left pixel of the image. + * The next four bytes represent the pixel to its right, etc. + * Each row follows the previous one without any padding. + * + * @param width The width of the input image, in pixels. + * + * @param height The height of the input image, in pixels. + * + * @param transparent If false, + * alpha channel information is ignored. + * + * @return Returns a ByteArray object containing JPEG-encoded image data. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function encodeByteArray(byteArray:ByteArray, width:int, height:int, + transparent:Boolean = true):ByteArray + { + return internalEncode(byteArray, width, height, transparent); + } + + //-------------------------------------------------------------------------- + // + // Methods: Initialization + // + //-------------------------------------------------------------------------- + + /** + * @private + * Initializes the Huffman tables YDC_HT, UVDC_HT, YAC_HT, and UVAC_HT. + */ + private function initHuffmanTbl():void + { + YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes, + std_dc_luminance_values); + + UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes, + std_dc_chrominance_values); + + YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes, + std_ac_luminance_values); + + UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes, + std_ac_chrominance_values); + } + + /** + * @private + */ + private function computeHuffmanTbl(nrcodes:Array, std_table:Array):Array + { + var codevalue:int = 0; + var pos_in_table:int = 0; + + var HT:Array = []; + + for (var k:int = 1; k <= 16; k++) + { + for (var j:int = 1; j <= nrcodes[k]; j++) + { + HT[std_table[pos_in_table]] = new BitString(); + HT[std_table[pos_in_table]].val = codevalue; + HT[std_table[pos_in_table]].len = k; + + pos_in_table++; + codevalue++; + } + + codevalue *= 2; + } + + return HT; + } + + /** + * @private + * Initializes the category and bitcode arrays. + */ + private function initCategoryNumber():void + { + var nr:int; + + var nrlower:int = 1; + var nrupper:int = 2; + + for (var cat:int = 1; cat <= 15; cat++) + { + // Positive numbers + for (nr = nrlower; nr < nrupper; nr++) + { + category[32767 + nr] = cat; + + bitcode[32767 + nr] = new BitString(); + bitcode[32767 + nr].len = cat; + bitcode[32767 + nr].val = nr; + } + + // Negative numbers + for (nr = -(nrupper - 1); nr <= -nrlower; nr++) + { + category[32767 + nr] = cat; + + bitcode[32767 + nr] = new BitString(); + bitcode[32767 + nr].len = cat; + bitcode[32767 + nr].val = nrupper - 1 + nr; + } + + nrlower <<= 1; + nrupper <<= 1; + } + } + + /** + * @private + * Initializes YTable, UVTable, fdtbl_Y, and fdtbl_UV. + */ + private function initQuantTables(sf:int):void + { + var i:int = 0; + var t:Number; + + var YQT:Array = + [ + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 + ]; + + for (i = 0; i < 64; i++) + { + t = Math.floor((YQT[i] * sf + 50)/100); + if (t < 1) + t = 1; + else if (t > 255) + t = 255; + YTable[ZigZag[i]] = t; + } + + var UVQT:Array = + [ + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + ]; + + for (i = 0; i < 64; i++) + { + t = Math.floor((UVQT[i] * sf + 50) / 100); + if (t < 1) + t = 1; + else if (t > 255) + t = 255; + UVTable[ZigZag[i]] = t; + } + + var aasf:Array = + [ + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + ]; + + i = 0; + for (var row:int = 0; row < 8; row++) + { + for (var col:int = 0; col < 8; col++) + { + fdtbl_Y[i] = + (1.0 / (YTable [ZigZag[i]] * aasf[row] * aasf[col] * 8.0)); + + fdtbl_UV[i] = + (1.0 / (UVTable[ZigZag[i]] * aasf[row] * aasf[col] * 8.0)); + + i++; + } + } + } + + //-------------------------------------------------------------------------- + // + // Methods: Core processing + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function internalEncode(source:Object, width:int, height:int, + transparent:Boolean = true):ByteArray + { + // The source is either a BitmapData or a ByteArray. + var sourceBitmapData:BitmapData = source as BitmapData; + var sourceByteArray:ByteArray = source as ByteArray; + + // Initialize bit writer + byteout = new ByteArray(); + bytenew = 0; + bytepos = 7; + + // Add JPEG headers + writeWord(0xFFD8); // SOI + writeAPP0(); + writeDQT(); + writeSOF0(width, height); + writeDHT(); + writeSOS(); + + // Encode 8x8 macroblocks + var DCY:Number = 0; + var DCU:Number = 0; + var DCV:Number = 0; + bytenew = 0; + bytepos = 7; + + for (var ypos:int = 0; ypos < height; ypos += 8) + { + for (var xpos:int = 0; xpos < width; xpos += 8) + { + RGB2YUV(sourceBitmapData, sourceByteArray, xpos, ypos, width, height); + + DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + + // Do the bit alignment of the EOI marker + if (bytepos >= 0) + { + var fillbits:BitString = new BitString(); + fillbits.len = bytepos + 1; + fillbits.val = (1 << (bytepos + 1)) - 1; + writeBits(fillbits); + } + + // Add EOI + writeWord(0xFFD9); + + return byteout; + } + + /** + * @private + */ + private function RGB2YUV(sourceBitmapData:BitmapData, + sourceByteArray:ByteArray, + xpos:int, ypos:int, + width:int, height:int):void + { + var k:int = 0; // index into 64-element block arrays + + for (var j:int = 0; j < 8; j++) + { + var y:int = ypos + j; + if (y >= height) + y = height - 1; + + for (var i:int = 0; i < 8; i++) + { + var x:int = xpos + i; + if (x >= width) + x = width - 1; + + var pixel:uint; + if (sourceBitmapData) + { + pixel = sourceBitmapData.getPixel32(x, y); + } + else + { + sourceByteArray.position = 4 * (y * width + x); + pixel = sourceByteArray.readUnsignedInt(); + } + + var r:Number = Number((pixel >> 16) & 0xFF); + var g:Number = Number((pixel >> 8) & 0xFF); + var b:Number = Number(pixel & 0xFF); + + YDU[k] = 0.29900 * r + 0.58700 * g + 0.11400 * b - 128.0; + UDU[k] = -0.16874 * r - 0.33126 * g + 0.50000 * b; + VDU[k] = 0.50000 * r - 0.41869 * g - 0.08131 * b; + + k++; + } + } + } + + /** + * @private + */ + private function processDU(CDU:Array, fdtbl:Array, DC:Number, + HTDC:Array, HTAC:Array):Number + { + var EOB:BitString = HTAC[0x00]; + var M16zeroes:BitString = HTAC[0xF0]; + var i:int; + + var DU_DCT:Array = fDCTQuant(CDU, fdtbl); + + // ZigZag reorder + for (i = 0; i < 64; i++) + { + DU[ZigZag[i]] = DU_DCT[i]; + } + + var Diff:int = DU[0] - DC; + DC = DU[0]; + + // Encode DC + if (Diff == 0) + { + writeBits(HTDC[0]); // Diff might be 0 + } + else + { + writeBits(HTDC[category[32767 + Diff]]); + writeBits(bitcode[32767 + Diff]); + } + + // Encode ACs + var end0pos:int = 63; + for (; (end0pos > 0) && (DU[end0pos] == 0); end0pos--) + { + }; + + // end0pos = first element in reverse order != 0 + if (end0pos == 0) + { + writeBits(EOB); + return DC; + } + + i = 1; + while (i <= end0pos) + { + var startpos:int = i; + for (; (DU[i] == 0) && (i <= end0pos); i++) + { + } + var nrzeroes:int = i - startpos; + + if (nrzeroes >= 16) + { + for (var nrmarker:int = 1; nrmarker <= nrzeroes / 16; nrmarker++) + { + writeBits(M16zeroes); + } + nrzeroes = int(nrzeroes & 0xF); + } + + writeBits(HTAC[nrzeroes * 16 + category[32767 + DU[i]]]); + writeBits(bitcode[32767 + DU[i]]); + + i++; + } + + if (end0pos != 63) + writeBits(EOB); + + return DC; + } + + /** + * @private + */ + private function fDCTQuant(data:Array, fdtbl:Array):Array + { + // Pass 1: process rows. + var dataOff:int = 0; + var i:int; + for (i = 0; i < 8; i++) + { + var tmp0:Number = data[dataOff + 0] + data[dataOff + 7]; + var tmp7:Number = data[dataOff + 0] - data[dataOff + 7]; + var tmp1:Number = data[dataOff + 1] + data[dataOff + 6]; + var tmp6:Number = data[dataOff + 1] - data[dataOff + 6]; + var tmp2:Number = data[dataOff + 2] + data[dataOff + 5]; + var tmp5:Number = data[dataOff + 2] - data[dataOff + 5]; + var tmp3:Number = data[dataOff + 3] + data[dataOff + 4]; + var tmp4:Number = data[dataOff + 3] - data[dataOff + 4]; + + // Even part + var tmp10:Number = tmp0 + tmp3; // phase 2 + var tmp13:Number = tmp0 - tmp3; + var tmp11:Number = tmp1 + tmp2; + var tmp12:Number = tmp1 - tmp2; + + data[dataOff + 0] = tmp10 + tmp11; // phase 3 + data[dataOff + 4] = tmp10 - tmp11; + + var z1:Number = (tmp12 + tmp13) * 0.707106781; // c4 + data[dataOff + 2] = tmp13 + z1; // phase 5 + data[dataOff + 6] = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + var z5:Number = (tmp10 - tmp12) * 0.382683433; // c6 + var z2:Number = 0.541196100 * tmp10 + z5; // c2 - c6 + var z4:Number = 1.306562965 * tmp12 + z5; // c2 + c6 + var z3:Number = tmp11 * 0.707106781; // c4 + + var z11:Number = tmp7 + z3; // phase 5 + var z13:Number = tmp7 - z3; + + data[dataOff + 5] = z13 + z2; // phase 6 + data[dataOff + 3] = z13 - z2; + data[dataOff + 1] = z11 + z4; + data[dataOff + 7] = z11 - z4; + + dataOff += 8; // advance pointer to next row + } + + // Pass 2: process columns. + dataOff = 0; + for (i = 0; i < 8; i++) + { + tmp0 = data[dataOff + 0] + data[dataOff + 56]; + tmp7 = data[dataOff + 0] - data[dataOff + 56]; + tmp1 = data[dataOff + 8] + data[dataOff + 48]; + tmp6 = data[dataOff + 8] - data[dataOff + 48]; + tmp2 = data[dataOff + 16] + data[dataOff + 40]; + tmp5 = data[dataOff + 16] - data[dataOff + 40]; + tmp3 = data[dataOff + 24] + data[dataOff + 32]; + tmp4 = data[dataOff + 24] - data[dataOff + 32]; + + // Even par + tmp10 = tmp0 + tmp3; // phase 2 + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + data[dataOff + 0] = tmp10 + tmp11; // phase 3 + data[dataOff + 32] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781; // c4 + data[dataOff + 16] = tmp13 + z1; // phase 5 + data[dataOff + 48] = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433; // c6 + z2 = 0.541196100 * tmp10 + z5; // c2 - c6 + z4 = 1.306562965 * tmp12 + z5; // c2 + c6 + z3 = tmp11 * 0.707106781; // c4 + + z11 = tmp7 + z3; // phase 5 */ + z13 = tmp7 - z3; + + data[dataOff + 40] = z13 + z2; // phase 6 + data[dataOff + 24] = z13 - z2; + data[dataOff + 8] = z11 + z4; + data[dataOff + 56] = z11 - z4; + + dataOff++; // advance pointer to next column + } + + // Quantize/descale the coefficients + for (i = 0; i < 64; i++) + { + // Apply the quantization and scaling factor + // and round to nearest integer + data[i] = Math.round((data[i] * fdtbl[i])); + } + + return data; + } + + //-------------------------------------------------------------------------- + // + // Methods: Output + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function writeBits(bs:BitString):void + { + var value:int = bs.val; + var posval:int = bs.len - 1; + while (posval >= 0) + { + if (value & uint(1 << posval) ) + { + bytenew |= uint(1 << bytepos); + } + posval--; + bytepos--; + if (bytepos < 0) + { + if (bytenew == 0xFF) + { + writeByte(0xFF); + writeByte(0); + } + else + { + writeByte(bytenew); + } + bytepos = 7; + bytenew = 0; + } + } + } + + /** + * @private + */ + private function writeByte(value:int):void + { + byteout.writeByte(value); + } + + /** + * @private + */ + private function writeWord(value:int):void + { + writeByte((value >> 8) & 0xFF); + writeByte(value & 0xFF); + } + + /** + * @private + */ + private function writeAPP0():void + { + writeWord(0xFFE0); // marker + writeWord(16); // length + writeByte(0x4A); // J + writeByte(0x46); // F + writeByte(0x49); // I + writeByte(0x46); // F + writeByte(0); // = "JFIF",'\0' + writeByte(1); // versionhi + writeByte(1); // versionlo + writeByte(0); // xyunits + writeWord(1); // xdensity + writeWord(1); // ydensity + writeByte(0); // thumbnwidth + writeByte(0); // thumbnheight + } + + /** + * @private + */ + private function writeDQT():void + { + writeWord(0xFFDB); // marker + writeWord(132); // length + writeByte(0); + var i:int; + + for (i = 0; i < 64; i++) + { + writeByte(YTable[i]); + } + + writeByte(1); + + for (i = 0; i < 64; i++) + { + writeByte(UVTable[i]); + } + } + + /** + * @private + */ + private function writeSOF0(width:int, height:int):void + { + writeWord(0xFFC0); // marker + writeWord(17); // length, truecolor YUV JPG + writeByte(8); // precision + writeWord(height); + writeWord(width); + writeByte(3); // nrofcomponents + writeByte(1); // IdY + writeByte(0x11); // HVY + writeByte(0); // QTY + writeByte(2); // IdU + writeByte(0x11); // HVU + writeByte(1); // QTU + writeByte(3); // IdV + writeByte(0x11); // HVV + writeByte(1); // QTV + } + + /** + * @private + */ + private function writeDHT():void + { + var i:int; + + writeWord(0xFFC4); // marker + writeWord(0x01A2); // length + + writeByte(0); // HTYDCinfo + for (i = 0; i < 16; i++) + { + writeByte(std_dc_luminance_nrcodes[i + 1]); + } + for (i = 0; i <= 11; i++) + { + writeByte(std_dc_luminance_values[i]); + } + + writeByte(0x10); // HTYACinfo + for (i = 0; i < 16; i++) + { + writeByte(std_ac_luminance_nrcodes[i + 1]); + } + for (i = 0; i <= 161; i++) + { + writeByte(std_ac_luminance_values[i]); + } + + writeByte(1); // HTUDCinfo + for (i = 0; i < 16; i++) + { + writeByte(std_dc_chrominance_nrcodes[i + 1]); + } + for (i = 0; i <= 11; i++) + { + writeByte(std_dc_chrominance_values[i]); + } + + writeByte(0x11); // HTUACinfo + for (i = 0; i < 16; i++) + { + writeByte(std_ac_chrominance_nrcodes[i + 1]); + } + for (i = 0; i <= 161; i++) + { + writeByte(std_ac_chrominance_values[i]); + } + } + + /** + * @private + */ + private function writeSOS():void + { + writeWord(0xFFDA); // marker + writeWord(12); // length + writeByte(3); // nrofcomponents + writeByte(1); // IdY + writeByte(0); // HTY + writeByte(2); // IdU + writeByte(0x11); // HTU + writeByte(3); // IdV + writeByte(0x11); // HTV + writeByte(0); // Ss + writeByte(0x3f); // Se + writeByte(0); // Bf + } +} + +} + +class BitString +{ + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function BitString() + { + super(); + } + + /** + * @private + */ + public var len:int = 0; + + /** + * @private + */ + public var val:int = 0; +} diff --git a/src/mx/graphics/codec/PNGEncoder.as b/src/mx/graphics/codec/PNGEncoder.as new file mode 100644 index 00000000..b4ea6988 --- /dev/null +++ b/src/mx/graphics/codec/PNGEncoder.as @@ -0,0 +1,295 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.graphics.codec +{ + +import flash.display.BitmapData; +import flash.utils.ByteArray; + +/** + * The PNGEncoder class converts raw bitmap images into encoded + * images using Portable Network Graphics (PNG) lossless compression. + * + *

For the PNG specification, see http://www.w3.org/TR/PNG/

. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class PNGEncoder implements IImageEncoder +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * @private + * The MIME type for a PNG image. + */ + private static const CONTENT_TYPE:String = "image/png"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function PNGEncoder() + { + super(); + + initializeCRCTable(); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Used for computing the cyclic redundancy checksum + * at the end of each chunk. + */ + private var crcTable:Array; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // contentType + //---------------------------------- + + /** + * The MIME type for the PNG encoded image. + * The value is "image/png". + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get contentType():String + { + return CONTENT_TYPE; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Converts the pixels of a BitmapData object + * to a PNG-encoded ByteArray object. + * + * @param bitmapData The input BitmapData object. + * + * @return Returns a ByteArray object containing PNG-encoded image data. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function encode(bitmapData:BitmapData):ByteArray + { + return internalEncode(bitmapData, bitmapData.width, bitmapData.height, + bitmapData.transparent); + } + + /** + * Converts a ByteArray object containing raw pixels + * in 32-bit ARGB (Alpha, Red, Green, Blue) format + * to a new PNG-encoded ByteArray object. + * The original ByteArray is left unchanged. + * + * @param byteArray The input ByteArray object containing raw pixels. + * This ByteArray should contain + * 4 * width * height bytes. + * Each pixel is represented by 4 bytes, in the order ARGB. + * The first four bytes represent the top-left pixel of the image. + * The next four bytes represent the pixel to its right, etc. + * Each row follows the previous one without any padding. + * + * @param width The width of the input image, in pixels. + * + * @param height The height of the input image, in pixels. + * + * @param transparent If false, alpha channel information + * is ignored but you still must represent each pixel + * as four bytes in ARGB format. + * + * @return Returns a ByteArray object containing PNG-encoded image data. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function encodeByteArray(byteArray:ByteArray, width:int, height:int, + transparent:Boolean = true):ByteArray + { + return internalEncode(byteArray, width, height, transparent); + } + + /** + * @private + */ + private function initializeCRCTable():void + { + crcTable = []; + + for (var n:uint = 0; n < 256; n++) + { + var c:uint = n; + for (var k:uint = 0; k < 8; k++) + { + if (c & 1) + c = uint(uint(0xedb88320) ^ uint(c >>> 1)); + else + c = uint(c >>> 1); + } + crcTable[n] = c; + } + } + + /** + * @private + */ + private function internalEncode(source:Object, width:int, height:int, + transparent:Boolean = true):ByteArray + { + // The source is either a BitmapData or a ByteArray. + var sourceBitmapData:BitmapData = source as BitmapData; + var sourceByteArray:ByteArray = source as ByteArray; + + if (sourceByteArray) + sourceByteArray.position = 0; + + // Create output byte array + var png:ByteArray = new ByteArray(); + + // Write PNG signature + png.writeUnsignedInt(0x89504E47); + png.writeUnsignedInt(0x0D0A1A0A); + + // Build IHDR chunk + var IHDR:ByteArray = new ByteArray(); + IHDR.writeInt(width); + IHDR.writeInt(height); + IHDR.writeByte(8); // bit depth per channel + IHDR.writeByte(6); // color type: RGBA + IHDR.writeByte(0); // compression method + IHDR.writeByte(0); // filter method + IHDR.writeByte(0); // interlace method + writeChunk(png, 0x49484452, IHDR); + + // Build IDAT chunk + var IDAT:ByteArray = new ByteArray(); + for (var y:int = 0; y < height; y++) + { + IDAT.writeByte(0); // no filter + + var x:int; + var pixel:uint; + + if (!transparent) + { + for (x = 0; x < width; x++) + { + if (sourceBitmapData) + pixel = sourceBitmapData.getPixel(x, y); + else + pixel = sourceByteArray.readUnsignedInt(); + + IDAT.writeUnsignedInt(uint(((pixel & 0xFFFFFF) << 8) | 0xFF)); + } + } + else + { + for (x = 0; x < width; x++) + { + if (sourceBitmapData) + pixel = sourceBitmapData.getPixel32(x, y); + else + pixel = sourceByteArray.readUnsignedInt(); + + IDAT.writeUnsignedInt(uint(((pixel & 0xFFFFFF) << 8) | + (pixel >>> 24))); + } + } + } + IDAT.compress(); + writeChunk(png, 0x49444154, IDAT); + + // Build IEND chunk + writeChunk(png, 0x49454E44, null); + + // return PNG + png.position = 0; + return png; + } + + /** + * @private + */ + private function writeChunk(png:ByteArray, type:uint, data:ByteArray):void + { + // Write length of data. + var len:uint = 0; + if (data) + len = data.length; + png.writeUnsignedInt(len); + + // Write chunk type. + var typePos:uint = png.position; + png.writeUnsignedInt(type); + + // Write data. + if (data) + png.writeBytes(data); + + // Write CRC of chunk type and data. + var crcPos:uint = png.position; + png.position = typePos; + var crc:uint = 0xFFFFFFFF; + for (var i:uint = typePos; i < crcPos; i++) + { + crc = uint(crcTable[(crc ^ png.readUnsignedByte()) & uint(0xFF)] ^ + uint(crc >>> 8)); + } + crc = uint(crc ^ uint(0xFFFFFFFF)); + png.position = crcPos; + png.writeUnsignedInt(crc); + } +} + +} diff --git a/src/mx/logging/AbstractTarget.as b/src/mx/logging/AbstractTarget.as new file mode 100644 index 00000000..c4630536 --- /dev/null +++ b/src/mx/logging/AbstractTarget.as @@ -0,0 +1,386 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging +{ + +import mx.core.IMXMLObject; +import mx.logging.errors.InvalidFilterError; +import mx.managers.ISystemManager; +import mx.managers.SystemManager; +import mx.resources.IResourceManager; +import mx.resources.ResourceManager; +import mx.utils.UIDUtil; + +[ResourceBundle("logging")] + +/** + * This class provides the basic functionality required by the logging framework + * for a target implementation. + * It handles the validation of filter expressions and provides a default level + * property. + * No implementation of the logEvent() method is provided. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class AbstractTarget implements ILoggingTarget, IMXMLObject +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function AbstractTarget() + { + super(); + + _id = UIDUtil.createUID(); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Count of the number of loggers this target is listening to. When this + * value is zero changes to the filters property shouldn't do anything + */ + private var _loggerCount:uint = 0; + + /** + * @private + * Used for accessing localized Error messages. + */ + private var resourceManager:IResourceManager = + ResourceManager.getInstance(); + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // filters + //---------------------------------- + + /** + * @private + * Storage for the filters property. + */ + private var _filters:Array = [ "*" ]; + + [Inspectable(category="General", arrayType="String")] + + /** + * In addition to the level setting, filters are used to + * provide a psuedo-hierarchical mapping for processing only those events + * for a given category. + *

+ * Each logger belongs to a category. + * By convention these categories map to the fully-qualified class name in + * which the logger is used. + * For example, a logger that is logging messages for the + * mx.rpc.soap.WebService class, uses + * "mx.rpc.soap.WebService" as the parameter to the + * Log.getLogger() method call. + * When messages are sent under this category only those targets that have + * a filter which matches that category receive notification of those + * events. + * Filter expressions can include a wildcard match, indicated with an + * asterisk. + * The wildcard must be the right-most character in the expression. + * For example: rpc~~, mx.~~, or ~~. + * If an invalid expression is specified, a InvalidFilterError + * is thrown. + * If null or [] is specified, the filters are set to the + * default of ["~~"]. + *

+ *

For example: + *

+     *           var traceLogger:ILoggingTarget = new TraceTarget();
+     *           traceLogger.filters = ["mx.rpc.~~", "mx.messaging.~~"];
+     *           Log.addTarget(traceLogger);
+     *     
+ *

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get filters():Array + { + return _filters; + } + + /** + * @private + * This method will make sure that all of the filter expressions specified + * are valid, and will throw InvalidFilterError if any are not. + */ + public function set filters(value:Array):void + { + if (value && value.length > 0) + { + // a valid filter value will be fully qualified or have a wildcard + // in it. the wild card can only be located at the end of the + // expression. valid examples xx*, xx.*, * + var filter:String; + var index:int; + var message:String; + for (var i:uint = 0; i= 0) && (index != (filter.length -1))) + { + message = resourceManager.getString( + "logging", "charPlacement", [ filter ]); + throw new InvalidFilterError(message); + } + } // for + } + else + { + // if null was specified then default to all + value = ["*"]; + } + + if (_loggerCount > 0) + { + Log.removeTarget(this); + _filters = value; + Log.addTarget(this); + } + else + { + _filters = value; + } + } + + //---------------------------------- + // id + //---------------------------------- + + /** + * @prviate + * Storage for the id property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private var _id:String; + + [Inspectable(category="General")] + + /** + * Provides access to the id of this target. + * The id is assigned at runtime by the mxml compiler if used as an mxml + * tag, or internally if used within a script block + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get id():String + { + return _id; + } + + //---------------------------------- + // level + //---------------------------------- + + /** + * @private + * Storage for the level property. + */ + private var _level:int = LogEventLevel.ALL; + + /** + * Provides access to the level this target is currently set at. + * Value values are: + *
    + *
  • LogEventLevel.FATAL (1000) designates events that are very + * harmful and will eventually lead to application failure
  • + * + *
  • LogEventLevel.ERROR (8) designates error events that might + * still allow the application to continue running.
  • + * + *
  • LogEventLevel.WARN (6) designates events that could be + * harmful to the application operation
  • + * + *
  • LogEventLevel.INFO (4) designates informational messages + * that highlight the progress of the application at + * coarse-grained level.
  • + * + *
  • LogEventLevel.DEBUG (2) designates informational + * level messages that are fine grained and most helpful when + * debugging an application.
  • + * + *
  • LogEventLevel.ALL (0) intended to force a target to + * process all messages.
  • + *
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get level():int + { + return _level; + } + + /** + * @private + */ + public function set level(value:int):void + { + // A change of level may impact the target level for Log. + Log.removeTarget(this); + _level = value; + Log.addTarget(this); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Sets up this target with the specified logger. + * This allows this target to receive log events from the specified logger. + * + * @param logger The ILogger that this target should listen to. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function addLogger(logger:ILogger):void + { + if (logger) + { + _loggerCount++; + logger.addEventListener(LogEvent.LOG, logHandler); + } + } + + /** + * Stops this target from receiving events from the specified logger. + * + * @param logger The ILogger that this target should ignore. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function removeLogger(logger:ILogger):void + { + if (logger) + { + _loggerCount--; + logger.removeEventListener(LogEvent.LOG, logHandler); + } + } + + /** + * Called after the implementing object has been created + * and all properties specified on the tag have been assigned. + * + * @param document MXML document that created this object. + * + * @param id Used by the document to refer to this object. + * If the object is a deep property on the document, id is null. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function initialized(document:Object, id:String):void + { + _id = id; + Log.addTarget(this); + } + + /** + * This method handles a LogEvent from an associated logger. + * A target uses this method to translate the event into the appropriate + * format for transmission, storage, or display. + * This method will be called only if the event's level is in range of the + * target's level. + * + *

NOTE: Descendants must override this method to make it useful.

+ * + * @param event An event from an associated logger. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function logEvent(event:LogEvent):void + { + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * @private + * This method will call the logEvent method if the level of the + * event is appropriate for the current level. + */ + private function logHandler(event:LogEvent):void + { + if (event.level >= level) + logEvent(event); + } +} + +} diff --git a/src/mx/logging/ILogger.as b/src/mx/logging/ILogger.as new file mode 100644 index 00000000..c6ad3a6b --- /dev/null +++ b/src/mx/logging/ILogger.as @@ -0,0 +1,320 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging +{ + +import flash.events.IEventDispatcher; + +/** + * All loggers within the logging framework must implement this interface. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface ILogger extends IEventDispatcher +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // category + //---------------------------------- + + /** + * The category value for the logger. + * + * @return String containing the category for this logger. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get category():String; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Logs the specified data at the given level. + * + *

The String specified for logging can contain braces with an index + * indicating which additional parameter should be inserted + * into the String before it is logged. + * For example "the first additional parameter was {0} the second was {1}" + * is translated into "the first additional parameter was 10 the + * second was 15" when called with 10 and 15 as parameters.

+ * + * @param level The level this information should be logged at. + * Valid values are: + *
    + *
  • LogEventLevel.FATAL designates events that are very + * harmful and will eventually lead to application failure
  • + * + *
  • LogEventLevel.ERROR designates error events + * that might still allow the application to continue running.
  • + * + *
  • LogEventLevel.WARN designates events that could be + * harmful to the application operation
  • + * + *
  • LogEventLevel.INFO designates informational messages + * that highlight the progress of the application at + * coarse-grained level.
  • + * + *
  • LogEventLevel.DEBUG designates informational + * level messages that are fine grained and most helpful when + * debugging an application.
  • + *
+ * + * @param message The information to log. + * This String can contain special marker characters of the form {x}, + * where x is a zero based index that will be replaced with + * the additional parameters found at that index if specified. + * + * @param rest Additional parameters that can be subsituted in the str + * parameter at each "{x}" location, where x + * is an integer (zero based) index value into the Array of values + * specified. + * + * @example + *
+     *  // Get the logger for the mx.messaging.Channel "category"
+     *  // and send some data to it.
+     *  var logger:ILogger = Log.getLogger("mx.messaging.Channel");
+     *  logger.log(LogEventLevel.DEBUG, "here is some channel info {0} and {1}", LogEventLevel.DEBUG, 15.4, true);
+     *
+     *  // This will log the following String as a DEBUG log message:
+     *  //   "here is some channel info 15.4 and true"
+     *  
+ * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function log(level:int, message:String, ... rest):void; + + /** + * Logs the specified data using the LogEventLevel.DEBUG + * level. + * LogEventLevel.DEBUG designates informational level + * messages that are fine grained and most helpful when debugging + * an application. + * + *

The string specified for logging can contain braces with an index + * indicating which additional parameter should be inserted + * into the string before it is logged. + * For example "the first additional parameter was {0} the second was {1}" + * will be translated into "the first additional parameter was 10 the + * second was 15" when called with 10 and 15 as parameters.

+ * + * @param message The information to log. + * This string can contain special marker characters of the form {x}, + * where x is a zero based index that will be replaced with + * the additional parameters found at that index if specified. + * + * @param rest Additional parameters that can be subsituted in the str + * parameter at each "{x}" location, where x + * is an integer (zero based) index value into the Array of values + * specified. + * + * @example + *
+     *  // Get the logger for the mx.messaging.Channel "category"
+     *  // and send some data to it.
+     *  var logger:ILogger = Log.getLogger("mx.messaging.Channel");
+     *  logger.debug("here is some channel info {0} and {1}", 15.4, true);
+     *
+     *  // This will log the following String:
+     *  //   "here is some channel info 15.4 and true"
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function debug(message:String, ... rest):void; + + /** + * Logs the specified data using the LogEventLevel.ERROR + * level. + * LogEventLevel.ERROR designates error events + * that might still allow the application to continue running. + * + *

The string specified for logging can contain braces with an index + * indicating which additional parameter should be inserted + * into the string before it is logged. + * For example "the first additional parameter was {0} the second was {1}" + * will be translated into "the first additional parameter was 10 the + * second was 15" when called with 10 and 15 as parameters.

+ * + * @param message The information to log. + * This String can contain special marker characters of the form {x}, + * where x is a zero based index that will be replaced with + * the additional parameters found at that index if specified. + * + * @param rest Additional parameters that can be subsituted in the str + * parameter at each "{x}" location, where x + * is an integer (zero based) index value into the Array of values + * specified. + * + * @example + *
+     *  // Get the logger for the mx.messaging.Channel "category"
+     *  // and send some data to it.
+     *  var logger:ILogger = Log.getLogger("mx.messaging.Channel");
+     *  logger.error("here is some channel info {0} and {1}", 15.4, true);
+     *
+     *  // This will log the following String:
+     *  //   "here is some channel info 15.4 and true"
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function error(message:String, ... rest):void; + + /** + * Logs the specified data using the LogEventLevel.FATAL + * level. + * LogEventLevel.FATAL designates events that are very + * harmful and will eventually lead to application failure + * + *

The string specified for logging can contain braces with an index + * indicating which additional parameter should be inserted + * into the string before it is logged. + * For example "the first additional parameter was {0} the second was {1}" + * will be translated into "the first additional parameter was 10 the + * second was 15" when called with 10 and 15 as parameters.

+ * + * @param message The information to log. + * This String can contain special marker characters of the form {x}, + * where x is a zero based index that will be replaced with + * the additional parameters found at that index if specified. + * + * @param rest Additional parameters that can be subsituted in the str + * parameter at each "{x}" location, where x + * is an integer (zero based) index value into the Array of values + * specified. + * + * @example + *
+     *  // Get the logger for the mx.messaging.Channel "category"
+     *  // and send some data to it.
+     *  var logger:ILogger = Log.getLogger("mx.messaging.Channel");
+     *  logger.fatal("here is some channel info {0} and {1}", 15.4, true);
+     *
+     *  // This will log the following String:
+     *  //   "here is some channel info 15.4 and true"
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function fatal(message:String, ... rest):void; + + /** + * Logs the specified data using the LogEvent.INFO level. + * LogEventLevel.INFO designates informational messages that + * highlight the progress of the application at coarse-grained level. + * + *

The string specified for logging can contain braces with an index + * indicating which additional parameter should be inserted + * into the string before it is logged. + * For example "the first additional parameter was {0} the second was {1}" + * will be translated into "the first additional parameter was 10 the + * second was 15" when called with 10 and 15 as parameters.

+ * + * @param message The information to log. + * This String can contain special marker characters of the form {x}, + * where x is a zero based index that will be replaced with + * the additional parameters found at that index if specified. + * + * @param rest Additional parameters that can be subsituted in the str + * parameter at each "{x}" location, where x + * is an integer (zero based) index value into the Array of values + * specified. + * + * @example + *
+     *  // Get the logger for the mx.messaging.Channel "category"
+     *  // and send some data to it.
+     *  var logger:ILogger = Log.getLogger("mx.messaging.Channel");
+     *  logger.info("here is some channel info {0} and {1}", 15.4, true);
+     *
+     *  // This will log the following String:
+     *  //   "here is some channel info 15.4 and true"
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function info(message:String, ... rest):void; + + /** + * Logs the specified data using the LogEventLevel.WARN level. + * LogEventLevel.WARN designates events that could be harmful + * to the application operation. + * + *

The string specified for logging can contain braces with an index + * indicating which additional parameter should be inserted + * into the string before it is logged. + * For example "the first additional parameter was {0} the second was {1}" + * will be translated into "the first additional parameter was 10 the + * second was 15" when called with 10 and 15 as parameters.

+ * + * @param message The information to log. + * This String can contain special marker characters of the form {x}, + * where x is a zero based index that will be replaced with + * the additional parameters found at that index if specified. + * + * @param rest Aadditional parameters that can be subsituted in the str + * parameter at each "{x}" location, where x + * is an integer (zero based) index value into the Array of values + * specified. + * + * @example + *
+     *  // Get the logger for the mx.messaging.Channel "category"
+     *  // and send some data to it.
+     *  var logger:ILogger = Log.getLogger("mx.messaging.Channel");
+     *  logger.warn("here is some channel info {0} and {1}", 15.4, true);
+     *
+     *  // This will log the following String:
+     *  //   "here is some channel info 15.4 and true"
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function warn(message:String, ... rest):void; +} + +} \ No newline at end of file diff --git a/src/mx/logging/ILoggingTarget.as b/src/mx/logging/ILoggingTarget.as new file mode 100644 index 00000000..9a11f983 --- /dev/null +++ b/src/mx/logging/ILoggingTarget.as @@ -0,0 +1,158 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging +{ + +/** + * All logger target implementations within the logging framework + * must implement this interface. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface ILoggingTarget +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // filters + //---------------------------------- + + /** + * In addition to the level setting, filters are used to + * provide a psuedo-hierarchical mapping for processing only those events + * for a given category. + * + *

Each logger belongs to a category. + * By convention these categories map to the fully qualified class name + * in which the logger is used. + * For example, a logger that is logging messages for the + * mx.rpc.soap.WebService class would use + * "mx.rpc.soap.WebService" as the parameter + * to the Log.getLogger() call. + * When messages are sent under this category only those targets that have + * a filter which matches that category will receive notification of those + * events. + * Filter expressions may include a wildcard match, indicated with an + * asterisk. + * The wildcard must be the right most character in the expression. + * For example: rpc~~, mx.~~, or ~~. + * If an invalid expression is specified a InvalidFilterError + * will be thrown. + * No spaces or any of the following characters are valid within a filter + * expression: []~$^&\/(){}<>+=`!#%?,:;'"@.

+ * + * @example + *
+     *  var traceLogger:ILoggingTarget = new TraceTarget();
+     *  traceLogger.filters = [ "mx.rpc.~~", "mx.messaging.~~" ];
+     *  Log.addTarget(traceLogger);
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get filters():Array; + + /** + * @private + */ + function set filters(value:Array):void; + + //---------------------------------- + // level + //---------------------------------- + + /** + * Provides access to the level this target is currently set at. + * Value values are: + *
    + *
  • LogEventLevel.FATAL designates events that are very + * harmful and will eventually lead to application failure
  • + * + *
  • LogEventLevel.ERROR designates error events that might + * still allow the application to continue running.
  • + * + *
  • LogEventLevel.WARN designates events that could be + * harmful to the application operation
  • + * + *
  • LogEventLevel.INFO designates informational messages + * that highlight the progress of the application at + * coarse-grained level.
  • + * + *
  • LogEventLevel.DEBUG designates informational + * level messages that are fine grained and most helpful when + * debugging an application.
  • + * + *
  • LogEventLevel.ALL intended to force a target to + * process all messages.
  • + *
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get level():int; + + /** + * @private + */ + function set level(value:int):void; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Sets up this target with the specified logger. + * This allows this target to receive log events from the specified logger. + * + *

Note: This method is called by the framework + * and should not be called by you directly.

+ * + * @param logger The ILogger that this target listens to. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function addLogger(logger:ILogger):void; + + /** + * Stops this target from receiving events from the specified logger. + * + *

Note: This method is called by the framework + * and should not be called by you directly.

+ * + * @param logger The ILogger that this target ignores. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function removeLogger(logger:ILogger):void; +} + +} diff --git a/src/mx/logging/Log.as b/src/mx/logging/Log.as new file mode 100644 index 00000000..97359616 --- /dev/null +++ b/src/mx/logging/Log.as @@ -0,0 +1,486 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging +{ + +import mx.logging.errors.InvalidCategoryError; +//import mx.managers.ISystemManager; +//import mx.managers.SystemManager; +import mx.resources.IResourceManager; +import mx.resources.ResourceManager; + +[ResourceBundle("logging")] + +/** + * Provides pseudo-hierarchical logging capabilities with multiple format and + * output options. + * The log system consists of two major components, the logger and a target. + * You can use the logger to send information to a target. + * The target is responsible for formatting and general output of the log data. + *

+ * Loggers are singleton instances created for a particular category of + * information. + * Typically, the category is the package name of the component + * that desires to log information. + * The category provides users the ability to specify what log information they + * are interested in. + * Multiple categories can be selected and combined with regular expressions. + * This allows for both broad and narrow logging information to be acquired. + * For example, you might be interested in all logging information under + * the "mx.messaging" and "mx.rpc" packages and want the output from these + * packages to be formatted as XML. + * To get the all of the logging information under the "mx.messaging" category + * including sub-packages and components a wildcard expression is required, such as + * "mx.messaging.~~". + * See the code example below for more details. + *

+ *

Targets provide the output mechanism of the data being logged. + * This mechanism typically includes formatting, transmission, or storage, but + * can be anything possible under the VM. + * There are two targets provided: MiniDebugTarget and + * TraceTarget. + * Each of these writers take the current log information and "sends" it + * somewhere for display and/or storage. + * Targets also provide the specification of what log data to output. + *

+ * + * @example + *
+ *  ... 
+ *  import mx.logging.targets.*;
+ *  import mx.logging.*;
+ *
+ *  private function initLogging():void {
+ *      // Create a target.
+ *      var logTarget:TraceTarget = new TraceTarget();
+ *
+ *      // Log only messages for the classes in the mx.rpc.* and 
+ *      // mx.messaging packages.
+ *      logTarget.filters=["mx.rpc.*","mx.messaging.*"];
+ *
+ *      // Log all log levels.
+ *      logTarget.level = LogEventLevel.ALL;
+ *
+ *      // Add date, time, category, and log level to the output.
+ *      logTarget.includeDate = true;
+ *      logTarget.includeTime = true;
+ *      logTarget.includeCategory = true;
+ *      logTarget.includeLevel = true;
+ *
+ *      // Begin logging.
+ *      Log.addTarget(logTarget);
+ *  } 
+ *  ...
+ *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class Log +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Sentinal value for the target log level to indicate no logging. + */ + private static var NONE:int = int.MAX_VALUE; + + /** + * @private + * The most verbose supported log level among registered targets. + */ + private static var _targetLevel:int = NONE; + // Initialize target level to a value out of range. + + /** + * @private + * An associative Array of existing loggers keyed by category + */ + private static var _loggers:Array; + + /** + * @private + * Array of targets that should be searched any time + * a new logger is created. + */ + private static var _targets:Array = []; + + /** + * @private + * Storage for the resourceManager getter. + * This gets initialized on first access, + * not at static initialization time, in order to ensure + * that the Singleton registry has already been initialized. + */ + private static var _resourceManager:IResourceManager; + + /** + * @private + * A reference to the object which manages + * all of the application's localized resources. + * This is a singleton instance which implements + * the IResourceManager interface. + */ + private static function get resourceManager():IResourceManager + { + if (!_resourceManager) + _resourceManager = ResourceManager.getInstance(); + + return _resourceManager; + } + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Indicates whether a fatal level log event will be processed by a + * log target. + * + * @return true if a fatal level log event will be logged; otherwise false. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isFatal():Boolean + { + return (_targetLevel <= LogEventLevel.FATAL) ? true : false; + } + + /** + * Indicates whether an error level log event will be processed by a + * log target. + * + * @return true if an error level log event will be logged; otherwise false. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isError():Boolean + { + return (_targetLevel <= LogEventLevel.ERROR) ? true : false; + } + + /** + * Indicates whether a warn level log event will be processed by a + * log target. + * + * @return true if a warn level log event will be logged; otherwise false. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isWarn():Boolean + { + return (_targetLevel <= LogEventLevel.WARN) ? true : false; + } + + /** + * Indicates whether an info level log event will be processed by a + * log target. + * + * @return true if an info level log event will be logged; otherwise false. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isInfo():Boolean + { + return (_targetLevel <= LogEventLevel.INFO) ? true : false; + } + + /** + * Indicates whether a debug level log event will be processed by a + * log target. + * + * @return true if a debug level log event will be logged; otherwise false. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isDebug():Boolean + { + return (_targetLevel <= LogEventLevel.DEBUG) ? true : false; + } + + /** + * Allows the specified target to begin receiving notification of log + * events. + * + * @param The specific target that should capture log events. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function addTarget(target:ILoggingTarget):void + { + if (target) + { + var filters:Array = target.filters; + var logger:ILogger; + // need to find what filters this target matches and set the specified + // target as a listener for that logger. + for (var i:String in _loggers) + { + if (categoryMatchInFilterList(i, filters)) + target.addLogger(ILogger(_loggers[i])); + } + // if we found a match all is good, otherwise we need to + // put the target in a waiting queue in the event that a logger + // is created that this target cares about. + _targets.push(target); + + if (_targetLevel == NONE) + _targetLevel = target.level + else if (target.level < _targetLevel) + _targetLevel = target.level; + } + else + { + var message:String = resourceManager.getString( + "logging", "invalidTarget"); + throw new ArgumentError(message); + } + } + + /** + * Stops the specified target from receiving notification of log + * events. + * + * @param The specific target that should capture log events. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function removeTarget(target:ILoggingTarget):void + { + if (target) + { + var filters:Array = target.filters; + var logger:ILogger; + // Disconnect this target from any matching loggers. + for (var i:String in _loggers) + { + if (categoryMatchInFilterList(i, filters)) + { + target.removeLogger(ILogger(_loggers[i])); + } + } + // Remove the target. + for (var j:int = 0; j<_targets.length; j++) + { + if (target == _targets[j]) + { + _targets.splice(j, 1); + j--; + } + } + resetTargetLevel(); + } + else + { + var message:String = resourceManager.getString( + "logging", "invalidTarget"); + throw new ArgumentError(message); + } + } + + /** + * Returns the logger associated with the specified category. + * If the category given doesn't exist a new instance of a logger will be + * returned and associated with that category. + * Categories must be at least one character in length and may not contain + * any blanks or any of the following characters: + * []~$^&\/(){}<>+=`!#%?,:;'"@ + * This method will throw an InvalidCategoryError if the + * category specified is malformed. + * + * @param category The category of the logger that should be returned. + * + * @return An instance of a logger object for the specified name. + * If the name doesn't exist, a new instance with the specified + * name is returned. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getLogger(category:String):ILogger + { + checkCategory(category); + if (!_loggers) + _loggers = []; + + // get the logger for the specified category or create one if it + // doesn't exist + var result:ILogger = _loggers[category]; + if (result == null) + { + result = new LogLogger(category); + _loggers[category] = result; + } + + // check to see if there are any targets waiting for this logger. + var target:ILoggingTarget; + for (var i:int = 0; i < _targets.length; i++) + { + target = ILoggingTarget(_targets[i]); + if (categoryMatchInFilterList(category, target.filters)) + target.addLogger(result); + } + + return result; + } + + /** + * This method removes all of the current loggers from the cache. + * Subsquent calls to the getLogger() method return new instances + * of loggers rather than any previous instances with the same category. + * This method is intended for use in debugging only. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function flush():void + { + _loggers = []; + _targets = []; + _targetLevel = NONE; + } + + /** + * This method checks the specified string value for illegal characters. + * + * @param value The String to check for illegal characters. + * The following characters are not valid: + * []~$^&\/(){}<>+=`!#%?,:;'"@ + * @return true if there are any illegal characters found, + * false otherwise + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function hasIllegalCharacters(value:String):Boolean + { + return value.search(/[\[\]\~\$\^\&\\(\)\{\}\+\?\/=`!@#%,:;'"<>\s]/) != -1; + } + + // private members + /** + * This method checks that the specified category matches any of the filter + * expressions provided in the filters Array. + * + * @param category The category to match against + * @param filters A list of Strings to check category against. + * @return true if the specified category matches any of the + * filter expressions found in the filters list, false + * otherwise. + * @private + */ + private static function categoryMatchInFilterList(category:String, filters:Array):Boolean + { + var result:Boolean = false; + var filter:String; + var index:int = -1; + for (var i:uint = 0; i < filters.length; i++) + { + filter = filters[i]; + // first check to see if we need to do a partial match + // do we have an asterisk? + index = filter.indexOf("*"); + + if (index == 0) + return true; + + index = index < 0 ? index = category.length : index -1; + + if (category.substring(0, index) == filter.substring(0, index)) + return true; + } + return false; + } + + /** + * This method will ensure that a valid category string has been specified. + * If the category is not valid an InvalidCategoryError will + * be thrown. + * Categories can not contain any blanks or any of the following characters: + * []`*~,!#$%^&()]{}+=\|'";?><./@ or be less than 1 character in length. + * @private + */ + private static function checkCategory(category:String):void + { + var message:String; + + if (category == null || category.length == 0) + { + message = resourceManager.getString( + "logging", "invalidLen"); + throw new InvalidCategoryError(message); + } + + if (hasIllegalCharacters(category) || (category.indexOf("*") != -1)) + { + message = resourceManager.getString( + "logging", "invalidChars"); + throw new InvalidCategoryError(message); + } + } + + /** + * @private + * This method resets the Log's target level to the most verbose log level + * for the currently registered targets. + */ + private static function resetTargetLevel():void + { + var minLevel:int = NONE; + for (var i:int = 0; i < _targets.length; i++) + { + if (minLevel == NONE || _targets[i].level < minLevel) + minLevel = _targets[i].level; + } + _targetLevel = minLevel; + } +} + +} diff --git a/src/mx/logging/LogEvent.as b/src/mx/logging/LogEvent.as new file mode 100644 index 00000000..490dae48 --- /dev/null +++ b/src/mx/logging/LogEvent.as @@ -0,0 +1,221 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging +{ + +import flash.events.Event; + +/** + * Represents the log information for a single logging event. + * The loging system dispatches a single event each time a process requests + * information be logged. + * This event can be captured by any object for storage or formatting. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class LogEvent extends Event +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * Event type constant; identifies a logging event. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const LOG:String = "log"; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Returns a string value representing the level specified. + * + * @param The level a string is desired for. + * + * @return The level specified in English. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getLevelString(value:uint):String + { + switch (value) + { + case LogEventLevel.INFO: + { + return "INFO"; + } + + case LogEventLevel.DEBUG: + { + return "DEBUG"; + } + + case LogEventLevel.ERROR: + { + return "ERROR"; + } + + case LogEventLevel.WARN: + { + return "WARN"; + } + + case LogEventLevel.FATAL: + { + return "FATAL"; + } + + case LogEventLevel.ALL: + { + return "ALL"; + } + } + + return "UNKNOWN"; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param msg String containing the log data. + * + * @param level The level for this log event. + * Valid values are: + *
    + *
  • LogEventLevel.FATAL designates events that are very + * harmful and will eventually lead to application failure
  • + * + *
  • LogEventLevel.ERROR designates error events that might + * still allow the application to continue running.
  • + * + *
  • LogEventLevel.WARN designates events that could be + * harmful to the application operation
  • + * + *
  • LogEventLevel.INFO designates informational messages + * that highlight the progress of the application at + * coarse-grained level.
  • + * + *
  • LogEventLevel.DEBUG designates informational + * level messages that are fine grained and most helpful when + * debugging an application.
  • + * + *
  • LogEventLevel.ALL intended to force a target to + * process all messages.
  • + *
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function LogEvent(message:String = "", + level:int = 0 /* LogEventLevel.ALL */) + { + super(LogEvent.LOG, false, false); + + this.message = message; + this.level = level; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // level + //---------------------------------- + + /** + * Provides access to the level for this log event. + * Valid values are: + *
    + *
  • LogEventLogEventLevel.INFO designates informational messages + * that highlight the progress of the application at + * coarse-grained level.
  • + * + *
  • LogEventLevel.DEBUG designates informational + * level messages that are fine grained and most helpful when + * debugging an application.
  • + * + *
  • LogEventLevel.ERROR designates error events that might + * still allow the application to continue running.
  • + * + *
  • LogEventLevel.WARN designates events that could be + * harmful to the application operation.
  • + * + *
  • LogEventLevel.FATAL designates events that are very + * harmful and will eventually lead to application failure.
  • + *
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var level:int; + + //---------------------------------- + // message + //---------------------------------- + + /** + * Provides access to the message that was logged. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var message:String; + + //-------------------------------------------------------------------------- + // + // Overridden methods: Event + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override public function clone():Event + { + return new LogEvent(message, /*type,*/ level); + } +} + +} \ No newline at end of file diff --git a/src/mx/logging/LogEventLevel.as b/src/mx/logging/LogEventLevel.as new file mode 100644 index 00000000..ccb5ed67 --- /dev/null +++ b/src/mx/logging/LogEventLevel.as @@ -0,0 +1,101 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging +{ + +/** + * Static class containing constants for use in the level + * property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public final class LogEventLevel +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * Designates events that are very + * harmful and will eventually lead to application failure. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const FATAL:int = 1000; + + /** + * Designates error events that might + * still allow the application to continue running. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ERROR:int = 8; + + /** + * Designates events that could be + * harmful to the application operation. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const WARN:int = 6; + + /** + * Designates informational messages that + * highlight the progress of the application at coarse-grained level. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const INFO:int = 4; + + /** + * Designates informational level + * messages that are fine grained and most helpful when debugging an + * application. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const DEBUG:int = 2; + + /** + * Tells a target to process all messages. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static const ALL:int = 0; +} + +} diff --git a/src/mx/logging/LogLogger.as b/src/mx/logging/LogLogger.as new file mode 100644 index 00000000..a9695c93 --- /dev/null +++ b/src/mx/logging/LogLogger.as @@ -0,0 +1,249 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging +{ + +import flash.events.EventDispatcher; +//import mx.managers.ISystemManager; +//import mx.managers.SystemManager; +import mx.resources.IResourceManager; +import mx.resources.ResourceManager; + +[ResourceBundle("logging")] + +/** + * The logger that is used within the logging framework. + * This class dispatches events for each message logged using the log() method. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class LogLogger extends EventDispatcher implements ILogger +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param category The category for which this log sends messages. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function LogLogger(category:String) + { + super(); + + _category = category; + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Used for accessing localized Error messages. + */ + private var resourceManager:IResourceManager = + ResourceManager.getInstance(); + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // category + //---------------------------------- + + /** + * @private + * Storage for the category property. + */ + private var _category:String; + + /** + * The category this logger send messages for. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get category():String + { + return _category; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function log(level:int, msg:String, ... rest):void + { + // we don't want to allow people to log messages at the + // Log.Level.ALL level, so throw a RTE if they do + if (level < LogEventLevel.DEBUG) + { + var message:String = resourceManager.getString( + "logging", "levelLimit"); + throw new ArgumentError(message); + } + + if (hasEventListener(LogEvent.LOG)) + { + // replace all of the parameters in the msg string + for (var i:int = 0; i < rest.length; i++) + { + msg = msg.replace(new RegExp("\\{"+i+"\\}", "g"), rest[i]); + } + + dispatchEvent(new LogEvent(msg, level)); + } + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function debug(msg:String, ... rest):void + { + if (hasEventListener(LogEvent.LOG)) + { + // replace all of the parameters in the msg string + for (var i:int = 0; i < rest.length; i++) + { + msg = msg.replace(new RegExp("\\{"+i+"\\}", "g"), rest[i]); + } + + dispatchEvent(new LogEvent(msg, LogEventLevel.DEBUG)); + } + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function error(msg:String, ... rest):void + { + if (hasEventListener(LogEvent.LOG)) + { + // replace all of the parameters in the msg string + for (var i:int = 0; i < rest.length; i++) + { + msg = msg.replace(new RegExp("\\{"+i+"\\}", "g"), rest[i]); + } + + dispatchEvent(new LogEvent(msg, LogEventLevel.ERROR)); + } + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function fatal(msg:String, ... rest):void + { + if (hasEventListener(LogEvent.LOG)) + { + // replace all of the parameters in the msg string + for (var i:int = 0; i < rest.length; i++) + { + msg = msg.replace(new RegExp("\\{"+i+"\\}", "g"), rest[i]); + } + + dispatchEvent(new LogEvent(msg, LogEventLevel.FATAL)); + } + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function info(msg:String, ... rest):void + { + if (hasEventListener(LogEvent.LOG)) + { + // replace all of the parameters in the msg string + for (var i:int = 0; i < rest.length; i++) + { + msg = msg.replace(new RegExp("\\{"+i+"\\}", "g"), rest[i]); + } + + dispatchEvent(new LogEvent(msg, LogEventLevel.INFO)); + } + } + + /** + * @inheritDoc + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function warn(msg:String, ... rest):void + { + if (hasEventListener(LogEvent.LOG)) + { + // replace all of the parameters in the msg string + for (var i:int = 0; i < rest.length; i++) + { + msg = msg.replace(new RegExp("\\{"+i+"\\}", "g"), rest[i]); + } + + dispatchEvent(new LogEvent(msg, LogEventLevel.WARN)); + } + } +} + +} diff --git a/src/mx/logging/errors/InvalidCategoryError.as b/src/mx/logging/errors/InvalidCategoryError.as new file mode 100644 index 00000000..8366100d --- /dev/null +++ b/src/mx/logging/errors/InvalidCategoryError.as @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging.errors +{ + +/** + * This error is thrown when a category specified for a logger + * contains invalid characters or is malformed. + * This error is thrown by the following method: + *
    + *
  • Log.getLogger() if a category specified + * is malformed.
  • + *
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class InvalidCategoryError extends Error +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param message The message that describes this error. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function InvalidCategoryError(message:String) + { + super(message); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Returns the messge as a String. + * + * @return The message. + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function toString():String + { + return String(message); + } +} + +} diff --git a/src/mx/logging/errors/InvalidFilterError.as b/src/mx/logging/errors/InvalidFilterError.as new file mode 100644 index 00000000..7bdcd362 --- /dev/null +++ b/src/mx/logging/errors/InvalidFilterError.as @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging.errors +{ + +/** + * This error is thrown when a filter specified for a target + * contains invalid characters or is malformed. + * This error is thrown by the following methods/properties: + *
    + *
  • ILoggerTarget.filters if a filter expression + * in this listis malformed.
  • + *
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class InvalidFilterError extends Error +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param message The message that describes this error. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function InvalidFilterError(message:String) + { + super(message); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Returns the messge as a String. + * + * @return The message. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function toString():String + { + return String(message); + } +} + +} diff --git a/src/mx/logging/targets/LineFormattedTarget.as b/src/mx/logging/targets/LineFormattedTarget.as new file mode 100644 index 00000000..7d1b748c --- /dev/null +++ b/src/mx/logging/targets/LineFormattedTarget.as @@ -0,0 +1,252 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging.targets +{ + +import mx.core.mx_internal; +import mx.logging.AbstractTarget; +import mx.logging.ILogger; +import mx.logging.LogEvent; + +use namespace mx_internal; + +/** + * All logger target implementations that have a formatted line style output + * should extend this class. + * It provides default behavior for including date, time, category, and level + * within the output. + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class LineFormattedTarget extends AbstractTarget +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + *

Constructs an instance of a logger target that will format + * the message data on a single line.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function LineFormattedTarget() + { + super(); + + includeTime = false; + includeDate = false; + includeCategory = false; + includeLevel = false; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // fieldSeparator + //---------------------------------- + + [Inspectable(category="General", defaultValue=" ")] + + /** + * The separator string to use between fields (the default is " ") + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var fieldSeparator:String = " "; + + //---------------------------------- + // includeCategory + //---------------------------------- + + [Inspectable(category="General", defaultValue="false")] + + /** + * Indicates if the category for this target should added to the trace. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var includeCategory:Boolean; + + //---------------------------------- + // includeDate + //---------------------------------- + + [Inspectable(category="General", defaultValue="false")] + + /** + * Indicates if the date should be added to the trace. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var includeDate:Boolean; + + //---------------------------------- + // includeLevel + //---------------------------------- + + [Inspectable(category="General", defaultValue="false")] + + /** + * Indicates if the level for the event should added to the trace. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var includeLevel:Boolean; + + //---------------------------------- + // includeTime + //---------------------------------- + + [Inspectable(category="General", defaultValue="false")] + + /** + * Indicates if the time should be added to the trace. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var includeTime:Boolean; + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * This method handles a LogEvent from an associated logger. + * A target uses this method to translate the event into the appropriate + * format for transmission, storage, or display. + * This method is called only if the event's level is in range of the + * target's level. + * + * @param event The LogEvent handled by this method. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + override public function logEvent(event:LogEvent):void + { + var date:String = "" + if (includeDate || includeTime) + { + var d:Date = new Date(); + if (includeDate) + { + date = Number(d.getMonth() + 1).toString() + "/" + + d.getDate().toString() + "/" + + d.getFullYear() + fieldSeparator; + } + if (includeTime) + { + date += padTime(d.getHours()) + ":" + + padTime(d.getMinutes()) + ":" + + padTime(d.getSeconds()) + "." + + padTime(d.getMilliseconds(), true) + fieldSeparator; + } + } + + var level:String = ""; + if (includeLevel) + { + level = "[" + LogEvent.getLevelString(event.level) + + "]" + fieldSeparator; + } + + var category:String = includeCategory ? + ILogger(event.target).category + fieldSeparator : + ""; + + internalLog(date + level + category + event.message); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function padTime(num:Number, millis:Boolean = false):String + { + if (millis) + { + if (num < 10) + return "00" + num.toString(); + else if (num < 100) + return "0" + num.toString(); + else + return num.toString(); + } + else + { + return num > 9 ? num.toString() : "0" + num.toString(); + } + } + + /** + * Descendants of this class should override this method to direct the + * specified message to the desired output. + * + * @param message String containing preprocessed log message which may + * include time, date, category, etc. based on property settings, + * such as includeDate, includeCategory, + * etc. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + mx_internal function internalLog(message:String):void + { + // override this method to perform the redirection to the desired output + } +} + +} diff --git a/src/mx/logging/targets/TraceTarget.as b/src/mx/logging/targets/TraceTarget.as new file mode 100644 index 00000000..b7afcbba --- /dev/null +++ b/src/mx/logging/targets/TraceTarget.as @@ -0,0 +1,84 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.logging.targets +{ + +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * Provides a logger target that uses the global trace() method to output log messages. + * + *

To view trace() method output, you must be running the + * debugger version of Flash Player or AIR Debug Launcher.

+ * + *

The debugger version of Flash Player and AIR Debug Launcher send output from the trace() method + * to the flashlog.txt file. The default location of this file is the same directory as + * the mm.cfg file. You can customize the location of this file by using the TraceOutputFileName + * property in the mm.cfg file. You must also set TraceOutputFileEnable to 1 in your mm.cfg file.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class TraceTarget extends LineFormattedTarget +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + *

Constructs an instance of a logger target that will send + * the log data to the global trace() method. + * All output will be directed to flashlog.txt by default.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function TraceTarget() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * This method outputs the specified message directly to + * trace(). + * All output will be directed to flashlog.txt by default. + * + * @param message String containing preprocessed log message which may + * include time, date, category, etc. based on property settings, + * such as includeDate, includeCategory, etc. + */ + override mx_internal function internalLog(message:String):void + { + trace(message); + } +} + +} diff --git a/src/mx/managers/systemClasses/ChildManager.as b/src/mx/managers/systemClasses/ChildManager.as new file mode 100644 index 00000000..bc1f3d75 --- /dev/null +++ b/src/mx/managers/systemClasses/ChildManager.as @@ -0,0 +1,425 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.managers.systemClasses +{ + +import flash.display.DisplayObject; +import flash.display.DisplayObjectContainer; +import flash.display.InteractiveObject; +import flash.events.IEventDispatcher; + +import mx.core.IFlexDisplayObject; +import mx.core.IFlexModule; +import mx.core.IFlexModuleFactory; +import mx.core.IFontContextComponent; +import mx.core.IInvalidating; +import mx.core.IUIComponent; +import mx.core.UIComponent; +import mx.core.mx_internal; +import mx.events.FlexEvent; +import mx.managers.ILayoutManagerClient; +import mx.managers.ISystemManager; +import mx.managers.ISystemManagerChildManager; +import mx.managers.SystemManager; +import mx.messaging.config.LoaderConfig; +import mx.preloaders.Preloader; +import mx.styles.ISimpleStyleClient; +import mx.styles.IStyleClient; +import mx.utils.LoaderUtil; + +use namespace mx_internal; + +[ExcludeClass] + +public class ChildManager //implements ISystemManagerChildManager +{ + include "../../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + + public function ChildManager(systemManager:IFlexModuleFactory) + { + super(); +/* + if (systemManager is ISystemManager) + { + systemManager["childManager"] = this; + this.systemManager = ISystemManager(systemManager); + this.systemManager.registerImplementation("mx.managers::ISystemManagerChildManager", this); + }*/ + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /* + private var systemManager:ISystemManager; + + + //-------------------------------------------------------------------------- + // + // Methods: Child management + // + //-------------------------------------------------------------------------- + + + public function addingChild(child:DisplayObject):void + { + var newNestLevel:int = 1; + + // non-top level system managers may not be able to reference their parent if + // they are a proxy for popups. + if (!topLevel && DisplayObject(systemManager).parent) + { + // non-topLevel SystemManagers are buried by Flash.display.Loader and + // other non-framework layers so we have to figure out the nestlevel + // by searching up the parent chain. + var obj:DisplayObjectContainer = DisplayObject(systemManager).parent.parent; + while (obj) + { + if (obj is ILayoutManagerClient) + { + newNestLevel = ILayoutManagerClient(obj).nestLevel + 1; + break; + } + obj = obj.parent; + } + } + nestLevel = newNestLevel; + + if (child is IUIComponent) + IUIComponent(child).systemManager = systemManager; + + // If the document property isn't already set on the child, + // set it to be the same as this component's document. + // The document setter will recursively set it on any + // descendants of the child that exist. + if (child is IUIComponent && + !IUIComponent(child).document) + { + IUIComponent(child).document = systemManager.document; + } + + // Set the moduleFactory to the child, but don't overwrite an existing moduleFactory. + if (child is IFlexModule && IFlexModule(child).moduleFactory == null) + IFlexModule(child).moduleFactory = systemManager; + + // Set the font context in non-UIComponent children. + // UIComponent children use moduleFactory. + if (child is IFontContextComponent && !child is UIComponent && + IFontContextComponent(child).fontContext == null) + IFontContextComponent(child).fontContext = systemManager; + + // Set the nestLevel of the child to be one greater + // than the nestLevel of this component. + // The nestLevel setter will recursively set it on any + // descendants of the child that exist. + if (child is ILayoutManagerClient) + ILayoutManagerClient(child).nestLevel = nestLevel + 1; + + if (child is InteractiveObject) + if (InteractiveObject(systemManager).doubleClickEnabled) + InteractiveObject(child).doubleClickEnabled = true; + + if (child is IUIComponent) + IUIComponent(child).parentChanged(DisplayObjectContainer(systemManager)); + + // Sets up the inheritingStyles and nonInheritingStyles objects + // and their proto chains so that getStyle() works. + // If this object already has some children, + // then reinitialize the children's proto chains. + if (child is IStyleClient) + IStyleClient(child).regenerateStyleCache(true); + + if (child is ISimpleStyleClient) + ISimpleStyleClient(child).styleChanged(null); + + if (child is IStyleClient) + IStyleClient(child).notifyStyleChangeInChildren(null, true); + + // Need to check to see if the child is an UIComponent + // without actually linking in the UIComponent class. + if (child is UIComponent) + UIComponent(child).initThemeColor(); + + // Inform the component that it's style properties + // have been fully initialized. Most components won't care, + // but some need to react to even this early change. + if (child is UIComponent) + UIComponent(child).stylesInitialized(); + } + + + public function childAdded(child:DisplayObject):void + { + if (child.hasEventListener(FlexEvent.ADD)) + child.dispatchEvent(new FlexEvent(FlexEvent.ADD)); + + if (child is IUIComponent) + IUIComponent(child).initialize(); // calls child.createChildren() + } + + + public function removingChild(child:DisplayObject):void + { + if (child.hasEventListener(FlexEvent.REMOVE)) + child.dispatchEvent(new FlexEvent(FlexEvent.REMOVE)); + } + + + public function childRemoved(child:DisplayObject):void + { + if (child is IUIComponent) + IUIComponent(child).parentChanged(null); + } + + //-------------------------------------------------------------------------- + // + // Methods: Styles + // + //-------------------------------------------------------------------------- + + + public function regenerateStyleCache(recursive:Boolean):void + { + var foundTopLevelWindow:Boolean = false; + + var n:int = systemManager.rawChildren.numChildren; + for (var i:int = 0; i < n; i++) + { + var child:IStyleClient = + systemManager.rawChildren.getChildAt(i) as IStyleClient; + + if (child) + child.regenerateStyleCache(recursive); + + if (isTopLevelWindow(DisplayObject(child))) + foundTopLevelWindow = true; + + // Refetch numChildren because notifyStyleChangedInChildren() + // can add/delete a child and therefore change numChildren. + n = systemManager.rawChildren.numChildren; + } + + // During startup the top level window isn't added + // to the child list until late into the startup sequence. + // Make sure we call regenerateStyleCache() + // on the top level window even if it isn't a child yet. + if (!foundTopLevelWindow && topLevelWindow is IStyleClient) + IStyleClient(topLevelWindow).regenerateStyleCache(recursive); + } + + + public function notifyStyleChangeInChildren(styleProp:String, + recursive:Boolean):void + { + var foundTopLevelWindow:Boolean = false; + + var n:int = systemManager.rawChildren.numChildren; + for (var i:int = 0; i < n; i++) + { + var child:IStyleClient = + systemManager.rawChildren.getChildAt(i) as IStyleClient; + + if (child) + { + child.styleChanged(styleProp); + child.notifyStyleChangeInChildren(styleProp, recursive); + } + + if (isTopLevelWindow(DisplayObject(child))) + foundTopLevelWindow = true; + + // Refetch numChildren because notifyStyleChangedInChildren() + // can add/delete a child and therefore change numChildren. + n = systemManager.rawChildren.numChildren; + } + + // During startup the top level window isn't added + // to the child list until late into the startup sequence. + // Make sure we call notifyStyleChangeInChildren() + // on the top level window even if it isn't a child yet. + if (!foundTopLevelWindow && topLevelWindow is IStyleClient) + { + IStyleClient(topLevelWindow).styleChanged(styleProp); + IStyleClient(topLevelWindow).notifyStyleChangeInChildren( + styleProp, recursive); + } + } + + + public function initializeTopLevelWindow(width:Number, height:Number):void + { +/* + CONFIG::performanceInstrumentation + { + var perfUtil:mx.utils.PerfUtil = mx.utils.PerfUtil.getInstance(); + perfUtil.markTime("ChildManager.initializeTopLevelWindow().start"); + perfUtil.markTime("SystemManager.create().start"); + } +*//* + var app:IUIComponent; + // Create a new instance of the toplevel class + systemManager.document = app = topLevelWindow = IUIComponent(systemManager.create()); +/* + CONFIG::performanceInstrumentation + { + perfUtil.markTime("SystemManager.create().end"); + } +*//* + if (systemManager.document) + { + // Add listener for the creationComplete event + IEventDispatcher(app).addEventListener(FlexEvent.CREATION_COMPLETE, + appCreationCompleteHandler); + + // if somebody has set this in our applicationdomain hierarchy, don't overwrite it + if (!LoaderConfig._url) + { + LoaderConfig._url = LoaderUtil.normalizeURL(systemManager.loaderInfo); + LoaderConfig._parameters = systemManager.loaderInfo.parameters; + LoaderConfig._swfVersion = systemManager.loaderInfo.swfVersion; + } + + IFlexDisplayObject(app).setActualSize(width, height); + + // Wait for the app to finish its initialization sequence + // before doing an addChild(). + // Otherwise, the measurement/layout code will cause the + // player to do a bunch of unnecessary screen repaints, + // which slows application startup time. + + // Pass the application instance to the preloader. + // Note: preloader can be null when the user chooses + // Control > Play in the standalone player. + if (preloader) + preloader.registerApplication(app); + + // The Application doesn't get added to the SystemManager in the standard way. + // We want to recursively create the entire application subtree and process + // it with the LayoutManager before putting the Application on the display list. + // So here we what would normally happen inside an override of addChild(). + // Leter, when we actually attach the Application instance, + // we call super.addChild(), which is the bare player method. + addingChild(DisplayObject(app)); +/* + CONFIG::performanceInstrumentation + { + perfUtil.markTime("Application.createChildren().start"); + } +*//* + childAdded(DisplayObject(app)); // calls app.createChildren() +/* + CONFIG::performanceInstrumentation + { + perfUtil.markTime("Application.createChildren().end"); + } +*//* + } + else + { + systemManager.document = this; + } +/* + CONFIG::performanceInstrumentation + { + perfUtil.markTime("ChildManager.initializeTopLevelWindow().end"); + } +*//* + } + + + private function appCreationCompleteHandler(event:FlexEvent):void + { + if (!topLevel && DisplayObject(systemManager).parent) + { + var obj:DisplayObjectContainer = DisplayObject(systemManager).parent.parent; + while (obj) + { + if (obj is IInvalidating) + { + IInvalidating(obj).invalidateSize(); + IInvalidating(obj).invalidateDisplayList(); + return; + } + obj = obj.parent; + } + } + } + + //-------------------------------------------------------------------------- + // + // Methods to implement SystemManager methods + // + // systemManager may be a SystemManager or WindowedSystemManager + // so we use the array access operertor to get at the methods/properties. + // + //-------------------------------------------------------------------------- + + private function isTopLevelWindow(object:DisplayObject):Boolean + { + return systemManager["isTopLevelWindow"](object); + } + + + private function get topLevel():Boolean + { + return systemManager["topLevel"]; + } + + private function set topLevel(topLevel:Boolean):void + { + systemManager["topLevel"] = topLevel; + } + + private function get topLevelWindow():IUIComponent + { + return systemManager["topLevelWindow"]; + } + + private function set topLevelWindow(window:IUIComponent):void + { + systemManager["topLevelWindow"] = window; + } + + private function get nestLevel():int + { + return systemManager["nestLevel"]; + } + + private function set nestLevel(level:int):void + { + systemManager["nestLevel"] = level; + } + + private function get preloader():Preloader + { + return systemManager["preloader"]; + } + + private function set preloader(preloader:Preloader):void + { + systemManager["preloader"] = preloader; + } */ +} + +} + + diff --git a/src/mx/modules/IModuleInfo.as b/src/mx/modules/IModuleInfo.as new file mode 100644 index 00000000..f0c96dfb --- /dev/null +++ b/src/mx/modules/IModuleInfo.as @@ -0,0 +1,320 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.modules +{ + +import flash.events.IEventDispatcher; +import flash.system.ApplicationDomain; +import flash.system.SecurityDomain; +import flash.utils.ByteArray; +import mx.core.IFlexModuleFactory; + +//-------------------------------------- +// Events +//-------------------------------------- + +/** + * Dispatched by the backing ModuleInfo if there was an error during + * module loading. + * + * @eventType mx.events.ModuleEvent.ERROR + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="error", type="mx.events.ModuleEvent")] + +/** + * Dispatched by the backing ModuleInfo at regular intervals + * while the module is being loaded. + * + * @eventType mx.events.ModuleEvent.PROGRESS + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="progress", type="mx.events.ModuleEvent")] + +/** + * Dispatched by the backing ModuleInfo once the module is sufficiently + * loaded to call the IModuleInfo.factory() method and the + * IFlexModuleFactory.create() method. + * + * @eventType mx.events.ModuleEvent.READY + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="ready", type="mx.events.ModuleEvent")] + +/** + * Dispatched by the backing ModuleInfo once the module is sufficiently + * loaded to call the IModuleInfo.factory() method and + * the IFlexModuleFactory.info() method. + * + * @eventType mx.events.ModuleEvent.SETUP + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="setup", type="mx.events.ModuleEvent")] + +/** + * Dispatched by the backing ModuleInfo when the module data is unloaded. + * + * @eventType mx.events.ModuleEvent.UNLOAD + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="unload", type="mx.events.ModuleEvent")] + +/** + * An interface that acts as a handle for a particular module. + * From this interface, the module status can be queried, + * its inner factory can be obtained, and it can be loaded or unloaded. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IModuleInfo extends IEventDispatcher +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // data + //---------------------------------- + + /** + * User data that can be associated with the singleton IModuleInfo + * for a given URL. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get data():Object; + + /** + * @private + */ + function set data(value:Object):void; + + //---------------------------------- + // error + //---------------------------------- + + /** + * A flag that is true if there was an error + * during module loading. + * + *

This flag is true when the ModuleManager dispatches the + * ModuleEvent.ERROR event.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get error():Boolean; + + //---------------------------------- + // factory + //---------------------------------- + + /** + * The IFlexModuleFactory implementation defined in the module. + * This will only be non-null after the + * ModuleEvent.SETUP event has been dispatched + * (or the IModuleInfo.setup() method returns true). + * At this point, the IFlexModuleFactory.info() method can be called. + * Once a ModuleEvent.READY event is dispatched + * (or the IModuleInfo.ready() method returns true), + * it is possible to call the IFlexModuleFactory.create() method. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get factory():IFlexModuleFactory; + + //---------------------------------- + // loaded + //---------------------------------- + + /** + * A flag that is true if the load() + * method has been called on this module. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get loaded():Boolean; + + //---------------------------------- + // ready + //---------------------------------- + + /** + * A flag that is true if the module is sufficiently loaded + * to get a handle to its associated IFlexModuleFactory implementation + * and call its create() method. + * + *

This flag is true when the ModuleManager dispatches the + * ModuleEvent.READY event.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get ready():Boolean; + + //---------------------------------- + // setup + //---------------------------------- + + /** + * A flag that is true if the module is sufficiently loaded + * to get a handle to its associated IFlexModuleFactory implementation + * and call its info() method. + * + *

This flag is true when the ModuleManager dispatches the + * ModuleEvent.SETUP event.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get setup():Boolean; + + //---------------------------------- + // url + //---------------------------------- + + /** + * The URL associated with this module (for example, "MyImageModule.swf" or + * "http://somedomain.com/modules/MyImageModule.swf". The URL can be local or remote, but + * if it is remote, you must establish a trust between the module's domain and the + * application that loads it. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get url():String; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Requests that the module be loaded. If the module is already loaded, + * the call does nothing. Otherwise, the module begins loading and dispatches + * progress events as loading proceeds. + * + * @param applicationDomain The current application domain in which your code is executing. + * + * @param securityDomain The current security "sandbox". + * + * @param bytes A ByteArray object. The ByteArray is expected to contain + * the bytes of a SWF file that represents a compiled Module. The ByteArray + * object can be obtained by using the URLLoader class. If this parameter + * is specified the module will be loaded from the ByteArray. If this + * parameter is null the module will be loaded from the url specified in + * the url property. + * + * @param moduleFactory The moduleFactory of the caller. One use of the + * moduleFactory is to determine the parent style manager of the loaded + * module. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function load(applicationDomain:ApplicationDomain = null, + securityDomain:SecurityDomain = null, + bytes:ByteArray = null, + moduleFactory:IFlexModuleFactory = null):void; + + /** + * Releases the current reference to the module. + * This does not unload the module unless there are no other + * open references to it and the ModuleManager is set up + * to have only a limited number of loaded modules. + * + * @see mx.modules.ModuleManager + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function release():void; + + /** + * Unloads the module. + * Flash Player and AIR will not fully unload and garbage collect this module if + * there are any outstanding references to definitions inside the + * module. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function unload():void; + + /** + * Publishes an interface to the ModuleManager. This allows late (or decoupled) + * subscriptions to factories with a String handle. Use a URL that starts with + * publish:// to reference factories that are published in this manner. + * + * @param factory The class that implements the module's IFlexModuleFactory interface. + * + * @see mx.modules.ModuleManager + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function publish(factory:IFlexModuleFactory):void; + +} + +} diff --git a/src/mx/mxml/FlexReferences.as b/src/mx/mxml/FlexReferences.as new file mode 100644 index 00000000..17fe8fb6 --- /dev/null +++ b/src/mx/mxml/FlexReferences.as @@ -0,0 +1,65 @@ +package mx.mxml +{ + /* + import mx.binding.ArrayElementWatcher; + import mx.binding.BindingManager; + import mx.binding.FunctionReturnWatcher; + import mx.binding.RepeaterComponentWatcher; + import mx.binding.RepeaterItemWatcher; + import mx.binding.StaticPropertyWatcher; + import mx.binding.XMLWatcher; + import mx.core.BitmapAsset; + import mx.core.ClassFactory; + import mx.core.DeferredInstanceFromClass; + import mx.core.FontAsset; + import mx.core.IStateClient2; + import mx.filters.IBitmapFilter; + import mx.states.AddItems; + import mx.states.IOverride; + import mx.states.SetProperty; + import mx.states.SetStyle; + import mx.states.State; + import mx.styles.CSSStyleDeclaration; + */ + /** + * Pulls various Flex classes into the Reflex swc. + * These classes are not referenced by Reflex and are not required to build Reflex in Flash Pro or AS3 projects. + * They will not be pulled into you swf unless you use MXML. When using MXML (with MXMLC) these classes are sometimes + * pulled into projects automatically by the compiler. We make them available to the compiler here so that project setup does + * not require any outside Flex SDK references. + */ + public class FlexReferences + { + /* + // binding + static private var bm:BindingManager; + static private var aw:ArrayElementWatcher; + static private var frw:FunctionReturnWatcher; + static private var rcw:RepeaterComponentWatcher; + static private var riw:RepeaterItemWatcher; + static private var spw:StaticPropertyWatcher; + static private var xw:XMLWatcher; + + // core + static private var cf:ClassFactory; + static private var fa:FontAsset; + static private var ba:BitmapAsset; + + // states & styles + static private var sc2:mx.core.IStateClient2; + static private var csd:CSSStyleDeclaration; + + static private var bf:IBitmapFilter; + static private var difc:DeferredInstanceFromClass; + */ + /* + // + static private var ai:AddItems; + static private var io:IOverride; + static private var sp:SetProperty; + static private var ss:SetStyle; + static private var st:State; + */ + + } +} \ No newline at end of file diff --git a/src/mx/resources/IResourceBundle.as b/src/mx/resources/IResourceBundle.as new file mode 100644 index 00000000..b3dadb93 --- /dev/null +++ b/src/mx/resources/IResourceBundle.as @@ -0,0 +1,248 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.resources +{ + +/** + * The IResourceBundle and IResourceManager interfaces work together + * to provide localization support for Flex applications. + * + *

There are three main concepts involved in localization: + * locales, resources, and resource bundles.

+ * + *

A locale specifies a language and a country + * for which your application has been localized. + * For example, the locale "en_US" + * specifies English as spoken in the United States. + * (See the mx.resources.Locale class for more information.)

+ * + *

A resource is a named value that is locale-dependent. + * For example, your application might have a resource + * whose name is "OPEN" + * and whose value for an English locale is "Open" + * but whose value for a French locale is "Ouvrir".

+ * + *

A resource bundle is a named group of resources + * whose values have been localized for a particular locale. + * A resource bundle is identified by the combination of its + * bundleName and its locale, + * and has a content Object that contains + * the name-value pairs for the bundle's resources.

+ * + *

The IResourceBundle interface represents a specific resource bundle. + * However, most applications will only need to use IResourceManager. + * A single ResourceManager object implementing this interface + * manages multiple resource bundles, possibly for multiple locales, + * and provides access to the resources that they contain. + * For example, you can retrieve a specific resource as a String by calling + * resourceManager.getString(bundleName, resourceName). + * By changing the localeChain property of the ResourceManager, + * you can change which resource bundles are searched for resource values.

+ * + *

Generally, you do not create resource bundles yourself; + * instead, they are usually compiled from ~~.properties files. + * A properties file named MyResources.properties + * produces a resource bundle with "MyResources" + * for its bundleName. + * You generally produce multiple versions of each properties file, + * one for each locale that your application supports.

+ * + *

Flex properties files are similar to Java properties files, + * except that they also support MXML's Embed() + * and ClassReference() directives. + * These directives work the same way in a properties file + * as they do in a CSS file, producing class references. + * Also, the encoding for Flex properties files + * is always assumed to be UTF-8.

+ * + *

The Flex framework's resources have been localized + * for U.S. English (the "en_US" locale) and + * for Japanese (the "ja_JP" locale). + * The framework resources are organized into multiple bundles + * corresponding to framework packages; for example, the "formatters" + * bundle is used by classes in the mx.formatters package. + * (There is also a "SharedResources" bundle for resources used by + * multiple packages.)

+ * + *

The properties files for the framework resources, + * such as formatters.properties, can be found in the + * frameworks/projects/framework/bundles/{locale}/src directories + * of the Flex SDK. + * Your applications normally link against the Flex framework + * as a precompiled library, framework.swc, + * in the frameworks/libs directory. + * This library has no resources in it. + * Instead, the framework resources have been compiled into separate + * resource bundle libraries such as framework_rb.swc. + * These are located in the frameworks/locales/{locale} directories + * and your application must also link in one or more of these.

+ * + *

You are free to organize your application's own resources + * into whatever bundles you find convenient. + * If you localize your application for locales + * other than "en_US" and "ja_JP", + * you should localize the framework's properties files for those locales + * as well and compile additional resource bundle libaries for them.

+ * + *

When your application starts, the ResourceManager is automatically + * populated with whatever resource bundles were compiled + * into the application. + * If you create a code module, by default the resources that its classes + * need are compiled into the module. + * When the module is loaded into an application, any bundles that the + * application does not already have are added to the ResourceManager.

+ * + *

You can compile "resource modules" that have only resources in them, + * and load them with the loadResourceModule() method + * of the ResourceManager. + * With resource modules, you can support multiple locales by loading + * the resources you need at run time rather than compiling them into + * your application.

+ * + *

Although the ResourceManager is normally populated with resource bundles + * that were compiled into your application or loaded from modules, + * you can also programmatically create resource bundles and add them + * to the ResourceManager yourself with the addResourceBundle() + * method.

+ * + * @see mx.resources.ResourceBundle + * @see mx.resources.IResourceManager + * @see mx.resources.ResourceManager + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IResourceBundle +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // bundleName + //---------------------------------- + + /** + * A name that identifies this resource bundle, + * such as "MyResources". + * + *

This read-only property is set + * when a resource bundle is constructed.

+ * + *

Resource bundles that are automatically created from compiled + * properties files have bundle names based on the names of those files. + * For example, a properties file named MyResources.properties + * will produce a resource bundle whose bundleName + * is "MyResources".

+ * + *

The ResourceManager can manage multiple bundles with the same + * bundleName as long as they have different values + * for their locale property.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get bundleName():String; + + //---------------------------------- + // content + //---------------------------------- + + /** + * An object containing key-value pairs for the resources + * in this resource bundle. + * + *

In general, you should access resources by using IResourceManager + * methods such as getString(), rather than directly + * accessing them in a resource bundle. + * However, if you are programmatically creating your own + * resource bundles, you can initialize them with resources, + * as follows:

+ * + *
+     *  var rb:IResourceBundle = new ResourceBundle("fr_FR", "MyResources");
+     *  rb.content["LANGUAGE"] = "Francais";
+     *  rb.content["GREETING"] = "Bonjour";
+     *  
+ * + *

When a resource bundle is produced by compiling a properties + * file, its resource values are either of type String or Class. + * For example, if the properties file contains

+ * + *
+     *  LANGUAGE=English
+     *  MINIMUM_AGE=18
+     *  ENABLED=true
+     *  LOGO=Embed("logo.png")
+     *  
+ * + *

then the value of the LANGUAGE resource + * is the String "English", + * the value of the MINIMUM_AGE resource + * is the String "18", + * the value of the ENABLED resource + * is the String "true", + * and the value of the LOGO resource + * is a Class that represents the embedded PNG file.

+ * + *

You can use IResourceManager methods such as getInt() + * and getBoolean() to convert resource strings like + * "18" and "true" into the type + * that your code expects.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get content():Object; + + //---------------------------------- + // locale + //---------------------------------- + + /** + * The locale for which this bundle's resources have been localized. + * This is a String such as "en_US" for U.S. English. + * + *

This read-only property is set + * when a resource bundle is constructed.

+ * + *

Resource bundles that are automatically created from compiled + * properties files have locales based on the + * -compiler.locale option of the mxmlc or compc compilers. + * For example, suppose that you compile your application with the option + * -compiler.locale=en_US,ja_JP and that you have specified + * -compiler.source-path=resources/{locale} so that + * your application's resources, located in + * resources/en_US/MyResources.properties and + * resources/ja_JP/MyResources.properties, are found. + * Then your application will have two resource bundles + * whose bundleName is "MyResources", + * one whose locale is "en_US" + * and one whose locale is "ja_JP".

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get locale():String; +} + +} diff --git a/src/mx/resources/IResourceManager.as b/src/mx/resources/IResourceManager.as new file mode 100644 index 00000000..331dc4c4 --- /dev/null +++ b/src/mx/resources/IResourceManager.as @@ -0,0 +1,818 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.resources +{ + +import flash.events.IEventDispatcher; +import flash.system.ApplicationDomain; +import flash.system.SecurityDomain; + +/** + * The APIs of the IResourceManager interface + * provide localization support for Flex applications. + * + *

There are three main concepts involved in localization: + * locales, resources, and resource bundles.

+ * + *

A locale specifies a language and a country + * for which your application has been localized. + * For example, the locale "en_US" + * specifies English as spoken in the United States. + * (See the mx.resources.Locale class for more information.)

+ * + *

A resource is a named value that is locale-dependent. + * For example, your application might have a resource + * whose name is "OPEN" + * and whose value for an English locale is "Open" + * but whose value for a French locale is "Ouvrir".

+ * + *

A resource bundle is a named group of resources + * whose values have been localized for a particular locale. + * A resource bundle is identified by the combination of its + * bundleName and its locale, + * and has a content object that contains + * the name-value pairs for the bundle's resources. + * See the documentation for mx.resources.IResourceBundle + * for information about how you typically create resource + * bundles from properties files.

+ * + *

A single ResourceManager object implementing the IResourceManager + * interface manages multiple resource bundles, possibly for multiple + * locales, and provides access to the resources that they contain. + * For example, you can retrieve a specific resource as a String by calling + * resourceManager.getString(bundleName, resourceName).

+ * + *

All classes that extend UIComponent, Formatter, or Validator + * have a resourceManager property + * that provides a reference to the object implementing this interface. + * Other classes can call ResourceManager.getInstance() + * to obtain this object.

+ * + *

Resource retrieval methods such as getString() + * search for resources in the locales specified + * by the localeChain property. + * By changing this property, you can make your application + * suddenly use, for example, Japanese rather than English resources.

+ * + *

When your application starts, the ResourceManager is automatically + * populated with whatever resource bundles were compiled + * into the application. + * If you create a code module, by default the resources that its classes + * need are compiled into the module. + * When the module is loaded into an application, any bundles that the + * application does not already have are added to the ResourceManager.

+ * + *

You can compile "resource modules" which have only resources in them, + * and load them with the loadResourceModule() method + * of IResourceManager. + * With resource modules, you can support multiple locales by loading + * the resources you need at run time rather than compiling them into + * your application.

+ * + *

Although the ResourceManager is normally populated with resource bundles + * that were compiled into your application or loaded from modules, + * you can also programmatically create resource bundles and add them + * to the ResourceManager yourself with the addResourceBundle() + * method.

+ * + * @see mx.resources.ResourceManager + * @see mx.resources.IResourceBundle + * @see mx.resources.ResourceBundle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IResourceManager extends IEventDispatcher +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // localeChain + //---------------------------------- + + /** + * An Array of locale Strings, such as [ "en_US" ], + * which specifies one or more locales to be searched for resources. + * + *

When you call the ResourceManager methods getObject(), + * getString(), getStringArray(), + * getNumber(), getInt(), + * getUint(), getBoolean(), or + * getClass() to get the value of a resource, + * you specify a bundle name and a resource name, + * but not a locale. + * The ResourceManager starts with the first locale in the + * localeChain and looks for a ResourceBundle + * with the specified bundle name for that locale. + * If such a ResourceBundle exists, and the specified resource + * exists in it, then the value of that resource is returned. + * Otherwise, the ResourceManager proceeds on to the other + * locales in the localeChain.

+ * + *

This scheme makes it possible to have locales that do not + * necessarily contain a complete set of localized resources. + * For example, if you are localizing your application for + * Indian English rather than U.S. English, you need only + * supply resources for the en_IN locale in which the + * Indian spelling or usage differs from that in the U.S., + * and then set the localeChain property + * to [ "en_IN", "en_US" ].

+ * + *

Many framework classes assume that they can always + * obtain, from some locale, the resources that they expect, + * and they will throw errors if they cannot do so. + * Therefore, you must ensure that the localeChain + * always contains a complete set of resources. + * Unless you have done a complete localization of all the + * framework's resources as well as your own application's + * resources, you can keep the "en_US" locale + * at the end of your localeChain to ensure this.

+ * + *

Setting this property causes the ResourceManager to dispatch + * a "change" Event.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get localeChain():Array /* of String */; + + /** + * @private + */ + function set localeChain(value:Array /* of String */):void; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Begins loading a resource module containing resource bundles. + * + *

Each call to this method returns a new event-dispatching object + * that you can use to learn how the loading is progressing + * and whether it completes successfully or results in an error. + * This object dispatches ResourceEvent.PROGRESS, + * ResourceEvent.COMPLETE, and + * ResourceEvent.ERROR events.

+ * + *

When the module has been loaded, the resource bundles + * are added to the ResourceManager, but the localeChain + * is left unchanged. + * If the update parameter is true, + * the update() method will be called.

+ * + * @param url The URL from which to load the resource module. + * + * @param update Whether to call + * the update() method when the module finishes loading. + * + * @param applicationDomain The ApplicationDomain passed to the + * load() method of the IModuleInfo class + * that loads the resource module. + * This parameter is optional and defaults to null. + * + * @param securityDomain The SecurityDomain passed to the + * load() method of the IModuleInfo class + * that loads the resource module. + * This parameter is optional and defaults to null. + * + * @return An object that is associated with this particular load operation + * that dispatches ResourceEvent.PROGRESS, + * ResourceEvent.COMPLETE, and + * ResourceEvent.ERROR events. + * + * @see mx.events.ResourceEvent + * @see mx.resources.IResourceManager#update() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function loadResourceModule(url:String, update:Boolean = true, + applicationDomain:ApplicationDomain = null, + securityDomain:SecurityDomain = null): + IEventDispatcher; + + /** + * Begins unloading a loaded resource module. + * + *

When the module is unloaded, its resource bundles + * are removed from the ResourceManager, but the localeChain + * is left unchanged. + * If the update parameter is true, + * the update() method will be called.

+ * + * @param url The URL that was used to load the resource module. + * + * @param update Whether to call + * the update() method when the module finishes unloading. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function unloadResourceModule(url:String, update:Boolean = true):void; + + /** + * Adds the specified ResourceBundle to the ResourceManager + * so that its resources can be accessed by ResourceManager + * methods such as getString(). + * + * @param resourceBundle The resource bundle to be added. + * @param useWeakReference Determines if the ResourceManager + * keeps a weak reference to the resource bundle. + * If useWeakReference is true then the ResourceManager + * provides a weak reference to the resource bundle. When the + * caller chooses to use a weak reference it becomes the + * caller's responsibility to keep a hard reference the resource bundle + * so it is not garbaged collected prematurely. If useWeakReference is + * false, the ResourceManager keeps a hard reference to the resource + * bundle so it will not be garbage collected. + * + *

When a Flex sub-application or module automatically adds its compiled + * resource bundles to the ResourceManager, it calls the addResourceBundle() + * with useWeakReference set to true, to avoid becoming pinned in memory. + * If you create resource bundles at runtime in a sub-application or + * module, you should do the same. You then need to hold on to these + * resource bundles with a hard reference to prevent them from being + * garbage collected.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function addResourceBundle(resourceBundle:IResourceBundle, + useWeakReference:Boolean = false):void; + + /** + * Removes the specified ResourceBundle from the ResourceManager + * so that its resources can no longer be accessed by ResourceManager + * methods such as getString(). + * + * @param locale A locale string such as "en_US". + * + * @param bundleName A bundle name such as "MyResources". + * + * @see mx.resources.IResourceBundle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function removeResourceBundle(locale:String, bundleName:String):void; + + /** + * Removes all ResourceBundles for the specified locale + * from the ResourceManager so that their resources + * can no longer be accessed by ResourceManager methods + * such as getString(). + * + * @param locale A locale string such as "en_US". + * + * @see mx.resources.IResourceBundle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function removeResourceBundlesForLocale(locale:String):void; + + /** + * Dispatches a change event from the + * ResourceManager. + * + *

This causes binding expressions to re-evaluate + * if they involve the ResourceManager methods + * getObject(), getString(), + * getStringArray(), getNumber(), + * getInt(), getUint(), + * getBoolean(), or getClass().

+ * + *

This also causes the resourcesChanged() method + * of a UIComponent, Formatter, or Validator to execute. + * Many components implement this method to update + * their state based on the latest resources.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function update():void; + + /** + * Returns an Array of Strings specifying all locales for which + * ResourceBundle objects exist in the ResourceManager. + * + *

The order of locales in this array is not specified.

+ * + * @return An Array of locale Strings. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getLocales():Array /* of String */; + + /** + * Returns an Array of Strings specifying all locales for which + * ResourceBundle objects exist in the ResourceManager, + * ordered using user preferences as reported by + * Capabilities.language or + * Capabilities.languages. + * + * @return An Array of locale Strings. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getPreferredLocaleChain():Array /* of String */; + + /** + * Returns an Array of Strings specifying the bundle names + * for all ResourceBundle objects that exist in the ResourceManager + * and that belong to the specified locale. + * + *

The order of bundle names in this Array is not specified.

+ * + * @param locale A locale string such as "en_US". + * + * @return An Array of bundle names. + * + * @see mx.resources.IResourceBundle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getBundleNamesForLocale(locale:String):Array /* of String */; + + /** + * Returns a ResourceBundle with the specified locale + * and bundleName that has been previously added + * to the ResourceManager with addResourceBundle(). + * If no such ResourceBundle exists, this method returns null. + * + * @param locale A locale string such as "en_US". + * + * @param bundleName A bundle name such as "MyResources". + * + * @return The ResourceBundle with the specified locale + * and bundleName if one exists; otherwise null. + * + * @see mx.resources.IResourceBundle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getResourceBundle(locale:String, + bundleName:String):IResourceBundle; + + /** + * Searches the locales in the localeChain + * for the specified resource and returns + * the first resource bundle in which it is found. + * If the resource isn't found, this method returns null. + * + * @param bundleName A bundle name such as "MyResources". + * + * @param resourceName The name of a resource in the resource bundle. + * + * @return The first ResourceBundle in the localeChain + * that contains the specified resource, or null. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function findResourceBundleWithResource( + bundleName:String, + resourceName:String):IResourceBundle; + + [Bindable("change")] + + /** + * Gets the value of a specified resource as an Object. + * + *

The value is returned exactly as it is stored + * in the content Object of the ResourceBundle, + * with no conversion. + * If the resource was compiled from a properties files, + * the resource value in the content Object + * is always a String unless you used the Embed() + * or ClassReference() directive, in which case + * it is a Class. + * Use the getString(), getStringArray(), + * getNumber(), getInt() + * getUint(), getBoolean(), and + * getClass() methods to convert the value + * to more specific types.

+ * + *

If the specified resource is not found, + * this method returns undefined.

+ * + * @param bundleName The name of a resource bundle. + * + * @param resourceName The name of a resource in the resource bundle. + * + * @param locale A specific locale to be used for the lookup, + * or null to search all locales + * in the localeChain. + * This parameter is optional and defaults to null; + * you should seldom need to specify it. + * + * @return The resource value, exactly as it is stored + * in the content Object, + * or undefined if the resource is not found. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getObject(bundleName:String, resourceName:String, + locale:String = null):*; + + [Bindable("change")] + + /** + * Gets the value of a specified resource as a String, + * after substituting specified values for placeholders. + * + *

This method calls getObject() + * and then casts the result to a String.

+ * + *

If a parameters Array is passed to this method, + * the parameters in it are converted to Strings + * and then substituted, in order, for the placeholders + * "{0}", "{1}", and so on, in the String + * before it is returned.

+ * + *

If the specified resource is not found, + * this method returns null.

+ * + * @param bundleName The name of a resource bundle. + * + * @param resourceName The name of a resource in the resource bundle. + * + * @param parameters An Array of parameters that are + * substituted for the placeholders. + * Each parameter is converted to a String with the toString() method + * before being substituted. + * + * @param locale A specific locale to be used for the lookup, + * or null to search all locales + * in the localeChain. + * This parameter is optional and defaults to null; + * you should seldom need to specify it. + * + * @return The resource value, as a String, + * or null if it is not found. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getString(bundleName:String, resourceName:String, + parameters:Array = null, + locale:String = null):String; + + [Bindable("change")] + + /** + * Gets the value of a specified resource as an Array of Strings. + * + *

This method assumes that the resource value is a String + * containing a comma-separated list of items. + * It calls the getString() method, splits the String + * into items at the commas, and trims white space + * before and after each item. + * It is useful if you have written a line such as:

+ * + *
+     *  COUNTRIES=India, China, Japan
+     *  
+ * + *

in a properties file and you want to obtain the value + * [ "India", "China", "Japan" ] + * rather than the value "India, China, Japan".

+ * + *

If the specified resource is not found, + * this method returns null.

+ * + * @param bundleName The name of a resource bundle. + * + * @param resourceName The name of a resource in the resource bundle. + * + * @param locale A specific locale to be used for the lookup, + * or null to search all locales + * in the localeChain. + * This parameter is optional and defaults to null; + * you should seldom need to specify it. + * + * @return The resource value, as an Array of Strings, + * or null if it is not found. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getStringArray(bundleName:String, + resourceName:String, + locale:String = null):Array /* of String */; + + [Bindable("change")] + + /** + * Gets the value of a specified resource as a Number. + * + *

This method calls getObject() + * and casts the result to a Number. + * It is useful if you have written a line such as:

+ * + *
+     *  LONGITUDE=170.3
+     *  
+ * + *

in a properties file and want to obtain the value + * 170.3 rather than "170.3".

+ * + *

If the specified resource is not found, + * this method returns NaN.

+ * + * @param bundleName The name of a resource bundle. + * + * @param resourceName The name of a resource in the resource bundle. + * + * @param locale A specific locale to be used for the lookup, + * or null to search all locales + * in the localeChain. + * This parameter is optional and defaults to null; + * you should seldom need to specify it. + * + * @return The resource value, as a Number, + * or NaN if it is not found. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getNumber(bundleName:String, resourceName:String, + locale:String = null):Number; + + [Bindable("change")] + + /** + * Gets the value of a specified resource as an int. + * + *

This method calls getObject() + * and casts the result to an int. + * It is useful if you have written a line such as:

+ * + *
+     *  MINIMUM=5
+     *  
+ * + *

in a properties file and want to obtain the value + * 5 rather than "5".

+ * + *

If the specified resource is not found, + * this method returns 0.

+ * + * @param bundleName The name of a resource bundle. + * + * @param resourceName The name of a resource in the resource bundle. + * + * @param locale A specific locale to be used for the lookup, + * or null to search all locales + * in the localeChain. + * This parameter is optional and defaults to null; + * you should seldom need to specify it. + * + * @return The resource value, as an int, + * or 0 if it is not found. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getInt(bundleName:String, resourceName:String, + locale:String = null):int; + + [Bindable("change")] + + /** + * Gets the value of a specified resource as a uint. + * + *

This method calls the getObject() method + * and casts the result to a uint. + * It is useful if you have written a line such as:

+ * + *
+     *  MINIMUM=5
+     *  
+ * + *

in a properties file and want to obtain the value + * 5 rather than "5".

+ * + *

If the specified resource is not found, + * this method returns 0.

+ * + * @param bundleName The name of a resource bundle. + * + * @param resourceName The name of a resource in the resource bundle. + * + * @param locale A specific locale to be used for the lookup, + * or null to search all locales + * in the localeChain. + * This parameter is optional and defaults to null; + * you should seldom need to specify it. + * + * @return The resource value, as a uint, + * or 0 if it is not found. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getUint(bundleName:String, resourceName:String, + locale:String = null):uint; + + [Bindable("change")] + + /** + * Gets the value of a specified resource as a Boolean. + * + *

This method first calls getString() + * and converts the result to lowercase. + * It then returns true + * if the result was "true". + * and false otherwise.

+ * + *

If the specified resource is not found, + * this method returns false.

+ * + * @param bundleName The name of a resource bundle. + * + * @param resourceName The name of a resource in the resource bundle. + * + * @param locale A specific locale to be used for the lookup, + * or null to search all locales + * in the localeChain. + * This parameter is optional and defaults to null; + * you should seldom need to specify it. + * + * @return The resource value, as a Boolean, + * or false if it is not found. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getBoolean(bundleName:String, resourceName:String, + locale:String = null):Boolean; + + [Bindable("change")] + + /** + * Gets the value of a specified resource as a Class. + * + *

This method calls getObject() + * and coerces it to type Class using the as operator. + * The result will be null if the resource value + * was not a class reference. + * It is useful if you have written a lines such as

+ * + *
+     *  IMAGE=Embed("image.jpg")
+     *  BUTTON_SKIN=ClassReference("skins.ButtonSkin_en_US")
+     *  
+ * + *

in a properties file and want to obtain + * the Class that the Embed() + * or ClassReference() directive produced.

+ * + *

If the specified resource is not found, + * this method returns null.

+ * + * @param bundleName The name of a resource bundle. + * + * @param resourceName The name of a resource in the resource bundle. + * + * @param locale A specific locale to be used for the lookup, + * or null to search all locales + * in the localeChain. + * This parameter is optional and defaults to null; + * you should seldom need to specify it. + * + * @return The resource value, as a Class, + * or null if it is not found. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getClass(bundleName:String, resourceName:String, + locale:String = null):Class; + + /** + * Creates instances of all ResourceBundle subclasses that were compiled into the SWF + * and adds them to the ResourceManager. + * + *

For example, if the locales parameter is [ "en_US", "ja_JP" ] + * and the bundleNames parameter is [ "core", "controls" ], + * then four resource bundles will be installed.

+ * + *

This method is used only by classes that implement the IFlexModuleFactory interface.

+ * + * @param applicationDomain The ApplicationDomain that is used to look up the resource bundle + * classes by name. + * + * @param locales An Array of Strings that specify the locales for which the SWF was compiled. + * + * @param bundleNames An Array of Strings that specify the names of the resource bundles + * that were compiled into the SWF. + * + * @param useWeakReference A flag that specifyies whether the resource bundles should be + * intalled into the ResourceManager using a weak reference. + * + * @return An Array of the ResourceBundle instances that were created + * and added to the ResourceManager. + * + * @see mx.core.IFlexModuleFactory + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function installCompiledResourceBundles( + applicationDomain:ApplicationDomain, + locales:Array /* of String */, + bundleNames:Array /* of String */, + useWeakReference:Boolean = false):Array; + + /** + * Initializes the localeChain property of the ResourceManager + * using an algorithm that compares the operating system's list of user-preferred + * locales with the list of locales available in the SWF. + * + *

For example, if the user has indicated in the operating system that she + * prefers French, and the SWF was compiled for the locales en_US, fr_FR, and de_DE, + * then the localeChain will be set so that the first locale in it + * is fr_FR.

+ * + *

This method is used only by classes that implement the IFlexModuleFactory interface.

+ * + * @param compiledLocales An Array of Strings specifying the locales + * for which the SWF was compiled. + * + * @see mx.core.IFlexModuleFactory + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function initializeLocaleChain(compiledLocales:Array):void; +} + +} diff --git a/src/mx/resources/IResourceModule.as b/src/mx/resources/IResourceModule.as new file mode 100644 index 00000000..5c993950 --- /dev/null +++ b/src/mx/resources/IResourceModule.as @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.resources +{ + +[ExcludeClass] + +/** + * @private + * When the MXML compiler compiles a resource module, the class + * that it autogenerates to represent the module implements this interface. + */ +public interface IResourceModule +{ + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * An Array of ResourceBundle instances, containing one for each + * of the resource bundle classes in this resource module. + * + *

The order of ResourceBundle instances in this Array + * is not specified.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get resourceBundles():Array /* of ResourceBundle */; +} + +} diff --git a/src/mx/resources/Locale.as b/src/mx/resources/Locale.as new file mode 100644 index 00000000..604ab9c5 --- /dev/null +++ b/src/mx/resources/Locale.as @@ -0,0 +1,228 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.resources +{ + +import mx.managers.ISystemManager; + +/** + * The Locale class can be used to parse a locale String such as "en_US_MAC" + * into its three parts: a language code, a country code, and a variant. + * + *

The localization APIs in the IResourceManager and IResourceBundle + * interfaces use locale Strings rather than Locale instances, + * so this class is seldom used in an application.

+ * + * @see mx.resources.IResourceBundle + * @see mx.resources.IResourceManager + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class Locale +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private static var currentLocale:Locale; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param localeString A 1-, 2-, or 3-part locale String, + * such as "en", "en_US", or "en_US_MAC". + * The parts are separated by underscore characters. + * The first part is a two-letter lowercase language code + * as defined by ISO-639, such as "en" for English. + * The second part is a two-letter uppercase country code + * as defined by ISO-3166, such as "US" for the United States. + * The third part is a variant String, which can be used + * to optionally distinguish multiple locales for the same language and country. + * It is sometimes used to indicate the operating system + * that the locale should be used with, such as "MAC", "WIN", or "UNIX". + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function Locale(localeString:String) + { + super(); + + this.localeString = localeString; + + var parts:Array = localeString.split("_"); + + if (parts.length > 0) + _language = parts[0]; + + if (parts.length > 1) + _country = parts[1]; + + if (parts.length > 2) + _variant = parts.slice(2).join("_"); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var localeString:String; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // language + //---------------------------------- + + /** + * @private + * Storage for the language property. + */ + private var _language:String; + + [Inspectable(category="General", defaultValue="null")] + + /** + * The language code of this Locale instance. [Read-Only] + * + *
+     *  var locale:Locale = new Locale("en_US_MAC");
+     *  trace(locale.language); // outputs "en"
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get language():String + { + return _language; + } + + //---------------------------------- + // country + //---------------------------------- + + /** + * @private + * Storage for the country property. + */ + private var _country:String; + + [Inspectable(category="General", defaultValue="null")] + + /** + * The country code of this Locale instance. [Read-Only] + * + *
+     *  var locale:Locale = new Locale("en_US_MAC");
+     *  trace(locale.country); // outputs "US"
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get country():String + { + return _country + } + + //---------------------------------- + // variant + //---------------------------------- + + /** + * @private + * Storage for the variant property. + */ + private var _variant:String; + + [Inspectable(category="General", defaultValue="null")] + + /** + * The variant part of this Locale instance. [Read-Only] + * + *
+     *  var locale:Locale = new Locale("en_US_MAC");
+     *  trace(locale.variant); // outputs "MAC"
+     *  
+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get variant():String + { + return _variant; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Returns the locale String that was used to construct + * this Locale instance. For example: + * + *
+     *  var locale:Locale = new Locale("en_US_MAC");
+     *  trace(locale.toString()); // outputs "en_US_MAC"
+     *  
+ * + * @return Returns the locale String that was used to + * construct this Locale instance. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function toString():String + { + return localeString; + } +} + +} diff --git a/src/mx/resources/LocaleSorter.as b/src/mx/resources/LocaleSorter.as new file mode 100644 index 00000000..297c6c56 --- /dev/null +++ b/src/mx/resources/LocaleSorter.as @@ -0,0 +1,1040 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.resources +{ + +[ExcludeClass] + +/** + * @private + * The APIs of the LocaleSorter class provides the sorting functionality + * of application locales against system preferences. + */ +public class LocaleSorter +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Sorts a list of locales using the order specified + * by the user preferences. + * + * @param appLocales An Array of locales supported by the application. + * + * @param systemPreferences The locale chain of user-preferred locales. + * + * @param ultimateFallbackLocale The ultimate fallback locale + * that will be used when no locale from systemPreference matches + * a locale from application supported locale list. + * + * @param addAll When true, adds all the non-matching locales + * at the end of the result list preserving the given order. + * + * @return A locale chain that matches user preferences order. + */ + public static function sortLocalesByPreference( + appLocales:Array, systemPreferences:Array, + ultimateFallbackLocale:String = null, + addAll:Boolean = false):Array + { + var result:Array = []; + + var hasLocale:Object = {}; + + var i:int; + var j:int; + var k:int; + var l:int; + var locale:String; + + var locales:Array = trimAndNormalize(appLocales); + var preferenceLocales:Array = trimAndNormalize(systemPreferences); + + addUltimateFallbackLocale(preferenceLocales, ultimateFallbackLocale); + + // For better performance, save the locales in a lookup table. + for (j = 0; j < locales.length; j++) + { + hasLocale[locales[j]] = j; + } + + function promote(locale:String):void + { + if (typeof hasLocale[locale] != "undefined") + { + result.push(appLocales[hasLocale[locale]]); + delete hasLocale[locale]; + } + } + + for (i = 0, l = preferenceLocales.length; i < l; i++) + { + var plocale:LocaleID = LocaleID.fromString(preferenceLocales[i]); + + // Step 1: Promote the perfect match. + promote(preferenceLocales[i]); + + promote(plocale.toString()); + + // Step 2: Promote the parent chain. + while (plocale.transformToParent()) + { + promote(plocale.toString()); + } + + // Parse it again. + plocale = LocaleID.fromString(preferenceLocales[i]); + + // Step 3: Promote the order of siblings from preferenceLocales. + for (j = 0; j < l; j++) + { + locale = preferenceLocales[j]; + if (plocale.isSiblingOf(LocaleID.fromString(locale))) + promote(locale); + } + + // Step 4: Promote all remaining siblings + // (aka not in preferenceLocales) + // eg. push en_UK after en_US and en if the user + // doesn't have en_UK in the preference list + for (j = 0, k = locales.length; j < k; j++) + { + locale = locales[j]; + if (plocale.isSiblingOf(LocaleID.fromString(locale))) + promote(locale); + } + } + + if (addAll) + { + // Check what locales are not already loaded + // and push them preserving the order. + for (j = 0, k = locales.length; j < k; j++) + { + promote(locales[j]); + } + } + + return result; + } + + /** + * @private + */ + private static function trimAndNormalize(list:Array):Array + { + var resultList:Array = []; + + for (var i:int = 0; i < list.length; i++) + { + resultList.push(normalizeLocale(list[i])); + } + + return resultList; + } + + /** + * @private + */ + private static function normalizeLocale(locale:String):String + { + return locale.toLowerCase().replace(/-/g, "_"); + } + + /** + * @private + */ + private static function addUltimateFallbackLocale( + preferenceLocales:Array, + ultimateFallbackLocale:String):void + { + if (ultimateFallbackLocale != null && ultimateFallbackLocale != "") + { + var locale:String = normalizeLocale(ultimateFallbackLocale); + + if (preferenceLocales.indexOf(locale) == -1) + { + // Add the locale to the end of the chain. + preferenceLocales.push(locale); + } + } + } +} + +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Helper class: LocaleID +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * @private + * The APIs of the internal LocaleID class parse a locale string + * according to: + * RFC 4646: http://www.ietf.org/rfc/rfc4646.txt + * RFC 4647: http://www.ietf.org/rfc/rfc4647.txt + * IANA Language Subtag Registry: + * http://www.iana.org/assignments/language-subtag-registry + */ +class LocaleID +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public static const STATE_PRIMARY_LANGUAGE:int = 0; + + /** + * @private + */ + public static const STATE_EXTENDED_LANGUAGES:int = 1; + + /** + * @private + */ + public static const STATE_SCRIPT:int = 2; + + /** + * @private + */ + public static const STATE_REGION:int = 3; + + /** + * @private + */ + public static const STATE_VARIANTS:int = 4; + + /** + * @private + */ + public static const STATE_EXTENSIONS:int = 5; + + /** + * @private + */ + public static const STATE_PRIVATES:int = 6; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private static function appendElements(dest:Array, src:Array):void + { + for (var i:uint = 0, argc:uint = src.length; i < argc; i++) + { + dest.push(src[i]); + } + } + + /** + * @private + */ + public static function fromString(str:String):LocaleID + { + var localeID:LocaleID = new LocaleID(); + + var state:int = STATE_PRIMARY_LANGUAGE; + var subtags:Array = str.replace(/-/g, '_').split('_'); + + var last_extension:Array; + + for (var i:int = 0, l:int = subtags.length; i < l; i++) + { + var subtag:String = subtags[i].toLowerCase(); + + if (state == STATE_PRIMARY_LANGUAGE) + { + if (subtag == "x") + { + localeID.privateLangs = true; + // not used in our implementation, + // but makes the tag private + } + else if (subtag == "i") + { + localeID.lang += "i-"; + // and wait the next subtag + // to complete the language name + } + else + { + localeID.lang += subtag; + state = STATE_EXTENDED_LANGUAGES; + } + } + else + { + // looking for: + // an extended language - 3 chars + // a script - 4 chars + // a region - 2-3 chars + // a variant - alpha with at least 5 chars + // or numeric with at least 4 chars + // an extension/private singleton - 1 char + + var subtag_length:int = subtag.length; + if (subtag_length == 0) + continue; // skip zero-lengthed subtags + + var firstChar:String = subtag.charAt(0).toLowerCase(); + + if (state <= STATE_EXTENDED_LANGUAGES && + subtag_length == 3) + { + localeID.extended_langs.push(subtag); + if (localeID.extended_langs.length == 3) + { + // Allow a maximum of 3 extended langs. + state = STATE_SCRIPT; + } + } + else if (state <= STATE_SCRIPT && + subtag_length == 4) + { + localeID.script = subtag; + state = STATE_REGION; + } + else if (state <= STATE_REGION && + (subtag_length == 2 || subtag_length == 3)) + { + localeID.region = subtag; + state = STATE_VARIANTS; + } + else if (state <= STATE_VARIANTS && + ((firstChar >= "a" && firstChar <= "z" && + subtag_length >= 5) || + (firstChar >= "0" && firstChar <= "9" && + subtag_length >= 4))) + { + // variant + localeID.variants.push(subtag); + state = STATE_VARIANTS; + } + else if (state < STATE_PRIVATES && + subtag_length == 1) + { + // singleton + if (subtag == "x") + { + state = STATE_PRIVATES; + last_extension = localeID.privates; + } + else + { + state = STATE_EXTENSIONS; + last_extension = localeID.extensions[subtag] || []; + localeID.extensions[subtag] = last_extension; + } + } + else if (state >= STATE_EXTENSIONS) + { + last_extension.push(subtag); + } + } + } + + localeID.canonicalize(); + + return localeID; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function LocaleID() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var lang:String = ""; + + /** + * @private + */ + private var script:String = ""; + + /** + * @private + */ + private var region:String = ""; + + /** + * @private + */ + private var extended_langs:Array = []; + + /** + * @private + */ + private var variants:Array = []; + + /** + * @private + */ + private var extensions:Object = {}; + + /** + * @private + */ + private var privates:Array = []; + + /** + * @private + */ + private var privateLangs:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public function canonicalize():void + { + for (var i:String in extensions) + { + if (extensions.hasOwnProperty(i)) + { + // Also clear zero length extensions. + if (extensions[i].length == 0) + delete extensions[i]; + else + extensions[i] = extensions[i].sort(); + } + } + + extended_langs = extended_langs.sort(); + variants = variants.sort(); + privates = privates.sort(); + + if (script == "") + script = LocaleRegistry.getScriptByLang(lang); + + // Still no script, check the region. + if (script == "" && region != "") + script = LocaleRegistry.getScriptByLangAndRegion(lang, region); + + if (region == "" && script != "") + { + region = LocaleRegistry.getDefaultRegionForLangAndScript( + lang, script); + } + } + + /** + * @private + */ + public function toString():String + { + var stack:Array = [ lang ]; + + appendElements(stack, extended_langs); + + if (script != "") + stack.push(script); + + if (region != "") + stack.push(region); + + appendElements(stack, variants); + + for (var i:String in extensions) + { + if (extensions.hasOwnProperty(i)) + { + stack.push(i); + appendElements(stack, extensions[i]); + } + } + + if (privates.length > 0) + { + stack.push("x"); + appendElements(stack, privates); + } + + return stack.join("_"); + } + + /** + * @private + */ + public function equals(locale:LocaleID):Boolean + { + return toString() == locale.toString(); + } + + /** + * @private + */ + public function isSiblingOf(other:LocaleID):Boolean + { + return lang == other.lang && script == other.script; + } + + /** + * @private + */ + public function transformToParent():Boolean + { + if (privates.length > 0) + { + privates.splice(privates.length - 1, 1); + return true; + } + + var lastExtensionName:String = null; + for (var i:String in extensions) + { + if (extensions.hasOwnProperty(i)) + lastExtensionName = i; + } + + if (lastExtensionName) + { + var lastExtension:Array = extensions[lastExtensionName]; + if (lastExtension.length == 1) + { + delete extensions[lastExtensionName]; + return true; + } + lastExtension.splice(lastExtension.length - 1, 1); + return true; + } + + if (variants.length > 0) + { + variants.splice(variants.length - 1, 1); + return true; + } + + if (script != "") + { + // Check if we can suppress the script. + if (LocaleRegistry.getScriptByLang(lang) != "") + { + script = ""; + return true; + } + else if (region == "") + { + // Maybe the default region can suppress the script. + var defaultRegion:String = + LocaleRegistry.getDefaultRegionForLangAndScript( + lang, script); + if (defaultRegion != "") + { + region = defaultRegion; + script = ""; + return true; + } + } + } + + if (region != "") + { + if (!(script == "" && LocaleRegistry.getScriptByLang(lang) == "")) + { + region = ""; + return true; + } + } + + if (extended_langs.length > 0) + { + extended_langs.splice(extended_langs.length - 1, 1); + return true; + } + + return false; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Helper class: LocaleRegistry +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * @private + */ +class LocaleRegistry +{ + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * @private + * A list of codes representing writing systems, in arbitrary order. + * For example, "latn" represents the Latin alphabet, used for + * writing languages such as English, French, Indonesian, and Swahili, + * and "arab" represents the Arabic script, used for writing + * Arabic, Persian, Pashto, and Urdu. + */ + private static const SCRIPTS:Array = + [ + "", // 0 + "latn", // 1 Latin + "ethi", // 2 Ethiopian + "arab", // 3 Arabic + "beng", // 4 Bengali + "cyrl", // 5 Cyrillic + "thaa", // 6 Thaana + "tibt", // 7 Tibetan + "grek", // 8 Greek + "gujr", // 9 Gujarati + "hebr", // 10 Hebrew + "deva", // 11 Devanagari + "armn", // 12 Armenian + "jpan", // 13 Japanese + "geor", // 14 Georgian + "khmr", // 15 Khmer + "knda", // 16 Kannada + "kore", // 17 Korean + "laoo", // 18 Lao + "mlym", // 19 Malayalam + "mymr", // 20 Burmese + "orya", // 21 Oriya + "guru", // 22 Punjabi + "sinh", // 23 Sinhalese + "taml", // 24 Tamil + "telu", // 25 Telugu + "thai", // 26 Thai + "nkoo", // 27 N'Ko + "blis", // 28 Bliss + "hans", // 29 Simplified Chinese + "hant", // 30 Traditional Chinese + "mong", // 31 Mongolian + "syrc" // 32 Syriac + ]; + + /** + * @private + * The inverse of the SCRIPT Array. + * Maps a script code (like "jpan" for Japanese) + * to its index in the SCRIPT Array. + */ + private static const SCRIPT_BY_ID:Object = + { + latn: 1, + ethi: 2, + arab: 3, + beng: 4, + cyrl: 5, + thaa: 6, + tibt: 7, + grek: 8, + gujr: 9, + hebr: 10, + deva: 11, + armn: 12, + jpan: 13, + geor: 14, + khmr: 15, + knda: 16, + kore: 17, + laoo: 18, + mlym: 19, + mymr: 20, + orya: 21, + guru: 22, + sinh: 23, + taml: 24, + telu: 25, + thai: 26, + nkoo: 27, + blis: 28, + hans: 29, + hant: 30, + mong: 31, + syrc: 32 + }; + + /** + * @private + * This table maps a language and a script to the most + * prominent region where that combination is used. + * + * Note: "is" must be quoted because it is a reserved word. + */ + private static const DEFAULT_REGION_BY_LANG_AND_SCRIPT:Object = + { + bg: { 5: "bg" }, // Bulgarian / Cyrillic -> Bulgaria + ca: { 1: "es" }, // Catalan / Latin -> Spain + zh: { 30: "tw", 29: "cn" }, // Chinese / Traditional Chinese -> Taiwan + // Chinese / Simplified Chinese -> China + cs: { 1: "cz" }, // Czech / Latin -> Czech Republic + da: { 1: "dk" }, // Danish / Latin -> Denmark + de: { 1: "de" }, // German / Latin -> Germany + el: { 8: "gr" }, // Greek / Greek -> Greece + en: { 1: "us" }, // English / Latin -> United States + es: { 1: "es" }, // Spanish / Latin -> Spain + fi: { 1: "fi" }, // Finnish / Latin -> Finland + fr: { 1: "fr" }, // French / Latin -> France + he: { 10: "il" }, // Hebrew / Hebrew -> Israel + hu: { 1: "hu" }, // Hungarian / Latin -> Hungary + "is": { 1: "is" }, // Icelandic / Latin -> Iceland + it: { 1: "it" }, // Italian / Latin -> Italy + ja: { 13: "jp" }, // Japanese / Japanese -> Japan + ko: { 17: "kr" }, // Korean / Korean -> Korea + nl: { 1: "nl" }, // Dutch / Latin -> Netherlands + nb: { 1: "no" }, // Norwegian Bokmaal / Latin -> Norway + pl: { 1: "pl" }, // Polish / Latin -> Poland + pt: { 1: "br" }, // Portuguese / Latin -> Brazil + ro: { 1: "ro" }, // Romanian / Latin -> Romania + ru: { 5: "ru" }, // Russian / Cyrillic -> Russia + hr: { 1: "hr" }, // Croatian / Latin -> Croatia + sk: { 1: "sk" }, // Slovak / Latin -> Slovakia + sq: { 1: "al" }, // Albanian / Latin -> Albania + sv: { 1: "se" }, // Swedish / Latin -> Sweden + th: { 26: "th" }, // Thai / Thai -> Thailand + tr: { 1: "tr" }, // Turkish / Latin -> Turkey + ur: { 3: "pk" }, // Urdu / Arabic -> Pakistan + id: { 1: "id" }, // Indonesian / Latin -> Indonesia + uk: { 5: "ua" }, // Ukrainian / Cyrillic -> Ukraine + be: { 5: "by" }, // Byelorussian / Cyrillic -> Belarus + sl: { 1: "si" }, // Slovenian / Latin -> Slovenia + et: { 1: "ee" }, // Estonian / Latin -> Estonia + lv: { 1: "lv" }, // Latvian / Latin -> Latvia + lt: { 1: "lt" }, // Lithuanian / Latin -> Lithuania + fa: { 3: "ir" }, // Persian / Arabic -> Iran + vi: { 1: "vn" }, // Vietnamese / Latin -> Vietnam + hy: { 12: "am"}, // Armenian / Armenian -> Armenia + az: { 1: "az", 5: "az" }, // Azerbaijani / Latin -> Azerbaijan + // Azerbaijani / Cyrillic -> Azerbaijan + eu: { 1: "es" }, // Basque / Latin -> Spain + mk: { 5: "mk" }, // Macedonian / Cyrillic -> Macedonia + af: { 1: "za" }, // Afrikaans / Latin -> South Africa + ka: { 14: "ge" }, // Georgian / Georgian -> Georgia + fo: { 1: "fo" }, // Faeroese / Latin -> Faroe Islands + hi: { 11: "in" }, // Hindi / Devanagari -> India + ms: { 1: "my" }, // Malay / Latin -> Malaysia + kk: { 5: "kz" }, // Kazakh / Cyrillic -> Kazakhstan + ky: { 5: "kg" }, // Kirghiz / Cyrillic -> Kyrgyzstan + sw: { 1: "ke" }, // Swahili / Latin -> Kenya + uz: { 1: "uz", 5: "uz" }, // Uzbek / Latin -> Uzbekistan + // Uzbek / Cyrillic -> Uzbekistan + tt: { 5: "ru" }, // Tatar / Cyrillic -> Russia + pa: { 22: "in" }, // Punjabi / Punjabi -> India + gu: { 9: "in" }, // Gujarati / Gujarati -> India + ta: { 24: "in" }, // Tamil / Tamil -> India + te: { 25: "in" }, // Telugu / Telugu -> India + kn: { 16: "in" }, // Kannada / Kannada -> India + mr: { 11: "in" }, // Marathi / Devanagari -> India + sa: { 11: "in" }, // Sanskrit / Devanagari -> India + mn: { 5: "mn" }, // Mongolian / Cyrillic -> Mongolia + gl: { 1: "es" }, // Galician / Latin -> Spain + kok: { 11: "in" }, // Konkani / Devanagari -> India + syr: { 32: "sy" }, // Syriac / Syriac -> Syria + dv: { 6: "mv" }, // Dhivehi / Thaana -> Maldives + nn: { 1: "no" }, // Norwegian Nynorsk / Latin -> Norway + sr: { 1: "cs", 5: "cs" }, // Serbian / Latin -> Serbia + // Serbian / Cyrillic -> Serbia + cy: { 1: "gb" }, // Welsh / Latin -> United Kingdom + mi: { 1: "nz" }, // Maori / Latin -> New Zealand + mt: { 1: "mt" }, // Maltese / Latin -> Malta + quz: { 1: "bo" }, // Quechua / Latin -> Bolivia + tn: { 1: "za" }, // Tswana / Latin -> South Africa + xh: { 1: "za" }, // Xhosa / Latin -> South Africa + zu: { 1: "za" }, // Zulu / Latin -> South Africa + nso: { 1: "za" }, // Northern Sotho / Latin -> South Africa + se: { 1: "no" }, // Northern Saami / Latin -> Norway + smj: { 1: "no" }, // Lule Saami / Latin -> Norway + sma: { 1: "no" }, // Southern Saami / Latin -> Norway + sms: { 1: "fi" }, // Skolt Saami / Latin -> Finland + smn: { 1: "fi" }, // Inari Saami / Latin -> Finland + bs: { 1: "ba" } // Bosnia / Latin -> Bosnia + }; + + /** + * @private + * This table maps a language to a script. + * It was derived from the entries at + * http://www.iana.org/assignments/language-subtag-registry + * which have a Suppress-Script property. + * + * Note: "as", "in", and "is" must be quoted + * because they are reserved words. + */ + private static const SCRIPT_ID_BY_LANG:Object = + { + ab: 5, // Abkhazian -> Cyrillic + af: 1, // Afrikaans -> Latin + am: 2, // Amharic -> Ethiopian + ar: 3, // Arabic -> Arabic + "as": 4, // Assamese -> Bengali + ay: 1, // Aymara -> Latin + be: 5, // Belarusian -> Cyrillic + bg: 5, // Bulgarian -> Cyrillic + bn: 4, // Bengali -> Bengali + bs: 1, // Bosnian -> Latin + ca: 1, // Catalan / Valencian -> Latin + ch: 1, // Chamorro -> Latin + cs: 1, // Czech -> Latin + cy: 1, // Welsh -> Latin + da: 1, // Danish -> Latin + de: 1, // German -> Latin + dv: 6, // Dhivehi / Maldivian -> Thaana + dz: 7, // Dzongkha -> Tibetan + el: 8, // Modern Greek -> Greek + en: 1, // English -> Latin + eo: 1, // Esperanto -> Latin + es: 1, // Spanish / Castilian -> Latin + et: 1, // Estonian -> Latin + eu: 1, // Basque -> Latin + fa: 3, // Persian -> Arabic + fi: 1, // Finnish -> Latin + fj: 1, // Fijian -> Latin + fo: 1, // Faroese -> Latin + fr: 1, // French -> Latin + frr: 1, // Northern Frisian -> Latin + fy: 1, // Western Frisian -> Latin + ga: 1, // Irish -> Latin + gl: 1, // Galician -> Latin + gn: 1, // Guarani -> Latin + gu: 9, // Gujarati -> Gujarati + gv: 1, // Manx -> Latin + he: 10, // Hebrew -> Hebrew + hi: 11, // Hindi -> Devanagari + hr: 1, // Croatian -> Latin + ht: 1, // Haitian Creole -> Latin + hu: 1, // Hungarian -> Latin + hy: 12, // Armenian -> Armenian + id: 1, // Indonesian -> Latin + "in": 1, // Indonesian -> Latin + "is": 1, // Icelandic -> Latin + it: 1, // Italian -> Latin + iw: 10, // Hebrew -> Hebrew + ja: 13, // Japanese -> Japanese + ka: 14, // Georgian -> Georgian + kk: 5, // Kazakh -> Cyrillic + kl: 1, // Kalaallisut / Greenlandic -> Latin + km: 15, // Central Khmer -> Khmer + kn: 16, // Kannada -> Kannada + ko: 17, // Korean -> Korean + la: 1, // Latin -> Latin + lb: 1, // Luxembourgish -> Latin + ln: 1, // Lingala -> Latin + lo: 18, // Lao -> Lao + lt: 1, // Lithuanian -> Latin + lv: 1, // Latvian -> Latin + mg: 1, // Malagasy -> Latin + mh: 1, // Marshallese -> Latin + mk: 5, // Macedonian -> Cyrillic + ml: 19, // Malayalam -> Malayalam + mo: 1, // Moldavian -> Latin + mr: 11, // Marathi -> Devanagari + ms: 1, // Malay -> Latin + mt: 1, // Maltese -> Latin + my: 20, // Burmese -> Burmese + na: 1, // Nauru -> Latin + nb: 1, // Norwegian Bokmaal -> Latin + nd: 1, // North Ndebele -> Latin + ne: 11, // Nepali -> Devanagari + nl: 1, // Dutch / Flemish -> Latin + nn: 1, // Norwegian Nynorsk -> Latin + no: 1, // Norwegian -> Latin + nr: 1, // South Ndebele -> Latin + ny: 1, // Chichewa / Chewa / Nyanja -> Latin + om: 1, // Oromo -> Latin + or: 21, // Oriya -> Oriya + pa: 22, // Punjabi -> Punjabi + pl: 1, // Polish -> Latin + ps: 3, // Pashto -> Arabic + pt: 1, // Portuguese -> Latin + qu: 1, // Quechua -> Latin + rn: 1, // Rundi -> Latin + ro: 1, // Romanian -> Latin + ru: 5, // Russian -> Cyrillic + rw: 1, // Kinyarwanda -> Latin + sg: 1, // Sango -> Latin + si: 23, // Sinhalese -> Sinhalese + sk: 1, // Slovak -> Latin + sl: 1, // Slovenian -> Latin + sm: 1, // Samoan -> Latin + so: 1, // Somali -> Latin + sq: 1, // Albanian -> Latin + ss: 1, // Swati -> Latin + st: 1, // Southern Sotho -> Latin + sv: 1, // Swedish -> Latin + sw: 1, // Swahili -> Latin + ta: 24, // Tamil -> Tamil + te: 25, // Telugu -> Telugu + th: 26, // Thai -> Thai + ti: 2, // Tigrinya -> Ethiopian + tl: 1, // Tagalog -> Latin + tn: 1, // Tswana -> Latin + to: 1, // Tonga -> Latin + tr: 1, // Turkish -> Latin + ts: 1, // Tsonga -> Latin + uk: 5, // Ukrainian -> Cyrillic + ur: 3, // Urdu -> Arabic + ve: 1, // Venda -> Latin + vi: 1, // Vietnamese -> Latin + wo: 1, // Wolof -> Latin + xh: 1, // Xhosa -> Latin + yi: 10, // Yiddish -> Hebrew + zu: 1, // Zulu -> Latin + cpe: 1, // Creoles and pidgins -> Latin + dsb: 1, // Lower Sorbian -> Latin + frs: 1, // Eastern Frisian -> Latin + gsw: 1, // Swiss German / Alemaanic / Alsatian -> Latin + hsb: 1, // Upper Sorbian -> Latin + kok: 11, // Konkana -> Devanagari + mai: 11, // Maithili -> Devanagari + men: 1, // Mende -> Latin + nds: 1, // Low German -> Latin + niu: 1, // Niuean -> Latin + nqo: 27, // N'Ko -> N'Ko + nso: 1, // Northern Sotho / Pedi / Sepedi -> Latin + son: 1, // Songhai -> Latin + tem: 1, // Timne -> Latin + tkl: 1, // Tokelau -> Latin + tmh: 1, // Tamashek -> Latin + tpi: 1, // Tok Pisin -> Latin + tvl: 1, // Tuvalu -> Latin + zbl: 28 // Bliss -> Bliss + }; + + /** + * @private + * This table maps a language-as-spoken-in-a-region + * to the script used to write it. + * + * Chinese in China -> Simplified Chinese + * Chinese in Singapore -> Simplified Chinese + * Chinese in Taiwan -> Traditional Chinese + * Chinese in Hong Kong -> Traditional Chinese + * Chinese in Macao -> Traditional Chinese + * Mongolian in China -> Mongolian + * Mongolian in Singapore -> Cyrillic + * Punjabi in Pakistan -> Arabic + * Punjabi in India -> Punjabi + * Hausa in Ghana -> Latin + * Hausa in Niger -> Latin + */ + private static const SCRIPT_ID_BY_LANG_AND_REGION:Object = + { + zh: { cn: 29, sg: 29, tw: 30, hk: 30, mo: 30 }, + mn: { cn: 31, sg: 5 }, + pa: { pk: 3, "in": 22 }, // "in" is reserved word + ha: { gh: 1, ne: 1 } + }; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Given a language and a region, returns the script system + * used to write the language there. + * + * Examples: + * lang zh (Chinese), region cn (China) -> hans (Simplified Chinese) + * lang zh (Chinese), region tw (Taiwan) -> hast (Traditional Chinese) + */ + public static function getScriptByLangAndRegion( + lang:String, region:String):String + { + var langRegions:Object = SCRIPT_ID_BY_LANG_AND_REGION[lang]; + if (langRegions == null) + return ""; + + var scriptID:Object = langRegions[region]; + if (scriptID == null) + return ""; + + return SCRIPTS[int(scriptID)].toLowerCase(); + } + + /** + * @private + * Given a language, returns the script generally used to write it. + * + * Examples: + * lang bg (Bulgarian) -> cyrl (Cyrillic) + */ + public static function getScriptByLang(lang:String):String + { + var scriptID:Object = SCRIPT_ID_BY_LANG[lang]; + if (scriptID == null) + return ""; + + return SCRIPTS[int(scriptID)].toLowerCase(); + } + + /** + * @private + * Given a language and a script used for writing it, + * returns the most prominent region where that combination is used. + * + * Examples: + */ + public static function getDefaultRegionForLangAndScript( + lang:String, script:String):String + { + var langObj:Object = DEFAULT_REGION_BY_LANG_AND_SCRIPT[lang]; + var scriptID:Object = SCRIPT_BY_ID[script]; + if (langObj == null || scriptID == null) + return ""; + + return langObj[int(scriptID)] || ""; + } +} diff --git a/src/mx/resources/ResourceBundle.as b/src/mx/resources/ResourceBundle.as new file mode 100644 index 00000000..88c5f690 --- /dev/null +++ b/src/mx/resources/ResourceBundle.as @@ -0,0 +1,241 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.resources +{ + +import flash.system.ApplicationDomain; + +import mx.core.mx_internal; +import mx.utils.StringUtil; + +use namespace mx_internal; + +/** + * Provides an implementation of the IResourceBundle interface. + * The IResourceManager and IResourceBundle interfaces work together + * to provide internationalization support for Flex applications. + * + *

A Flex application typically has multiple instances of this class, + * all managed by a single instance of the ResourceManager class. + * It is possible to have ResourceBundle instances for multiple locales, + * one for each locale. There can be multiple ResourceBundle instances with + * different bundle names.

+ * + * @see mx.resources.IResourceBundle + * @see mx.resources.IResourceManager + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class ResourceBundle implements IResourceBundle +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Set by SystemManager constructor in order to make the deprecated + * getResourceBundle() method work with the new resource scheme + * in the single-locale case. + */ + mx_internal static var locale:String; + + /** + * @private + * Set by bootstrap loaders + * to allow for alternate search paths for resources + */ + mx_internal static var backupApplicationDomain:ApplicationDomain; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private static function getClassByName(name:String, + domain:ApplicationDomain):Class + { + var c:Class; + + if (domain.hasDefinition(name)) + c = domain.getDefinition(name) as Class; + + return c; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @param locale A locale string, such as "en_US". + * + * @param bundleName A name that identifies this bundle, + * such as "MyResources". + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function ResourceBundle(locale:String = null, + bundleName:String = null) + { + // The only reason that the arguments are optional is so that + // Flex 3 applications can link against Flex 2 resource SWCs. + // In Flex 2, the constructor had no arguments at all + // and the autogenerated ResourceBundle subclasses + // therefore called super() with no arguments. + // If, in Flex 3, the constructor has required arguments, + // this causes a VerifyError. + + super(); + + _locale = locale; + _bundleName = bundleName; + + _content = getContent(); + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // bundleName + //---------------------------------- + + /** + * @private + * Storage for the bundleName property. + */ + mx_internal var _bundleName:String; + + /** + * @copy mx.resources.IResourceBundle#bundleName + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get bundleName():String + { + return _bundleName; + } + + //---------------------------------- + // content + //---------------------------------- + + /** + * @private + * Storage for the content property. + */ + private var _content:Object = {}; + + /** + * @copy mx.resources.IResourceBundle#content + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get content():Object + { + return _content; + } + + //---------------------------------- + // locale + //---------------------------------- + + /** + * @private + * Storage for the locale property. + */ + mx_internal var _locale:String; + + /** + * @copy mx.resources.IResourceBundle#locale + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get locale():String + { + return _locale; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * When a properties file is compiled into a resource bundle, + * the MXML compiler autogenerates a subclass of ResourceBundle. + * The subclass overrides this method to return an Object + * that contains key-value pairs for the bundle's resources. + * + *

If you create your own ResourceBundle instances, + * you can set the key-value pairs on the content object.

+ * + * @return The Object that contains key-value pairs for the bundle's resources. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + protected function getContent():Object + { + return {}; + } + + /** + * @private + */ + private function _getObject(key:String):Object + { + var value:Object = content[key]; + if (!value) + { + throw new Error("Key " + key + + " was not found in resource bundle " + bundleName); + } + return value; + } +} + +} diff --git a/src/mx/resources/ResourceManager.as b/src/mx/resources/ResourceManager.as new file mode 100644 index 00000000..6ca12bda --- /dev/null +++ b/src/mx/resources/ResourceManager.as @@ -0,0 +1,133 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.resources +{ + +import flash.events.Event; +import flash.events.EventDispatcher; +import flash.events.IEventDispatcher; +import flash.events.TimerEvent; +import flash.system.SecurityDomain; +import flash.utils.getDefinitionByName; +import flash.utils.Timer; +import mx.core.IFlexModuleFactory; +import mx.core.mx_internal; +import mx.core.Singleton; +//import mx.events.ModuleEvent; +//import mx.events.ResourceEvent; +//import mx.modules.IModuleInfo; +//import mx.modules.ModuleManager; +//import mx.utils.StringUtil; + +/** + * This class is used to get a single instance of the IResourceManager + * implementation. + * The IResourceManager and IResourceBundle interfaces work together + * to provide internationalization support for Flex applications. + * + *

A single instance of an IResourceManager implementation + * manages all localized resources + * for a Flex application.

+ * + * @see mx.resources.IResourceManager + * @see mx.resources.IResourceBundle + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class ResourceManager +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Linker dependency on implementation class. + */ + private static var implClassDependency:ResourceManagerImpl; + + /** + * @private + * The sole instance of the ResourceManager. + */ + private static var instance:IResourceManager; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Gets the single instance of the ResourceManager class. + * This object manages all localized resources for a Flex application. + * + * @return An object implementing IResourceManager. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getInstance():IResourceManager + { + if (!instance) + { + /* + CONFIG::performanceInstrumentation + { + var perfUtil:mx.utils.PerfUtil = mx.utils.PerfUtil.getInstance(); + perfUtil.markTime("ResourceManager.getInstance().start"); + } + */ + if (!Singleton.getClass("mx.resources::IResourceManager")) + // install ResourceManagerImpl if not registered already + Singleton.registerClass("mx.resources::IResourceManager", + Class(getDefinitionByName("mx.resources::ResourceManagerImpl"))); + + + try + { + instance = IResourceManager( + Singleton.getInstance("mx.resources::IResourceManager")); + } + catch(e:Error) + { + // In non-Flex apps and modules, the Singleton manager + // won't have been initialized by SystemManager + // or FlexModuleFactory (since these don't get linked in) + // so the above call to getInstance() will throw an exception. + // In this situation, the ResourceManager simply creates + // its own ResourceManagerImpl. + instance = new ResourceManagerImpl(); + } +/* + CONFIG::performanceInstrumentation + { + perfUtil.markTime("ResourceManager.getInstance().end"); + } +*/ + } + + return instance; + } + +} + +} \ No newline at end of file diff --git a/src/mx/resources/ResourceManagerImpl.as b/src/mx/resources/ResourceManagerImpl.as new file mode 100644 index 00000000..366d0c6a --- /dev/null +++ b/src/mx/resources/ResourceManagerImpl.as @@ -0,0 +1,1314 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.resources +{ + +import flash.events.Event; +import flash.events.EventDispatcher; +import flash.events.FocusEvent; +import flash.events.IEventDispatcher; +import flash.events.TimerEvent; +import flash.system.ApplicationDomain; +import flash.system.Capabilities; +import flash.system.SecurityDomain; +import flash.utils.Dictionary; +import flash.utils.Timer; +import mx.core.IFlexModuleFactory; +import mx.core.mx_internal; +import mx.core.Singleton; +import mx.events.FlexEvent; +import mx.events.ModuleEvent; +import mx.events.ResourceEvent; +//import mx.managers.SystemManagerGlobals; +import mx.modules.IModuleInfo; +//import mx.modules.ModuleManager; +import mx.utils.StringUtil; + +use namespace mx_internal; + +/** + * @copy mx.resources.IResourceManager#change + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="change", type="flash.events.Event")] + +[ExcludeClass] + +/** + * @private + * This class provides an implementation of the IResourceManager interface. + * The IResourceManager and IResourceBundle interfaces work together + * to provide internationalization support for Flex applications. + * + *

A single instance of this class manages all localized resources + * for a Flex application.

+ * + * @see mx.resources.IResourceManager + * @see mx.resources.IResourceBundle + */ +public class ResourceManagerImpl extends EventDispatcher implements IResourceManager +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * The sole instance of the ResourceManager. + */ + private static var instance:IResourceManager; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Gets the single instance of the ResourceManagerImpl class. + * This object manages all localized resources for a Flex application. + * + * @return An object implementing IResourceManager. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getInstance():IResourceManager + { + if (!instance) + instance = new ResourceManagerImpl(); + + return instance; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function ResourceManagerImpl() + { + super(); +/* + if (SystemManagerGlobals.topLevelSystemManagers.length) + { + if (SystemManagerGlobals.topLevelSystemManagers[0].currentFrame == 1) + { + ignoreMissingBundles = true; + SystemManagerGlobals.topLevelSystemManagers[0]. + addEventListener(Event.ENTER_FRAME, enterFrameHandler); + } + } + + var info:Object = SystemManagerGlobals.info; + if (info) + processInfo(info, false); + + ignoreMissingBundles = false; + + if (SystemManagerGlobals.topLevelSystemManagers.length) + SystemManagerGlobals.topLevelSystemManagers[0]. + addEventListener(FlexEvent.NEW_CHILD_APPLICATION, newChildApplicationHandler); + */ + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * + * Whether or not to throw an error. + */ + private var ignoreMissingBundles:Boolean; + + /** + * @private + * + * The dictionary to hold all of the weak reference resource bundles. + */ + private var bundleDictionary:Dictionary; + + /** + * @private + * A map whose keys are locale strings like "en_US" + * and whose values are "bundle maps". + * A bundle map is a map whose keys are bundle names + * like "SharedResources" and whose values are ResourceBundle instances. + * You can get to an individual resource value like this: + * localeMap["en_US"]["SharedResources"].content["currencySymbol"] + */ + private var localeMap:Object = {}; + + /** + * @private + * A map whose keys are URLs for resource modules that have been loaded + * and whose values are ResourceModuleInfo instances for those modules. + */ + private var resourceModules:Object = {}; + + /** + * @private + */ + private var initializedForNonFrameworkApp:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // localeChain + //---------------------------------- + + /** + * @private + * Storage for the localeChain property. + */ + private var _localeChain:Array /* of String */; + + /** + * @copy mx.resources.IResourceManager#localeChain + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get localeChain():Array /* of String */ + { + return _localeChain; + } + + /** + * @private + */ + public function set localeChain(value:Array /* of String */):void + { + _localeChain = value; + + update(); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * This method is called by the SystemManager class of an Application + * when the application starts. + * It is also called by the FlexModuleFactory class of a code module + * when that module gets loaded. + * + * The MXML compiler autogenerated code which set the + * "compiledLocales" and "compiledResourceBundleNames" properties + * of the info() Object required by the IFlexModuleFactory + * interface that these classes implement. + * These two properties together indicate which resource bundle + * classes the MXML compiler autogenerated and linked into the + * application or module. + * + * The "compiledLocales" property has been set to the locales + * which were specified at compile time using the -locale option. + * For example, if you compile with -locale=en_US,ja_JP + * then the "compiledLocales" property is the array [ "en_US", "ja_JP" ]. + * + * The "compiledResourceBundleNames" property has been set + * to the names of the resource bundles which are used by + * the application or module, as determined by the compiler + * from [ResourceBundle] metadata and ~~Resource() directives. + * For example, if the classes in the application or module + * declare that they use resource bundles named "core" and "MyApp", + * then the "compiledResourceBundleNames" property is the array + * [ "core", "MyApp" ]. + * + * The compiler autogenerated a ResourceBundle subclass for each + * (locale, bundle name) pair. + * For example, with the above locales and bundle names, + * there would be four classes: + * en_US$core_properties + * en_US$MyApp_properties + * ja_JP$core_properties + * ja_JP$MyApp_properties + * + * This method creates one instance of each such class + * and installs it into the ResourceManager with addResourceBundle(). + * If a bundle for a given locale and bundle name already exists + * in the ResourceManager already exists, it does not get replaced. + * This can happen when a code module gets loaded into an application. + * + * When sub-applications and modules install their resource bundles + * they set useWeakReference = true. Any new resource bundles they + * create will be weak referenced by the ResourceManager. The + * sub-application or module will then provide a hard reference + * to the returned Array of resource bundles to keep them from + * being garbage collected. + */ + public function installCompiledResourceBundles( + applicationDomain:ApplicationDomain, + locales:Array /* of String */, + bundleNames:Array /* of String */, + useWeakReference:Boolean = false):Array + { + //trace("locales", locales); + //trace("bundleNames", bundleNames); + var bundles:Array = []; + var bundleCount:uint = 0; + var n:int = locales ? locales.length : 0; + var m:int = bundleNames ? bundleNames.length : 0; + + // Loop over the locales. + for (var i:int = 0; i < n; i++) + { + var locale:String = locales[i]; + + // Loop over the bundle names. + for (var j:int = 0; j < m; j++) + { + var bundleName:String = bundleNames[j]; + + var bundle:IResourceBundle = installCompiledResourceBundle( + applicationDomain, locale, bundleName, + useWeakReference); + + if (bundle) + bundles[bundleCount++] = bundle; + } + } + + return bundles; + } + + /** + * @private + */ + private function installCompiledResourceBundle( + applicationDomain:ApplicationDomain, + locale:String, bundleName:String, + useWeakReference:Boolean = false):IResourceBundle + { + var packageName:String = null; + var localName:String = bundleName; + var colonIndex:int = bundleName.indexOf(":"); + if (colonIndex != -1) + { + packageName = bundleName.substring(0, colonIndex); + localName = bundleName.substring(colonIndex + 1); + } + + // If a bundle with that locale and bundle name already exists + // in the ResourceManager, don't replace it. + // If we want to install a weakReferenceDictionary then don't rely on + // a weak reference dictionary because it may go way when the + // application goes away. + var resourceBundle:IResourceBundle = getResourceBundleInternal(locale, + bundleName, + useWeakReference); + if (resourceBundle) + return resourceBundle; + + // The autogenerated resource bundle classes produced by the + // mxmlc and compc compilers have names that incorporate + // the locale and bundle name, such as "en_US$core_properties". + var resourceBundleClassName:String = + locale + "$" + localName + "_properties"; + if (packageName != null) + resourceBundleClassName = packageName + "." + resourceBundleClassName; + + // Find the bundle class by its name. + // We do a hasDefinition() check before calling getDefinition() + // because getDefinition() will throw an RTE + // if the class doesn't exist. + var bundleClass:Class = null; + if (applicationDomain.hasDefinition(resourceBundleClassName)) + { + bundleClass = Class(applicationDomain.getDefinition( + resourceBundleClassName)); + } + + if (!bundleClass) + { + resourceBundleClassName = bundleName; + if (applicationDomain.hasDefinition(resourceBundleClassName)) + { + bundleClass = Class(applicationDomain.getDefinition( + resourceBundleClassName)); + } + } + + // In case we linked against a Flex 2 SWC, look for the old + // class name. + if (!bundleClass) + { + resourceBundleClassName = bundleName + "_properties"; + if (applicationDomain.hasDefinition(resourceBundleClassName)) + { + bundleClass = Class(applicationDomain.getDefinition( + resourceBundleClassName)); + } + } + + if (!bundleClass) + { + if (ignoreMissingBundles) + return null; + + throw new Error( + "Could not find compiled resource bundle '" + bundleName + + "' for locale '" + locale + "'."); + } + + // Create an instance of the bundle class. + resourceBundle = ResourceBundle(new bundleClass()); + + // In case we just created a ResourceBundle from a Flex 2 SWC, + // set its locale and bundleName, because the old constructor + // didn't used to do this. + ResourceBundle(resourceBundle)._locale = locale; + ResourceBundle(resourceBundle)._bundleName = bundleName; + + // Add that resource bundle instance to the ResourceManager. + addResourceBundle(resourceBundle, useWeakReference); + + return resourceBundle; + } + + // FocusEvent is used just so we can add a relatedObject + private function newChildApplicationHandler(event:FocusEvent):void + { + var info:Object = event.relatedObject["info"](); + var weakReference:Boolean = false; + if ("_resourceBundles" in event.relatedObject) + weakReference = true; + + // If the application has a "_resourceBundles" object for us to put + // the bundles into then we will. Otherwise have the ResourceManager + // create a hard reference to the resources. + var bundles:Array = processInfo(info, weakReference); + if (weakReference) + event.relatedObject["_resourceBundles"] = bundles; + } + + private function processInfo(info:Object, useWeakReference:Boolean):Array + { + var compiledLocales:Array = info["compiledLocales"]; + + ResourceBundle.locale = + compiledLocales != null && compiledLocales.length > 0 ? + compiledLocales[0] : + "en_US"; +/* + // The FlashVars of the SWF's HTML wrapper, + // or the query parameters of the SWF URL, + // can specify the ResourceManager's localeChain. + var localeChainList:String = + SystemManagerGlobals.parameters["localeChain"]; + if (localeChainList != null && localeChainList != "") + localeChain = localeChainList.split(","); + + var applicationDomain:ApplicationDomain = info["currentDomain"]; + + var compiledResourceBundleNames:Array = + info["compiledResourceBundleNames"]; + + var bundles:Array = installCompiledResourceBundles( + applicationDomain, compiledLocales, compiledResourceBundleNames, + useWeakReference); + + // If the localeChain wasn't specified in the FlashVars of the SWF's + // HTML wrapper, or in the query parameters of the SWF URL, + // then initialize it to the list of compiled locales, + // sorted according to the system's preferred locales as reported by + // Capabilities.languages or Capabilities.language. + // For example, if the applications was compiled with, say, + // -locale=en_US,ja_JP and Capabilities.languages reports [ "ja-JP" ], + // set the localeChain to [ "ja_JP" "en_US" ]. + if (!localeChain) + initializeLocaleChain(compiledLocales); +*/ + return [];//bundles; + } + + /** + * @copy mx.resources.IResourceManager#initializeLocaleChain() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function initializeLocaleChain(compiledLocales:Array):void + { + localeChain = LocaleSorter.sortLocalesByPreference( + compiledLocales, getSystemPreferredLocales(), null, true); + } + + /** + * @copy mx.resources.IResourceManager#loadResourceModule() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function loadResourceModule(url:String, updateFlag:Boolean = true, + applicationDomain:ApplicationDomain = null, + securityDomain:SecurityDomain = null): + IEventDispatcher + { + /* + var moduleInfo:IModuleInfo = ModuleManager.getModule(url); + + // Create the per-load IEventDispatcher that we'll return. + var resourceEventDispatcher:ResourceEventDispatcher = + new ResourceEventDispatcher(moduleInfo); + + // Set up a handler for the "ready" event from the module. + // We use a local Function rather than a method + // so that it can access the 'update' argument. + var readyHandler:Function = function(event:ModuleEvent):void + { + //trace("readyHandler"); + + var resourceModule:* = // IResourceModule + event.module.factory.create(); + + //dumpResourceModule(resourceModule); + + resourceModules[event.module.url].resourceModule = resourceModule; + + if (updateFlag) + update(); + } + moduleInfo.addEventListener(ModuleEvent.READY, readyHandler, + false, 0, true); + + // Set up a handler for the "error" event from the module. + // We use a local Function rather than a method + // for symmetry with the readyHandler. + var errorHandler:Function = function(event:ModuleEvent):void + { + var message:String = "Unable to load resource module from " + url; + + if (resourceEventDispatcher.willTrigger(ResourceEvent.ERROR)) + { + var resourceEvent:ResourceEvent = new ResourceEvent( + ResourceEvent.ERROR, event.bubbles, event.cancelable); + resourceEvent.bytesLoaded = 0; + resourceEvent.bytesTotal = 0; + resourceEvent.errorText = message; + resourceEventDispatcher.dispatchEvent(resourceEvent); + } + else + { + throw new Error(message); + } + } + moduleInfo.addEventListener(ModuleEvent.ERROR, errorHandler, + false, 0, true); + + resourceModules[url] = + new ResourceModuleInfo(moduleInfo, readyHandler, errorHandler); + + // This Timer gives the loadResourceModules() caller a chance + // to add event listeners to the return value, before the module + // is loaded. + // We use a local Function for the timerHandler rather than a method + // so that it can access the 'moduleInfo' local var. + var timer:Timer = new Timer(0); + var timerHandler:Function = function(event:TimerEvent):void + { + timer.removeEventListener(TimerEvent.TIMER, timerHandler); + timer.stop(); + + //trace("loading"); + + // Start loading the module. + moduleInfo.load(applicationDomain, securityDomain); + } + timer.addEventListener(TimerEvent.TIMER, timerHandler, + false, 0, true); + timer.start(); + */ + return null;//resourceEventDispatcher; + } + + /** + * @copy mx.resources.IResourceManager#unloadResourceModule() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function unloadResourceModule(url:String, update:Boolean = true):void + { + // Get the resource module info. + var rmi:ResourceModuleInfo = resourceModules[url]; + if (!rmi) + return; + + if (rmi.resourceModule) + { + // Get the bundles in this module. + var bundles:Array = rmi.resourceModule.resourceBundles; + if (bundles) + { + var n:int = bundles.length; + for (var i:int = 0; i < n; i++) + { + // Remove each bundle. + var locale:String = bundles[i].locale; + var bundleName:String = bundles[i].bundleName; + removeResourceBundle(locale, bundleName); + } + } + } + + // Remove all links to the module. + resourceModules[url] = null; + delete resourceModules[url]; + + // Unload the module. + rmi.moduleInfo.unload(); + + // Update if necessary. + if (update) + this.update(); + } + + /** + * @copy mx.resources.IResourceManager#addResourceBundle() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function addResourceBundle(resourceBundle:IResourceBundle, + useWeakReference:Boolean = false):void + { + var locale:String = resourceBundle.locale; + var bundleName:String = resourceBundle.bundleName; + + if (!localeMap[locale]) + localeMap[locale] = {}; + + if (useWeakReference) + { + if (!bundleDictionary) + { + bundleDictionary = new Dictionary(true); + } + + bundleDictionary[resourceBundle] = locale + bundleName; + localeMap[locale][bundleName] = bundleDictionary; + } + else + { + localeMap[locale][bundleName] = resourceBundle; + } + } + + /** + * @copy mx.resources.IResourceManager#getResourceBundle() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getResourceBundle(locale:String, + bundleName:String):IResourceBundle + { + return getResourceBundleInternal(locale, bundleName, false); + } + + /** + * @private + * + * @param ignoreWeakReferenceBundles if true, do not search weak + * reference bundles. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private function getResourceBundleInternal(locale:String, + bundleName:String, + ignoreWeakReferenceBundles:Boolean):IResourceBundle + { + var bundleMap:Object = localeMap[locale]; + if (!bundleMap) + return null; + + var bundle:IResourceBundle = null; + var bundleObject:Object = bundleMap[bundleName]; + if (bundleObject is Dictionary) + { + if (ignoreWeakReferenceBundles) + return null; + + var localeBundleNameString:String = locale + bundleName; + for (var obj:Object in bundleObject) + { + if (bundleObject[obj] == localeBundleNameString) + { + bundle = obj as IResourceBundle; + break; + } + } + } + else + { + bundle = bundleObject as IResourceBundle; + } + + return bundle; + } + + /** + * @copy mx.resources.IResourceManager#removeResourceBundle() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function removeResourceBundle(locale:String, bundleName:String):void + { + // Remove the specified bundle. + delete localeMap[locale][bundleName]; + + // If that leaves a locale node with no bundles, + // delete the locale node. + if (getBundleNamesForLocale(locale).length == 0) + delete localeMap[locale]; + } + + /** + * @copy mx.resources.IResourceManager#removeResourceBundlesForLocale() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function removeResourceBundlesForLocale(locale:String):void + { + delete localeMap[locale]; + } + + /** + * @copy mx.resources.IResourceManager#update() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function update():void + { + dispatchEvent(new Event(Event.CHANGE)); + } + + /** + * @copy mx.resources.IResourceManager#getLocales() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getLocales():Array /* of String */ + { + var locales:Array = []; + for (var p:String in localeMap) + { + locales.push(p); + } + return locales; + } + + /** + * @copy mx.resources.IResourceManager#getPreferredLocaleChain() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getPreferredLocaleChain():Array /* of String */ + { + return LocaleSorter.sortLocalesByPreference( + getLocales(), getSystemPreferredLocales(), null, true); + } + + /** + * @copy mx.resources.IResourceManager#getBundleNamesForLocale() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getBundleNamesForLocale(locale:String):Array /* of String */ + { + var bundleNames:Array = []; + for (var p:String in localeMap[locale]) + { + bundleNames.push(p); + } + return bundleNames; + } + + /** + * @copy mx.resources.findResourceBundleWithResource + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function findResourceBundleWithResource( + bundleName:String, resourceName:String):IResourceBundle + { + if (!_localeChain) + return null; + + var n:int = _localeChain.length; + for (var i:int = 0; i < n; i++) + { + var locale:String = localeChain[i]; + + var bundleMap:Object = localeMap[locale]; + if (!bundleMap) + continue; + + var bundleObject:Object = bundleMap[bundleName]; + if (!bundleObject) + continue; + + var bundle:IResourceBundle = null; + + if (bundleObject is Dictionary) + { + var localeBundleNameString:String = locale + bundleName; + for (var obj:Object in bundleObject) + { + if (bundleObject[obj] == localeBundleNameString) + { + bundle = obj as IResourceBundle; + break; + } + } + } + else + { + bundle = bundleObject as IResourceBundle; + } + + if (bundle && resourceName in bundle.content) + return bundle; + } + + return null; + } + + [Bindable("change")] + + /** + * @copy mx.resources.IResourceManager#getObject() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getObject(bundleName:String, resourceName:String, + locale:String = null):* + { + var resourceBundle:IResourceBundle = + findBundle(bundleName, resourceName, locale); + if (!resourceBundle) + return undefined; + + return resourceBundle.content[resourceName]; + } + + [Bindable("change")] + + /** + * @copy mx.resources.IResourceManager#getString() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getString(bundleName:String, resourceName:String, + parameters:Array = null, + locale:String = null):String + { + var resourceBundle:IResourceBundle = + findBundle(bundleName, resourceName, locale); + if (!resourceBundle) + return null; + + var value:String = String(resourceBundle.content[resourceName]); + + if (parameters) + value = StringUtil.substitute(value, parameters); + + return value; + } + + [Bindable("change")] + + /** + * @copy mx.resources.IResourceManager#getStringArray() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getStringArray(bundleName:String, + resourceName:String, + locale:String = null):Array /* of String */ + { + var resourceBundle:IResourceBundle = + findBundle(bundleName, resourceName, locale); + if (!resourceBundle) + return null; + + var value:* = resourceBundle.content[resourceName]; + + var array:Array = String(value).split(","); + + var n:int = array.length; + for (var i:int = 0; i < n; i++) + { + array[i] = StringUtil.trim(array[i]); + } + + return array; + } + + [Bindable("change")] + + /** + * @copy mx.resources.IResourceManager#getNumber() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getNumber(bundleName:String, resourceName:String, + locale:String = null):Number + { + var resourceBundle:IResourceBundle = + findBundle(bundleName, resourceName, locale); + if (!resourceBundle) + return NaN; + + var value:* = resourceBundle.content[resourceName]; + + return Number(value); + } + + [Bindable("change")] + + /** + * @copy mx.resources.IResourceManager#getInt() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getInt(bundleName:String, resourceName:String, + locale:String = null):int + { + var resourceBundle:IResourceBundle = + findBundle(bundleName, resourceName, locale); + if (!resourceBundle) + return 0; + + var value:* = resourceBundle.content[resourceName]; + + return int(value); + } + + [Bindable("change")] + + /** + * @copy mx.resources.IResourceManager#getUint() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getUint(bundleName:String, resourceName:String, + locale:String = null):uint + { + var resourceBundle:IResourceBundle = + findBundle(bundleName, resourceName, locale); + if (!resourceBundle) + return 0; + + var value:* = resourceBundle.content[resourceName]; + + return uint(value); + } + + [Bindable("change")] + + /** + * @copy mx.resources.IResourceManager#getBoolean() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getBoolean(bundleName:String, resourceName:String, + locale:String = null):Boolean + { + var resourceBundle:IResourceBundle = + findBundle(bundleName, resourceName, locale); + if (!resourceBundle) + return false; + + var value:* = resourceBundle.content[resourceName]; + + return String(value).toLowerCase() == "true"; + } + + [Bindable("change")] + + /** + * @copy mx.resources.IResourceManager#getClass() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getClass(bundleName:String, resourceName:String, + locale:String = null):Class + { + var resourceBundle:IResourceBundle = + findBundle(bundleName, resourceName, locale); + if (!resourceBundle) + return null; + + var value:* = resourceBundle.content[resourceName]; + + return value as Class; + } + + /** + * @private. + */ + private function findBundle(bundleName:String, resourceName:String, + locale:String):IResourceBundle + { + supportNonFrameworkApps(); + + return locale != null ? + getResourceBundle(locale, bundleName) : + findResourceBundleWithResource(bundleName, resourceName); + + } + + /** + * @private. + */ + private function supportNonFrameworkApps():void + { + if (initializedForNonFrameworkApp) + return; + initializedForNonFrameworkApp = true; + + if (getLocales().length > 0) + return; + + var applicationDomain:ApplicationDomain = + ApplicationDomain.currentDomain; + + if (!applicationDomain.hasDefinition("_CompiledResourceBundleInfo")) + return; + var c:Class = Class(applicationDomain.getDefinition( + "_CompiledResourceBundleInfo")); + + var locales:Array /* of String */ = c.compiledLocales; + var bundleNames:Array /* of String */ = c.compiledResourceBundleNames; + + installCompiledResourceBundles( + applicationDomain, locales, bundleNames); + + localeChain = locales; + } + + /** + * @private + */ + private function getSystemPreferredLocales():Array /* of String */ + { + var systemPreferences:Array; + + // Capabilities.languages was added in AIR 1.1, + // so this API may not exist. + if (Capabilities["languages"]) + systemPreferences = Capabilities["languages"]; + else + systemPreferences = [ Capabilities.language ]; + + return systemPreferences; + } + + /** + * @private. + */ + private function dumpResourceModule(resourceModule:*):void + { + for each (var bundle:ResourceBundle in resourceModule.resourceBundles) + { + trace(bundle.locale, bundle.bundleName); + for (var p:String in bundle.content) + { + //trace(p, bundle.getObject(p)); + } + } + } + + /** + * @private + */ + private function enterFrameHandler(event:Event):void + { + /* + if (SystemManagerGlobals.topLevelSystemManagers.length) + { + if (SystemManagerGlobals.topLevelSystemManagers[0].currentFrame == 2) + { + SystemManagerGlobals.topLevelSystemManagers[0]. + removeEventListener(Event.ENTER_FRAME, enterFrameHandler); + } + else + return; + } + + var info:Object = SystemManagerGlobals.info; + if (info) + processInfo(info, false); + */ + } +} + +} + +import flash.events.EventDispatcher; +import mx.events.ModuleEvent; +import mx.events.ResourceEvent; +import mx.modules.IModuleInfo; +import mx.resources.IResourceModule; + +//////////////////////////////////////////////////////////////////////////////// +// +// Helper class: ResourceModuleInfo +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * @private + */ +class ResourceModuleInfo +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function ResourceModuleInfo(moduleInfo:IModuleInfo, + readyHandler:Function, + errorHandler:Function) + { + super(); + + this.moduleInfo = moduleInfo; + this.readyHandler = readyHandler; + this.errorHandler = errorHandler; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // errorHandler + //---------------------------------- + + /** + * @private + */ + public var errorHandler:Function; + + //---------------------------------- + // moduleInfo + //---------------------------------- + + /** + * @private + */ + public var moduleInfo:IModuleInfo + + //---------------------------------- + // readyHandler + //---------------------------------- + + /** + * @private + */ + public var readyHandler:Function; + + //---------------------------------- + // resourceModule + //---------------------------------- + + /** + * @private + */ + public var resourceModule:IResourceModule; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Helper class: ResourceEventDispatcher +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * @private + */ +class ResourceEventDispatcher extends EventDispatcher +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function ResourceEventDispatcher(moduleInfo:IModuleInfo) + { + super(); + + moduleInfo.addEventListener( + ModuleEvent.ERROR, moduleInfo_errorHandler, false, 0, true); + + moduleInfo.addEventListener( + ModuleEvent.PROGRESS, moduleInfo_progressHandler, false, 0, true); + + moduleInfo.addEventListener( + ModuleEvent.READY, moduleInfo_readyHandler, false, 0, true); + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function moduleInfo_errorHandler(event:ModuleEvent):void + { + var resourceEvent:ResourceEvent = new ResourceEvent( + ResourceEvent.ERROR, event.bubbles, event.cancelable); + resourceEvent.bytesLoaded = event.bytesLoaded; + resourceEvent.bytesTotal = event.bytesTotal; + resourceEvent.errorText = event.errorText; + dispatchEvent(resourceEvent); + } + + /** + * @private + */ + private function moduleInfo_progressHandler(event:ModuleEvent):void + { + var resourceEvent:ResourceEvent = new ResourceEvent( + ResourceEvent.PROGRESS, event.bubbles, event.cancelable); + resourceEvent.bytesLoaded = event.bytesLoaded; + resourceEvent.bytesTotal = event.bytesTotal; + dispatchEvent(resourceEvent); + } + + /** + * @private + */ + private function moduleInfo_readyHandler(event:ModuleEvent):void + { + var resourceEvent:ResourceEvent = + new ResourceEvent(ResourceEvent.COMPLETE); + dispatchEvent(resourceEvent); + } +} diff --git a/src/mx/rpc/IResponder.as b/src/mx/rpc/IResponder.as new file mode 100644 index 00000000..88b9fa22 --- /dev/null +++ b/src/mx/rpc/IResponder.as @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.rpc +{ + +/** + * This interface provides the contract for any service + * that needs to respond to remote or asynchronous calls. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IResponder +{ + /** + * This method is called by a service when the return value + * has been received. + * While data is typed as Object, it is often + * (but not always) an mx.rpc.events.ResultEvent. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function result(data:Object):void; + + /** + * This method is called by a service when an error has been received. + * While info is typed as Object it is often + * (but not always) an mx.rpc.events.FaultEvent. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function fault(info:Object):void; +} + +} diff --git a/src/mx/states/AddItems.as b/src/mx/states/AddItems.as new file mode 100644 index 00000000..f1a7d67a --- /dev/null +++ b/src/mx/states/AddItems.as @@ -0,0 +1,99 @@ +package mx.states +{ + import flash.display.DisplayObject; + + import mx.collections.IList; + import mx.core.DeferredInstanceFromFunction; + + public class AddItems extends OverrideBase implements IOverride + { + + static public const FIRST:String = "first"; + static public const BEFORE:String = "before"; + static public const AFTER:String = "after"; + static public const LAST:String = "last"; + + public var itemsFactory:*; + public var destination:*; + public var propertyName:String; + public var position:String; + public var relativeTo:Array = []; + + public var destructionPolicy:String; + + private var item:*; // garbage collection? + + override public function initialize():void { + //trace("init"); + } + + override public function apply(parent:Object):void { + var object:* = getOverrideContext(destination, parent); + item = (itemsFactory as DeferredInstanceFromFunction).getInstance(); + if(propertyName == null || propertyName == "mxmlContent") { + parent.addElement(item); + } else if(object[propertyName] is IList) { + applyToList(parent, object, item); + } /*else if(object is Array) { + applyToArray(object as Array, item); + }*/ + } + + override public function remove(parent:Object):void { + var object:* = getOverrideContext(destination, parent); + if(propertyName == null || propertyName == "mxmlContent") { + parent.removeElement(item); + } else if(object[propertyName] is IList) { + removeFromList(parent, object, item); + } + item = null; + } + + private function applyToList(parent:Object, object:*, item:*):void { + var index:int = getInsertIndex(parent, position, object); + (object[propertyName] as IList).addItemAt(item, index); + } + /* + private function applyToArray(array:Array, item:*):void { + var index:int = getInsertIndex(position, array.length); + array[index] = item; + } + */ + + private function removeFromList(parent:Object, object:*, item:*):void { + var index:int = (object[propertyName] as IList).getItemIndex(item); + (object[propertyName] as IList).removeItemAt(index); + } + + private function getInsertIndex(parent:Object, position:String, dest:*):int { + switch(position) { + case FIRST: return 0; + case LAST: return (dest[propertyName] as IList).length; // last = added to new index + case AFTER: return getRelatedIndex(parent, dest)+1; + case BEFORE: return getRelatedIndex(parent, dest); + } + return -1; + } + + private function getRelatedIndex(parent:Object, object:Object):int { + if(relativeTo is Array) { + + for(var i:int = 0; i < relativeTo.length; i++) { + if(parent[relativeTo[i]]) { + return getObjectIndex(parent[relativeTo[i]], object); + } + } + + } + return -1; + } + + private function getObjectIndex(child:Object, parent:Object):int { + if(parent[propertyName] is IList) { + return (parent[propertyName] as IList).getItemIndex(child); + } + return -1; + } + + } +} \ No newline at end of file diff --git a/src/mx/states/IOverride.as b/src/mx/states/IOverride.as new file mode 100644 index 00000000..9cb23b89 --- /dev/null +++ b/src/mx/states/IOverride.as @@ -0,0 +1,13 @@ +package mx.states +{ + import flash.display.DisplayObject; + + public interface IOverride + { + + function initialize():void; + function apply(parent:Object):void; + function remove(parent:Object):void; + + } +} \ No newline at end of file diff --git a/src/mx/states/OverrideBase.as b/src/mx/states/OverrideBase.as new file mode 100644 index 00000000..c01c2827 --- /dev/null +++ b/src/mx/states/OverrideBase.as @@ -0,0 +1,47 @@ +package mx.states +{ + + import flash.events.EventDispatcher; + + public class OverrideBase extends EventDispatcher //implements IOverride + { + + public function initialize():void { + + } + + public function apply(parent:Object):void { + + } + + public function remove(parent:Object):void { + + } + + /** + * @private + * @param parent The document level context for this override. + * @param target The component level context for this override. + */ + protected function getOverrideContext(target:Object, parent:Object):Object + { + if (target == null) + return parent; + + if (target is String) + return parent[target]; + + return target; + } + + + public function initializeFromObject(properties:Object):Object { + for (var p:String in properties) + { + this[p] = properties[p]; + } + return Object(this); + } + + } +} \ No newline at end of file diff --git a/src/mx/states/SetProperty.as b/src/mx/states/SetProperty.as new file mode 100644 index 00000000..fcccbe8a --- /dev/null +++ b/src/mx/states/SetProperty.as @@ -0,0 +1,37 @@ +package mx.states +{ + import flash.display.DisplayObject; + + public class SetProperty extends OverrideBase implements IOverride + { + + public var name:String; + public var target:String; + public var value:*; + + public var isBaseValueDataBound:Boolean; + + private var oldValue:*; + + override public function initialize():void { + trace("init"); + } + + override public function apply(parent:Object):void { + if(parent == null || target == null) { return; } + var item:Object = parent[target]; + if(item == null) { return; } + oldValue = item[name]; + item[name] = value; + } + + override public function remove(parent:Object):void { + if(parent == null || target == null) { return; } + var item:Object = parent[target]; + if(item == null) { return; } + item[name] = oldValue; + oldValue = null; + } + + } +} \ No newline at end of file diff --git a/src/mx/states/SetStyle.as b/src/mx/states/SetStyle.as new file mode 100644 index 00000000..c974b3f4 --- /dev/null +++ b/src/mx/states/SetStyle.as @@ -0,0 +1,29 @@ +package mx.states +{ + import flash.display.DisplayObject; + + public class SetStyle extends OverrideBase implements IOverride + { + + public var name:String; + public var target:String; + public var value:*; + + private var oldValue:*; + + override public function initialize():void { + //trace("init"); + } + + override public function apply(parent:Object):void { + oldValue = parent[target].getStyle(name); + parent[target].setStyle(name, value); + } + + override public function remove(parent:Object):void { + parent[target].setStyle(name, oldValue); + oldValue = null; + } + + } +} \ No newline at end of file diff --git a/src/mx/states/State.as b/src/mx/states/State.as new file mode 100644 index 00000000..8fc901df --- /dev/null +++ b/src/mx/states/State.as @@ -0,0 +1,291 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +// AdobePatentID="B770" +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.states +{ + +import flash.events.EventDispatcher; +import mx.core.mx_internal; +import mx.events.FlexEvent; + +use namespace mx_internal; + +//-------------------------------------- +// Events +//-------------------------------------- + +/** + * Dispatched after a view state has been entered. + * + * @eventType mx.events.FlexEvent.ENTER_STATE + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="enterState", type="mx.events.FlexEvent")] + +/** + * Dispatched just before a view state is exited. + * This event is dispatched before the changes + * to the default view state have been removed. + * + * @eventType mx.events.FlexEvent.EXIT_STATE + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +[Event(name="exitState", type="mx.events.FlexEvent")] + +//-------------------------------------- +// Other metadata +//-------------------------------------- + +[DefaultProperty("overrides")] + +/** + * The State class defines a view state, a particular view of a component. + * For example, a product thumbnail could have two view states; + * a base view state with minimal information, and a rich view state with + * additional information. + * The overrides property specifies a set of child classes + * to add or remove from the base view state, and properties, styles, and event + * handlers to set when the view state is in effect. + * + *

You use the State class in the states property + * of Flex components. + * You can only specify a states property at the root of an + * application or a custom control, not on child controls.

+ * + *

You enable a view state by setting a component's + * currentState property.

+ * + * @mxml + *

The <mx:State> tag has the following attributes:

+ * + *
+ *  <mx:State
+ *  Properties
+ *  basedOn="null"
+ *  name="null"
+ *  overrides="null"
+ *  />
+ *  
+ * + * @see mx.states.AddChild + * @see mx.states.RemoveChild + * @see mx.states.SetEventHandler + * @see mx.states.SetProperty + * @see mx.states.SetStyle + * @see mx.states.Transition + * + * @includeExample examples/StatesExample.mxml + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class State extends EventDispatcher +{ + //include "../core/Version.as"; + + import mx.core.mx_internal; + + /** + * @private + * Version string for this class. + */ + mx_internal static const VERSION:String = "4.1.0.16076"; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function State(properties:Object=null) + { + super(); + + // Initialize from object if provided. + for (var p:String in properties) + { + this[p] = properties[p]; + } + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Initialized flag + */ + private var initialized:Boolean = false; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // basedOn + //---------------------------------- + + [Inspectable(category="General")] + + /** + * The name of the view state upon which this view state is based, or + * null if this view state is not based on a named view state. + * If this value is null, the view state is based on a root + * state that consists of the properties, styles, event handlers, and + * children that you define for a component without using a State class. + * + * @default null + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var basedOn:String; + + //---------------------------------- + // name + //---------------------------------- + + [Inspectable(category="General")] + + /** + * The name of the view state. + * State names must be unique for a given component. + * This property must be set. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var name:String; + + //---------------------------------- + // overrides + //---------------------------------- + + [ArrayElementType("mx.states.IOverride")] + [Inspectable(category="General")] + + /** + * The overrides for this view state, as an Array of objects that implement + * the IOverride interface. These overrides are applied in order when the + * state is entered, and removed in reverse order when the state is exited. + * + *

The following Flex classes implement the IOverride interface and let you + * define the view state characteristics:

+ *
    + *
  • AddChild
  • + *
  • RemoveChild
  • + *
  • SetEventHandler
  • + *
  • SetProperty
  • + *
  • SetStyle
  • + *
+ * + *

The overrides property is the default property of the + * State class. You can omit the <mx:overrides> tag + * and its child <mx:Array>tag if you use MXML tag + * syntax to define the overrides.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var overrides:Array /* of IOverride */ = []; + + //---------------------------------- + // stateGroups + //---------------------------------- + + [ArrayElementType("String")] + [Inspectable(category="General")] + + /** + * The state groups that this view state belongs to as an array of String. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public var stateGroups:Array /* of String */ = []; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Initialize this state and all of its overrides. + */ + mx_internal function initialize():void + { + if (!initialized) + { + initialized = true; + for (var i:int = 0; i < overrides.length; i++) + { + IOverride(overrides[i]).initialize(); + } + } + } + + /** + * @private + * Dispatches the "enterState" event. + */ + mx_internal function dispatchEnterState():void + { + if (hasEventListener(FlexEvent.ENTER_STATE)) + dispatchEvent(new FlexEvent(FlexEvent.ENTER_STATE)); + } + + /** + * @private + * Dispatches the "exitState" event. + */ + mx_internal function dispatchExitState():void + { + if (hasEventListener(FlexEvent.EXIT_STATE)) + dispatchEvent(new FlexEvent(FlexEvent.EXIT_STATE)); + } +} + +} diff --git a/src/mx/styles/CSSCondition.as b/src/mx/styles/CSSCondition.as deleted file mode 100644 index 031fe672..00000000 --- a/src/mx/styles/CSSCondition.as +++ /dev/null @@ -1,65 +0,0 @@ -package mx.styles -{ - import flash.display.DisplayObject; - - import reflex.styles.IStylable; - - public class CSSCondition - { - protected static var onSpace:RegExp = new RegExp("\\s+"); - protected var _kind:String; - protected var _value:String; - protected var _specificity:uint; - - public function CSSCondition(kind:String, value:String) - { - _kind = kind; - _value = value; - _specificity = (kind == "id" ? 100 : 10); - } - - public function get kind():String - { - return _kind; - } - - public function get value():String - { - return _value; - } - - public function get specificity():uint - { - return _specificity; - } - - public function match(obj:Object):Boolean - { - var stylable:IStylable = obj as IStylable; - var displayObject:DisplayObject = obj as DisplayObject; - - if (stylable) { - if (_kind == "id") return stylable.id == _value; - if (_kind == "class") return stylable.styleName.split(onSpace).indexOf(_value) != -1; - if (_kind == "pseudo") { - try { - if (displayObject[_value] is Boolean) return displayObject[_value]; - } catch (e:Error) {} - return stylable.state == _value; - } - } else if (_kind == "id" && displayObject) { - return displayObject.name == _value; - } - - return false; - } - - public function toString():String - { - if (_kind == "id") return "#" + _value; - if (_kind == "class") return "." + _value; - if (_kind == "pseudo") return ":" + _value; - return ""; - } - } -} \ No newline at end of file diff --git a/src/mx/styles/CSSSelector.as b/src/mx/styles/CSSSelector.as deleted file mode 100644 index c1b8c877..00000000 --- a/src/mx/styles/CSSSelector.as +++ /dev/null @@ -1,139 +0,0 @@ -package mx.styles -{ - import flash.display.DisplayObject; - import flash.utils.getDefinitionByName; - import flash.utils.getQualifiedClassName; - import flash.utils.getQualifiedSuperclassName; - - - public class CSSSelector - { - protected var _property:String; - protected var _type:String; - protected var _conditions:Array; - protected var _ancestor:CSSSelector; - protected var _specificity:uint; - - public function CSSSelector(type:String, conditions:Array = null, ancestor:CSSSelector = null) - { - if (!type || type == "global") type = "*"; - _type = type.split(".").pop(); - _conditions = conditions; - _ancestor = ancestor; - _specificity = (type == "*" ? 1 : 0); - if (type != "*" && type.substr(0, 1).toLowerCase() == type.substr(0, 1)) { - _property = type; // if this isn't capitalized, it may be a property. - if (ancestor && ancestor.property) { - _property + ancestor.property + "." + _property; - } - } - init(); - } - - protected function init():void - { - if (_conditions) { - for each (var condition:CSSCondition in _conditions) { - _specificity += condition.specificity; - } - _conditions.sortOn("value"); - _conditions.sortOn("kind"); - _conditions.sortOn("specificity", Array.NUMERIC); - } - } - - public function get property():String - { - return _property; - } - - public function get type():String - { - return _type; - } - - public function get conditions():Array - { - return _conditions; - } - - public function get ancestor():CSSSelector - { - return _ancestor; - } - - public function get specificity():uint - { - return _specificity; - } - - public function match(obj:Object, matchAncestors:Boolean = true):Boolean - { - var match:Boolean = false; - var condition:CSSCondition; - var parent:DisplayObject = obj is DisplayObject ? DisplayObject(obj).parent : null; - - // if this is a property - if (ancestor && matchAncestors && _property) { - return ancestor.match(obj, matchAncestors); - } else if (_type != "*" && getTypes(obj).indexOf(_type) == -1) { - return false; - } - - if (conditions) { - // must be IStylable to match any of the conditions - for each (condition in conditions) { - if (!condition.match(obj)) return false; - } - } - - if (ancestor && matchAncestors && obj is DisplayObject) { - var selector:CSSSelector = ancestor; - while (selector != null) { - while (parent != null) { - if (selector.match(parent, false)) { - selector = selector.ancestor; - break; - } - parent = parent.parent; - } - // if we didn't match all the ancestors - if (parent == null) return false; - } - } - - return true; - } - - public function getTypes(obj:Object):Array - { - var className:String = getQualifiedClassName(obj); - var types:Array = [className.split("::").pop()]; - - while (className != "Object") { - className = getQualifiedSuperclassName(obj); - types.push(className.split("::").pop()); - obj = getDefinitionByName(className); - } - - return types; - } - - public function toString():String - { - var selectors:Array = []; - var selector:CSSSelector = this; - while (selector) { - var str:String = selector.type; - if (selector.conditions) { - for each (var condition:CSSCondition in selector.conditions) { - str += condition; - } - } - selectors.unshift(str); - selector = selector.ancestor; - } - return selectors.join(" "); - } - } -} \ No newline at end of file diff --git a/src/mx/styles/CSSStyleDeclaration.as b/src/mx/styles/CSSStyleDeclaration.as deleted file mode 100644 index a3579956..00000000 --- a/src/mx/styles/CSSStyleDeclaration.as +++ /dev/null @@ -1,69 +0,0 @@ -package mx.styles -{ - import mx.core.mx_internal; - - public class CSSStyleDeclaration - { - public var index:int; - protected var _styles:Object = {}; - protected var _selector:CSSSelector; - - public function CSSStyleDeclaration(selector:CSSSelector = null, manager:IStyleManager2 = null) - { - if (selector) { - manager.setStyleDeclaration(selector.toString(), this); - } - _selector = selector; - } - - public function get selector():CSSSelector - { - return _selector; - } - - public function get specificity():uint - { - return _selector.specificity; - } - - public function get styles():Object - { - return _styles; - } - - public function getStyle(styleProp:String):* - { - return _styles[styleProp]; - } - - mx_internal function setStyle(styleProp:String, value:*):void - { - _styles[styleProp] = value; - } - - // always return null so that the new factory will be set - public function get factory():Function - { - return null; - } - - public function get defaultFactory():Function - { - return null; - } - - public function set defaultFactory(value:Function):void - { - - } - - public function set factory(value:Function):void - { - var styles:Object = {}; - value.call(styles); - for (var i:String in styles) { - mx_internal::setStyle(i, styles[i]); - } - } - } -} \ No newline at end of file diff --git a/src/mx/styles/IStyleManager.as b/src/mx/styles/IStyleManager.as new file mode 100644 index 00000000..0d485d52 --- /dev/null +++ b/src/mx/styles/IStyleManager.as @@ -0,0 +1,559 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.styles +{ + +import flash.events.IEventDispatcher; +import flash.system.ApplicationDomain; +import flash.system.SecurityDomain; + +/** + * The IStyleManager class manages the following: + *
    + *
  • Which CSS style properties the class inherits
  • + *
  • Which style properties are colors, and therefore get special handling
  • + *
  • A list of strings that are aliases for color values
  • + *
+ * + * This interface was used by Flex 2.0.1. + * Flex 3 now uses IStyleManager2 instead. + * + * @see mx.styles.CSSStyleDeclaration + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 2.0.1 + * + */ +public interface IStyleManager +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // inheritingStyles + //---------------------------------- + + /** + * @private + * Set of inheriting non-color styles. + * This is not the complete set from CSS. + * Some of the omitted we don't support at all, + * others may be added later as needed. + * The method registerInheritingStyle() adds to this set + * and isInheritingStyle() queries this set. + */ + function get inheritingStyles():Object; + + /** + * @private + */ + function set inheritingStyles(value:Object):void; + + //---------------------------------- + // stylesRoot + //---------------------------------- + + /** + * @private + * The root of all proto chains used for looking up styles. + * This object is initialized once by initProtoChainRoots() and + * then updated by calls to setStyle() on the global CSSStyleDeclaration. + * It is accessed by code that needs to construct proto chains, + * such as the initProtoChain() method of UIComponent. + */ + function get stylesRoot():Object; + + /** + * @private + */ + function set stylesRoot(value:Object):void; + + //---------------------------------- + // typeSelectorCache + //---------------------------------- + + /** + * @private + */ + function get typeSelectorCache():Object; + + /** + * @private + */ + function set typeSelectorCache(value:Object):void; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Gets the CSSStyleDeclaration object that stores the rules + * for the specified CSS selector. + * + *

If the selector parameter starts with a period (.), + * the returned CSSStyleDeclaration is a class selector and applies only to those instances + * whose styleName property specifies that selector + * (not including the period). + * For example, the class selector ".bigMargins" + * applies to any UIComponent whose styleName + * is "bigMargins".

+ * + *

If the selector parameter does not start with a period, + * the returned CSSStyleDeclaration is a type selector and applies to all instances + * of that type. + * For example, the type selector "Button" + * applies to all instances of Button and its subclasses.

+ * + *

The global selector is similar to a type selector + * and does not start with a period.

+ * + * @param selector The name of the CSS selector. + * + * @return The style declaration whose name matches the selector property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getStyleDeclaration(selector:String):CSSStyleDeclaration; + + /** + * Sets the CSSStyleDeclaration object that stores the rules + * for the specified CSS selector. + * + *

If the selector parameter starts with a period (.), + * the specified selector is a "class selector" and applies only to those instances + * whose styleName property specifies that selector + * (not including the period). + * For example, the class selector ".bigMargins" + * applies to any UIComponent whose styleName + * is "bigMargins".

+ * + *

If the selector parameter does not start with a period, + * the specified selector is a "type selector" and applies to all instances + * of that type. + * For example, the type selector "Button" + * applies to all instances of Button and its subclasses.

+ * + *

The global selector is similar to a type selector + * and does not start with a period.

+ * + * @param selector The name of the CSS selector. + * @param styleDeclaration The new style declaration. + * @param update Set to true to force an immediate update of the styles; internally, Flex + * calls the styleChanged() method of UIComponent. + * Set to false to avoid an immediate update of the styles in the application. + * + *

The styles will be updated the next time one of the following methods is called with + * the update property set to true: + *

    + *
  • clearStyleDeclaration()
  • + *
  • loadStyleDeclarations()
  • + *
  • setStyleDeclaration()
  • + *
  • unloadStyleDeclarations()
  • + *
+ *

+ * + *

Typically, if you call the one of these methods multiple times, + * you set this property to true only on the last call, + * so that Flex does not call the styleChanged() method multiple times.

+ * + *

If you call the getStyle() method, Flex returns the style value + * that was last applied to the UIComponent through a call to the styleChanged() method. + * The component's appearance might not reflect the value returned by the getStyle() method. This occurs + * because one of these style declaration methods might not yet have been called with the + * update property set to true.

+ * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function setStyleDeclaration(selector:String, + styleDeclaration:CSSStyleDeclaration, + update:Boolean):void; + + /** + * Clears the CSSStyleDeclaration object that stores the rules + * for the specified CSS selector. + * + *

If the specified selector is a class selector (for example, ".bigMargins" or ".myStyle"), + * you must be sure to start the + * selector property with a period (.).

+ * + *

If the specified selector is a type selector (for example, "Button"), do not start the + * selector property with a period.

+ * + *

The global selector is similar to a type selector + * and does not start with a period.

+ * + * @param selector The name of the CSS selector to clear. + * @param update Set to true to force an immediate update of the styles. + * Set to false to avoid an immediate update of the styles in the application. + * For more information about this method, see the description in the setStyleDeclaration() + * method. + * + * @see #setStyleDeclaration() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function clearStyleDeclaration(selector:String, update:Boolean):void; + + /** + * Adds to the list of styles that can inherit values + * from their parents. + * + *

Note: Ensure that you avoid using duplicate style names, as name + * collisions can result in decreased performance if a style that is + * already used becomes inheriting.

+ * + * @param styleName The name of the style that is added to the list of styles that can inherit values. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function registerInheritingStyle(styleName:String):void; + + /** + * Tests to see if a style is inheriting. + * + * @param styleName The name of the style that you test to see if it is inheriting. + * + * @return Returns true if the specified style is inheriting. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function isInheritingStyle(styleName:String):Boolean; + + /** + * Test to see if a TextFormat style is inheriting. + * + * @param styleName The name of the style that you test to see if it is inheriting. + * + * @return Returns true if the specified TextFormat style + * is inheriting. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function isInheritingTextFormatStyle(styleName:String):Boolean; + + /** + * Adds to the list of styles which may affect the measured size + * of the component. + * When one of these styles is set with setStyle(), + * the invalidateSize() method is automatically called on the component + * to make its measured size get recalculated later. + * + * @param styleName The name of the style that you add to the list. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function registerSizeInvalidatingStyle(styleName:String):void; + + /** + * Tests to see if a style changes the size of a component. + * + *

When one of these styles is set with the setStyle() method, + * the invalidateSize() method is automatically called on the component + * to make its measured size get recalculated later.

+ * + * @param styleName The name of the style to test. + * + * @return Returns true if the specified style is one + * which may affect the measured size of the component. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function isSizeInvalidatingStyle(styleName:String):Boolean; + + /** + * Adds to the list of styles which may affect the measured size + * of the component's parent container. + *

When one of these styles is set with setStyle(), + * the invalidateSize() method is automatically called on the component's + * parent container to make its measured size get recalculated + * later.

+ * + * @param styleName The name of the style to register. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function registerParentSizeInvalidatingStyle(styleName:String):void; + + /** + * Tests to see if the style changes the size of the component's parent container. + * + *

When one of these styles is set with setStyle(), + * the invalidateSize() method is automatically called on the component's + * parent container to make its measured size get recalculated + * later.

+ * + * @param styleName The name of the style to test. + * + * @return Returns true if the specified style is one + * which may affect the measured size of the component's + * parent container. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function isParentSizeInvalidatingStyle(styleName:String):Boolean; + + /** + * Adds to the list of styles which may affect the appearance + * or layout of the component's parent container. + * When one of these styles is set with setStyle(), + * the invalidateDisplayList() method is auomatically called on the component's + * parent container to make it redraw and/or relayout its children. + * + * @param styleName The name of the style to register. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function registerParentDisplayListInvalidatingStyle(styleName:String):void; + + /** + * Tests to see if this style affects the component's parent container in + * such a way as to require that the parent container redraws itself when this style changes. + * + *

When one of these styles is set with setStyle(), + * the invalidateDisplayList() method is auomatically called on the component's + * parent container to make it redraw and/or relayout its children.

+ * + * @param styleName The name of the style to test. + * + * @return Returns true if the specified style is one + * which may affect the appearance or layout of the component's + * parent container. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function isParentDisplayListInvalidatingStyle(styleName:String):Boolean; + + /** + * Adds a color name to the list of aliases for colors. + * + * @param colorName The name of the color to add to the list; for example, "blue". + * If you later access this color name, the value is not case-sensitive. + * + * @param colorValue Color value, for example, 0x0000FF. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function registerColorName(colorName:String, colorValue:uint):void; + + /** + * Tests to see if the given String is an alias for a color value. For example, + * by default, the String "blue" is an alias for 0x0000FF. + * + * @param colorName The color name to test. This parameter is not case-sensitive. + * + * @return Returns true if colorName is an alias + * for a color. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function isColorName(colorName:String):Boolean; + + /** + * Returns the numeric RGB color value that corresponds to the + * specified color string. + * The color string can be either a case-insensitive color name + * such as "red", "Blue", or + * "haloGreen", a hexadecimal value such as 0xFF0000, or a #-hexadecimal String + * such as "#FF0000". + * + *

This method returns a uint, such as 4521830, that represents a color. You can convert + * this uint to a hexadecimal value by passing the numeric base (in this case, 16), to + * the uint class's toString() method, as the following example shows:

+ *
+     *  import mx.styles.StyleManager;
+     *  private function getNewColorName():void {
+     *      StyleManager.registerColorName("soylentGreen",0x44FF66);
+     *      trace(StyleManager.getColorName("soylentGreen").toString(16));
+     *  }
+     *  
+ * + * @param colorName The color name. + * + * @return Returns a uint that represents the color value or NOT_A_COLOR + * if the value of the colorName property is not an alias for a color. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getColorName(colorName:Object):uint; + + /** + * Converts each element of the colors Array from a color name + * to a numeric RGB color value. + * Each color String can be either a case-insensitive color name + * such as "red", "Blue", or + * "haloGreen", a hexadecimal value such as 0xFF0000, or a #-hexadecimal String + * such as "#FF0000".. + * + * @param colors An Array of color names. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function getColorNames(colors:Array /* of Number or String */):void; + + /** + * Determines if a specified parameter is a valid style property. For example: + * + *
+     *  trace(StyleManager.isValidStyleValue(myButton.getStyle("color")).toString());
+     *  
+ * + *

This can be useful because some styles can be set to values + * such as 0, NaN, + * the empty String (""), or null, which can + * cause an if (value) test to fail.

+ * + * @param value The style property to test. + * + * @return If you pass the value returned by a getStyle() method call + * to this method, it returns true if the style + * was set and false if it was not set. + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function isValidStyleValue(value:*):Boolean; + + /** + * Loads a style SWF. + * + * @param url Location of the style SWF. + * + * @param update Set to true to force + * an immediate update of the styles. + * Set to false to avoid an immediate update + * of the styles in the application. + * This parameter is optional and defaults to true + * For more information about this parameter, see the description + * in the setStyleDeclaration() method. + * + * @param trustContent Obsolete, no longer used. + * This parameter is optional and defaults to false. + * + * @param applicationDomain The ApplicationDomain passed to the + * load() method of the IModuleInfo that loads the style SWF. + * This parameter is optional and defaults to null. + * + * @param securityDomain The SecurityDomain passed to the + * load() method of the IModuleInfo that loads the style SWF. + * This parameter is optional and defaults to null. + * + * @return An IEventDispatcher implementation that supports + * StyleEvent.PROGRESS, StyleEvent.COMPLETE, and + * StyleEvent.ERROR. + * + * @see #setStyleDeclaration() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4 + */ + function loadStyleDeclarations( + url:String, update:Boolean = true, + trustContent:Boolean = false, + applicationDomain:ApplicationDomain = null, + securityDomain:SecurityDomain = null):IEventDispatcher; + + /** + * Unloads a style SWF. + * + * @param url Location of the style SWF. + * @param update Set to true to force an immediate update of the styles. + * Set to false to avoid an immediate update of the styles in the application. + * For more information about this method, see the description in the setStyleDeclaration() + * method. + * + * @see #setStyleDeclaration() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function unloadStyleDeclarations( + url:String, update:Boolean = true):void; + + /** + * @private + * This method is called by code autogenerated by the MXML compiler, + * after StyleManager.styles is popuplated with CSSStyleDeclarations. + */ + function initProtoChainRoots():void; + + /** + * @private + * After an entire selector is added, replaced, or removed, + * this method updates all the DisplayList trees. + */ + function styleDeclarationsChanged():void; +} + +} diff --git a/src/mx/styles/IStyleManager2.as b/src/mx/styles/IStyleManager2.as index 5768e8b8..bd83fe90 100644 --- a/src/mx/styles/IStyleManager2.as +++ b/src/mx/styles/IStyleManager2.as @@ -1,10 +1,208 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + package mx.styles { - public interface IStyleManager2 - { - - function setStyleDeclaration(selector:String, styleDeclaration:CSSStyleDeclaration):void; - function getStyleDeclaration(selector:String):CSSStyleDeclaration; - //function initProtoChainRoots():void; - } -} \ No newline at end of file + +import flash.events.IEventDispatcher; +import flash.system.ApplicationDomain; +import flash.system.SecurityDomain; + +/** + * The IStyleManager2 class manages the following: + *
    + *
  • Which CSS style properties the class inherits
  • + *
  • Which style properties are colors, and therefore get special handling
  • + *
  • A list of strings that are aliases for color values
  • + *
+ * + * @see mx.styles.CSSStyleDeclaration + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + * + */ +public interface IStyleManager2 extends IStyleManager +{ + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + /** + * The style manager that is the parent of this StyleManager. + * + * @return the parent StyleManager or null if this is the top-level StyleManager. + */ + function get parent():IStyleManager2; + + //---------------------------------- + // qualifiedTypeSelectors + //---------------------------------- + + /** + * @private + * Qualified type selectors were added in Flex 4 to support styling + * components with the same local name, e.g. 'spark.components.Button'. + * Prior to this type selectors were always unqualified class names e.g. + * 'Button'. To ease migration of Flex 3 application, this property can + * control whether CSS type selectors must be fully qualified class names + * when the compatibility version is 4 or later. + */ + function get qualifiedTypeSelectors():Boolean; + + /** + * @private + */ + function set qualifiedTypeSelectors(value:Boolean):void; + + //---------------------------------- + // selectors + //---------------------------------- + + /** + * Returns an Array of all the CSS selectors that are registered with the StyleManager. + * You can pass items in this Array to the getStyleDeclaration() method to get the corresponding CSSStyleDeclaration object. + * Class selectors are prepended with a period. + * + * @return An Array of all of the selectors + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + function get selectors():Array; + + //---------------------------------- + // typeHierarchyCache + //---------------------------------- + + /** + * @private + */ + function get typeHierarchyCache():Object; + + /** + * @private + */ + function set typeHierarchyCache(value:Object):void; + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * Gets the list of style declarations for the given subject. The subject + * is the right most simple type selector in a potential selector chain. + * + * @param subject The style subject. + * @return Object map of StyleDeclarations for this subject. The object + * has four properties: class for class selectors, + * id for id selectors, pseudo for pseudo selectors and unconditional + * for selectors without conditions + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.6 + */ + function getStyleDeclarations(subject:String):Object; + + /** + * Gets a CSSStyleDeclaration object that stores the rules + * for the specified CSS selector. The CSSStyleDeclaration object is + * created by merging the properties of the specified CSS selector in + * this style manager with the properties of any parent style managers. + * + *

If the selector parameter starts with a period (.), + * the returned CSSStyleDeclaration is a class selector and applies only to those instances + * whose styleName property specifies that selector + * (not including the period). + * For example, the class selector ".bigMargins" + * applies to any UIComponent whose styleName + * is "bigMargins".

+ * + *

If the selector parameter does not start with a period, + * the returned CSSStyleDeclaration is a type selector and applies to all instances + * of that type. + * For example, the type selector "Button" + * applies to all instances of Button and its subclasses.

+ * + *

The global selector is similar to a type selector + * and does not start with a period.

+ * + * @param selector The name of the CSS selector. + * + * @return The style declaration whose name matches the selector property. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + function getMergedStyleDeclaration(selector:String):CSSStyleDeclaration; + + /** + * @private + * Determines whether any of the selectors declared a pseudo selector + * for the given state. This is used to avoid unnecessary style + * regeneration between state changes. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + function hasPseudoCondition(value:String):Boolean; + + /** + * @private + * Determines whether any of the selectors registered with the style + * manager have been advanced selectors (descendant selector, id selector, + * non-global class selector, or pseudo selector). + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + function hasAdvancedSelectors():Boolean; + + /** + * @private + * In Flex 2, the static method StyleManager.loadStyleDeclarations() + * had three parameters and called loadStyleDeclarations() + * on IStyleManager. + * In Flex 3, the static method has four parameters and calls + * this method. + */ + function loadStyleDeclarations2( + url:String, update:Boolean = true, + applicationDomain:ApplicationDomain = null, + securityDomain:SecurityDomain = null):IEventDispatcher; + + /** + * @private + * Used in media query handling. + * @param value normalized media query string + * @returns true if valid media + */ + function acceptMediaList(value:String):Boolean; +} + +} diff --git a/src/mx/styles/StyleManager.as b/src/mx/styles/StyleManager.as deleted file mode 100644 index 2c66cd2c..00000000 --- a/src/mx/styles/StyleManager.as +++ /dev/null @@ -1,180 +0,0 @@ -package mx.styles -{ - import flash.display.DisplayObject; - import flash.display.Stage; - import flash.events.Event; - import flash.utils.Dictionary; - - import flight.observers.PropertyChange; - - import mx.core.IMXMLObject; - - import reflex.styles.IStylable; - - public class StyleManager implements IMXMLObject, IStyleManager2 - { - protected static var collapseSpace:RegExp = new RegExp("\\t+| {2,}", "g"); - protected static var addMissingTypes:RegExp = new RegExp("(^| )(#|\\.|:)", "g"); - public static var selectors:Object = {}; - public static var declarations:Array = []; - protected static var instance:IStyleManager2 = new StyleManager(); - protected static var targetChanges:Dictionary = new Dictionary(true); - PropertyChange.addObserver(DisplayObject, "*", null, propertyChange); - - public static function getStyleManager(type:String):IStyleManager2 - { - return instance; - } - - //public function initProtoChainRoots():void {}; - - /** - * Grab a reference to the stage so we can set styles when items are - * added. - */ - public function initialized(document:Object, id:String):void - { - init(document.stage); - } - - public static function init(stage:Stage):void - { - stage.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, true); - stage.addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage, true); - } - - protected static function onAddedToStage(event:Event):void - { - var displayObject:DisplayObject = event.target as DisplayObject; - updateStyles(displayObject); - if (displayObject is IStylable) { - displayObject.addEventListener("stateChange", onStateChange); - } - } - - protected static function onRemovedFromStage(event:Event):void - { - var displayObject:DisplayObject = event.target as DisplayObject; - if (displayObject is IStylable) { - displayObject.removeEventListener("stateChange", onStateChange); - } - } - - protected static function onStateChange(event:Event):void - { - var displayObject:DisplayObject = event.target as DisplayObject; - updateStyles(displayObject); - } - - protected static function updateStyles(displayObject:DisplayObject):void - { - var styles:Object = getStyles(displayObject); - var i:String; - var changed:Object = targetChanges[displayObject]; - - // set classes first, then properties - for (i in styles) { - if (styles[i] is Class) { - if (changed && changed[i.split(".").shift()]) continue; - if (!applyStyle(displayObject, i.split("."), new styles[i]())) { - trace(i, "was not able to be set on", displayObject); - } - } - } - - // now set properties - for (i in styles) { - if ( !(styles[i] is Class) ) { - if (changed && changed[i.split(".").shift()]) continue; - if (!applyStyle(displayObject, i.split("."), styles[i])) { - trace(i, "was not able to be set on", displayObject); - } - } - } - } - - protected static function applyStyle(obj:Object, props:Array, value:*):Boolean - { - var len:uint = props.length; - try { - for (var j:uint = 0; j < len; j++) { - var name:String = props[j]; - if (j < len - 1) { - obj = obj[name]; - } else { - obj[name] = value; - } - } - return true; - } catch (e:Error) {trace(e);} - - return false; - } - - public static function getStyles(obj:Object):Object - { - var declaration:CSSStyleDeclaration; - - var matches:Array = []; - for each (declaration in declarations) { - if (declaration.selector.match(obj)) { - matches.push(declaration); - } - } - matches.sortOn("specificity", Array.NUMERIC); - var styles:Object = {}; - for each (declaration in matches) { - var prefix:String = ""; - if (declaration.selector.property) { - prefix = declaration.selector.property + "."; - } - mergeObjects(styles, declaration.styles, prefix); - } - return styles; - } - - protected static function mergeObjects(mergeTo:Object, mergeFrom:Object, prefix:String = ""):Object - { - for (var name:String in mergeFrom) { - mergeTo[prefix + name] = mergeFrom[name]; - } - return mergeTo; - } - - public static function registerInheritingStyle(...rest):void - { - trace("Registering inheriting style", rest); - } - - public function getStyleDeclaration(selector:String):CSSStyleDeclaration - { - return null; - } - - - public function setStyleDeclaration(selector:String, styleDeclaration:CSSStyleDeclaration):void - { - trace("style declarations"); - declarations.push(styleDeclaration); - } - - - public static function clearStyleDeclaration(selector:String, unload:Boolean):void - { - - } - - private static var propertyChangeReference:Function = propertyChange; // reference so doesn't slip out of PropertyChange memory - - protected static function propertyChange(target:Object, property:String, oldValue:*, newValue:*):void - { - trace(property, "has changed on", target); - var changes:Object = targetChanges[target]; - if (changes == null) { - targetChanges[target] = changes = {}; - } - - changes[property] = (newValue != null || !isNaN(newValue)); - } - } -} \ No newline at end of file diff --git a/src/mx/styles/StyleManagerImpl.as b/src/mx/styles/StyleManagerImpl.as new file mode 100644 index 00000000..fc3dda27 --- /dev/null +++ b/src/mx/styles/StyleManagerImpl.as @@ -0,0 +1,1849 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.styles +{ + +import flash.display.DisplayObject; +import flash.events.EventDispatcher; +import flash.display.LoaderInfo; +import flash.events.IEventDispatcher; +import flash.events.TimerEvent; +import flash.system.ApplicationDomain; +import flash.system.SecurityDomain; +import flash.utils.Timer +import flash.utils.describeType; + +import mx.core.FlexVersion; +import mx.core.IFlexModuleFactory; +import mx.core.mx_internal; +import mx.events.FlexChangeEvent; +import mx.events.ModuleEvent; +import mx.events.StyleEvent; +import mx.managers.ISystemManager; +import mx.managers.SystemManagerGlobals; +import mx.modules.IModuleInfo; +import mx.modules.ModuleManager +import mx.resources.IResourceManager; +import mx.resources.ResourceManager; +import mx.styles.IStyleModule; +import mx.styles.IStyleManager2; +import mx.events.Request; +import mx.utils.MediaQueryParser; +import flash.events.Event; + +use namespace mx_internal; + +[ExcludeClass] + +[ResourceBundle("styles")] + +/** + * @private + */ +public class StyleManagerImpl extends EventDispatcher implements IStyleManager2 +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + /** + * @private + */ + private static var instance:IStyleManager2; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public static function getInstance():IStyleManager2 + { + if (!instance) + { + // In Flex 4 each application/module creates its own style manager. + // There will be no style manager if the application/module was compiled for + // Flex 3 compatibility. In that case create there will be no instance + // associated with the top-level application so create a new instance. + instance = IStyleManager2(IFlexModuleFactory(SystemManagerGlobals.topLevelSystemManagers[0]). + getImplementation("mx.styles::IStyleManager2")); + + if (!instance) + instance = new StyleManagerImpl(SystemManagerGlobals.topLevelSystemManagers[0]); + } + + return instance; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * + * @param moduleFactory The module factory that is creating this instance. May not be null. + */ + public function StyleManagerImpl(moduleFactory:IFlexModuleFactory) + { + super(); + + this.moduleFactory = moduleFactory; + this.moduleFactory.registerImplementation("mx.styles::IStyleManager2", this); + + // get our parent styleManager + if (moduleFactory is DisplayObject) + { + var request:Request = new Request(Request.GET_PARENT_FLEX_MODULE_FACTORY_REQUEST); + DisplayObject(moduleFactory).dispatchEvent(request); + var parentModuleFactory:IFlexModuleFactory = request.value as IFlexModuleFactory; + if (parentModuleFactory) + { + _parent = IStyleManager2(parentModuleFactory. + getImplementation("mx.styles::IStyleManager2")); + if (_parent is IEventDispatcher) + { + IEventDispatcher(_parent).addEventListener(FlexChangeEvent.STYLE_MANAGER_CHANGE, styleManagerChangeHandler, false, 0, true); + } + } + + } + } + + //-------------------------------------------------------------------------- + // + // Variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * Used to assign the selectorIndex in CSSStyleDeclaration so we can track + * the order they were added to the StyleManager. + * MatchStyleDeclarations has to return the declarations in the order + * they were declared + */ + private var selectorIndex:int = 0; + + /** + * @private + */ + private var mqp:MediaQueryParser; + + /** + * @private + * Set of inheriting non-color styles. + * This is not the complete set from CSS. + * Some of the omitted we don't support at all, + * others may be added later as needed. + * The isInheritingTextFormatStyle() method queries this set. + */ + private var inheritingTextFormatStyles:Object = + { + align: true, + bold: true, + color: true, + font: true, + indent: true, + italic: true, + size: true + }; + + /** + * @private + * Set of styles for which setStyle() causes + * invalidateSize() to be called on the component. + * The method registerSizeInvalidatingStyle() adds to this set + * and isSizeInvalidatingStyle() queries this set. + */ + private var sizeInvalidatingStyles:Object = + { + alignmentBaseline: true, + baselineShift: true, + blockProgression: true, + borderStyle: true, + borderThickness: true, + breakOpportunity : true, + cffHinting: true, + columnCount: true, + columnGap: true, + columnWidth: true, + digitCase: true, + digitWidth: true, + direction: true, + dominantBaseline: true, + firstBaselineOffset: true, + fontAntiAliasType: true, + fontFamily: true, + fontGridFitType: true, + fontLookup: true, + fontSharpness: true, + fontSize: true, + fontStyle: true, + fontThickness: true, + fontWeight: true, + headerHeight: true, + horizontalAlign: true, + horizontalGap: true, + justificationRule: true, + justificationStyle: true, + kerning: true, + leading: true, + leadingModel: true, + letterSpacing: true, + ligatureLevel: true, + lineBreak: true, + lineHeight: true, + lineThrough: true, + listAutoPadding: true, + listStylePosition: true, + listStyleType: true, + locale: true, + marginBottom: true, + marginLeft: true, + marginRight: true, + marginTop: true, + paddingBottom: true, + paddingLeft: true, + paddingRight: true, + paddingTop: true, + paragraphEndIndent: true, + paragraphStartIndent: true, + paragraphSpaceAfter: true, + paragraphSpaceBefore: true, + renderingMode: true, + strokeWidth: true, + tabHeight: true, + tabWidth: true, + tabStops: true, + textAlign: true, + textAlignLast: true, + textDecoration: true, + textIndent: true, + textJustify: true, + textRotation: true, + tracking: true, + trackingLeft: true, + trackingRight: true, + typographicCase: true, + verticalAlign: true, + verticalGap: true, + wordSpacing:true, + whitespaceCollapse: true + } + + /** + * @private + * Set of styles for which setStyle() causes + * invalidateSize() to be called on the component's parent. + * The method registerParentSizeInvalidatingStyle() adds to this set + * and isParentSizeInvalidatingStyle() queries this set. + */ + private var parentSizeInvalidatingStyles:Object = + { + baseline: true, + bottom: true, + horizontalCenter: true, + left: true, + right: true, + top: true, + verticalCenter: true + } + + /** + * @private + * Set of styles for which setStyle() causes + * invalidateDisplayList() to be called on the component's parent. + * The method registerParentDisplayListInvalidatingStyle() adds to this set + * and isParentDisplayListInvalidatingStyle() queries this set. + */ + private var parentDisplayListInvalidatingStyles:Object = + { + baseline: true, + bottom: true, + horizontalCenter: true, + left: true, + right: true, + top: true, + verticalCenter: true + } + + /** + * @private + * Set of color names. + * The method registerColorName() adds to this set + * and isColorName() queries this set. + * All color names in this set are lowercase in order to support + * case-insensitive mapping in the StyleManager methods getColorName(), + * getColorNames(), registerColorName(), and isColorName(). + * We handle color names at runtime in a case-insensitive way + * because the MXML compiler does this at compile time, + * in conformance with the CSS spec. + */ + private var colorNames:Object = + { + transparent: "transparent", + black: 0x000000, + blue: 0x0000FF, + green: 0x008000, + gray: 0x808080, + silver: 0xC0C0C0, + lime: 0x00FF00, + olive: 0x808000, + white: 0xFFFFFF, + yellow: 0xFFFF00, + maroon: 0x800000, + navy: 0x000080, + red: 0xFF0000, + purple: 0x800080, + teal: 0x008080, + fuchsia: 0xFF00FF, + aqua: 0x00FFFF, + magenta: 0xFF00FF, + cyan: 0x00FFFF, + + // IMPORTANT: Theme colors must also be updated + // in the Flex compiler's CSS parser + // (in \src\java\macromedia\css\Descriptor.java) + // and possibly other places as well. Grep for them! + halogreen: 0x80FF4D, + haloblue: 0x009DFF, + haloorange: 0xFFB600, + halosilver: 0xAECAD9 + }; + + /** + * @private + * Whether any advanced selectors have been registered with this style + * manager. + */ + private var _hasAdvancedSelectors:Boolean; + + /** + * @private + * A map of CSS pseudo states. If a pseudo selector exists for a + * particular state name, it is likely that styles need to be recalculated. + */ + private var _pseudoCSSStates:Object; + + /** + * @private + * A map of CSS selectors -- such as "global", "Button", and ".bigRed" -- + * to CSSStyleDeclarations. + * This collection is accessed via getStyleDeclaration(), + * setStyleDeclaration(), and clearStyleDeclaration(). + */ + private var _selectors:Object = {}; + + /** + * @private + */ + private var styleModules:Object = {}; + + /** + * @private + * A map of selector "subjects" to an ordered map of selector Strings and + * their associated CSSStyleDeclarations. + * The subject is the right most simple type selector in a potential chain + * of selectors. + */ + private var _subjects:Object = {}; + + /** + * @private + * Used for accessing localized Error messages. + */ + private var resourceManager:IResourceManager = + ResourceManager.getInstance(); + + /** + * @private + * Cache merged styles between this and parent. + */ + private var mergedInheritingStylesCache:Object; + + /** + * @private + * This style manager's flex module factory. + */ + private var moduleFactory:IFlexModuleFactory; + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // parent + //---------------------------------- + + /** + * @private + */ + private var _parent:IStyleManager2; + + /** + * @private + * + * The style manager that is the parent of this StyleManager. + * + * @return the parent StyleManager or null if this is the top-level StyleManager. + */ + public function get parent():IStyleManager2 + { + return _parent; + } + + //---------------------------------- + // qualifiedTypeSelectors + //---------------------------------- + + /** + * @private + */ + private static var _qualifiedTypeSelectors:Boolean = true; + + public function get qualifiedTypeSelectors():Boolean + { + if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0) + return false; + + if (_qualifiedTypeSelectors) + return _qualifiedTypeSelectors; + + if (parent) + return parent.qualifiedTypeSelectors; + + return false; + } + + public function set qualifiedTypeSelectors(value:Boolean):void + { + _qualifiedTypeSelectors = value; + } + + //---------------------------------- + // stylesRoot + //---------------------------------- + + /** + * @private + */ + private var _stylesRoot:Object; + + /** + * @private + * The root of all proto chains used for looking up styles. + * This object is initialized once by initProtoChainRoots() and + * then updated by calls to setStyle() on the global CSSStyleDeclaration. + * It is accessed by code that needs to construct proto chains, + * such as the initProtoChain() method of UIComponent. + */ + public function get stylesRoot():Object + { + return _stylesRoot; + } + public function set stylesRoot(value:Object):void + { + _stylesRoot = value; + } + + //---------------------------------- + // inheritingStyles + //---------------------------------- + + /** + * @private + */ + private var _inheritingStyles:Object = {}; + + /** + * @private + * Set of inheriting non-color styles. + * This is not the complete set from CSS. + * Some of the omitted we don't support at all, + * others may be added later as needed. + * The method registerInheritingStyle() adds to this set + * and isInheritingStyle() queries this set. + */ + public function get inheritingStyles():Object + { + if (mergedInheritingStylesCache) + return mergedInheritingStylesCache; + + var mergedStyles:Object = _inheritingStyles; + + if (parent) + { + var otherStyles:Object = parent.inheritingStyles; + + for (var obj:Object in otherStyles) + { + if (mergedStyles[obj] === undefined) + mergedStyles[obj] = otherStyles[obj]; + } + } + + mergedInheritingStylesCache = mergedStyles; + + return mergedStyles; + } + + public function set inheritingStyles(value:Object):void + { + _inheritingStyles = value; + mergedInheritingStylesCache = null; + + if (hasEventListener(FlexChangeEvent.STYLE_MANAGER_CHANGE)) + dispatchInheritingStylesChangeEvent(); + } + + //---------------------------------- + // typeHierarchyCache + //---------------------------------- + + /** + * @private + */ + private var _typeHierarchyCache:Object; + + /** + * @private + */ + public function get typeHierarchyCache():Object + { + if (_typeHierarchyCache == null) + _typeHierarchyCache = {}; + + return _typeHierarchyCache; + } + + /** + * @private + */ + public function set typeHierarchyCache(value:Object):void + { + _typeHierarchyCache = value; + } + + //---------------------------------- + // typeSelectorCache + //---------------------------------- + + /** + * @private + */ + private var _typeSelectorCache:Object; + + /** + * @private + */ + public function get typeSelectorCache():Object + { + if (_typeSelectorCache == null) + _typeSelectorCache = {}; + + return _typeSelectorCache; + } + + /** + * @private + */ + public function set typeSelectorCache(value:Object):void + { + _typeSelectorCache = value; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + * This method is called by code autogenerated by the MXML compiler, + * after StyleManager.styles is popuplated with CSSStyleDeclarations. + */ + public function initProtoChainRoots():void + { + if (!stylesRoot) + { + var style:CSSStyleDeclaration = getMergedStyleDeclaration("global"); + if (style != null) + { + stylesRoot = style.addStyleToProtoChain({}, null); + } + } + } + + /** + * Returns an array of strings of all CSS selectors registered with the StyleManager. + * Pass items in this array to the getStyleDeclaration function to get the corresponding CSSStyleDeclaration. + * Note that class selectors are prepended with a period. + * + * @return An array of all of the selectors + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function get selectors():Array + { + var theSelectors:Array = []; + for (var i:String in _selectors) + theSelectors.push(i); + + if (parent) + { + var otherSelectors:Array = parent.selectors; + for (i in otherSelectors) + theSelectors.push(i); + } + + return theSelectors; + } + + /** + * Determines whether any of the selectors registered with the style + * manager have been advanced selectors (descendant selector, id selector, + * non-global class selector, pseudo selector). + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public function hasAdvancedSelectors():Boolean + { + if (_hasAdvancedSelectors) + return true; + + if (parent) + return parent.hasAdvancedSelectors(); + + return false; + } + + /** + * @private + * Determines whether at least one pseudo-condition has been specified for + * the given state. + */ + public function hasPseudoCondition(cssState:String):Boolean + { + if (_pseudoCSSStates != null && _pseudoCSSStates[cssState] != null) + return true; + + if (parent) + return parent.hasPseudoCondition(cssState); + + return false; + } + + private static var propNames:Array = ["class", "id", "pseudo", "unconditional"]; + + /** + * Retrieve all style declarations applicable to this subject. The subject + * is the right most simple type selector in a selector chain. Returns a + * map of selectors with four properties: class for class selectors, + * id for id selectors, pseudo for pseudo selectors and unconditional + * for selectors without conditions + * + * + * @param subject The subject of the style declaration's selector. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public function getStyleDeclarations(subject:String):Object + { + // For Flex 3 and earlier, if we were passed a subject with a package + // name, such as "mx.controls.Button", strip off the package name + // leaving just "Button" and look for that subject. + if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0) + { + if (subject.charAt(0) != ".") + { + var index:int = subject.lastIndexOf("."); + if (index != -1) + subject = subject.substr(index + 1); + } + } + + // NOTE: It's important the parent declarations come before this style + // manager's styles because the order here is the order they are added to the + // prototype chain. + var theSubjects:Object = null; + + if (parent) + theSubjects = parent.getStyleDeclarations(subject); + + var subjectsObject:Object = _subjects[subject]; + if (!theSubjects) + { + if (subjectsObject) + theSubjects = subjectsObject; + } + else if (subjectsObject) + { + var mergedSubjects:Object = {}; + for each (var prop:String in propNames) + { + mergedSubjects[prop] = subjectsObject[prop]; + } + mergedSubjects.parent = theSubjects; + theSubjects = mergedSubjects; + } + + return theSubjects; + } + + private function isUnique(element:*, index:int, arr:Array):Boolean { + return (arr.indexOf(element) >= 0); + } + + /** + * Gets the CSSStyleDeclaration object that stores the rules + * for the specified CSS selector. + * + *

If the selector parameter starts with a period (.), + * the returned CSSStyleDeclaration is a class selector and applies only to those instances + * whose styleName property specifies that selector + * (not including the period). + * For example, the class selector ".bigMargins" + * applies to any UIComponent whose styleName + * is "bigMargins".

+ * + *

If the selector parameter does not start with a period, + * the returned CSSStyleDeclaration is a type selector and applies to all instances + * of that type. + * For example, the type selector "Button" + * applies to all instances of Button and its subclasses.

+ * + *

The global selector is similar to a type selector + * and does not start with a period.

+ * + * @param selector The name of the CSS selector. + * + * @return The style declaration whose name matches the selector property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getStyleDeclaration(selector:String):CSSStyleDeclaration + { + // For Flex 3 and earlier, if we were passed a selector with a package + // name, such as "mx.controls.Button", strip off the package name + // leaving just "Button" and look for that type selector. + if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0) + { + if (selector.charAt(0) != ".") + { + var index:int = selector.lastIndexOf("."); + if (index != -1) + selector = selector.substr(index + 1); + } + } + + return _selectors[selector]; + } + + /** + * Gets a CSSStyleDeclaration object that stores the rules + * for the specified CSS selector. The CSSStyleDeclaration object is the created by merging + * the properties of the specified CSS selector of this style manager with all of the parent + * style managers. + * + *

+ * If this style manager contains a style declaration for the given selector, its style properties + * will be updated with properties from the parent style manager's merged style declaration. If + * this style manager does not have a style declaration for a given selector, the parent's merged + * style declaration will be set into this style manager depending on the value of the + * setSelector parameter. + *

+ * + *

If the selector parameter starts with a period (.), + * the returned CSSStyleDeclaration is a class selector and applies only to those instances + * whose styleName property specifies that selector + * (not including the period). + * For example, the class selector ".bigMargins" + * applies to any UIComponent whose styleName + * is "bigMargins".

+ * + *

If the selector parameter does not start with a period, + * the returned CSSStyleDeclaration is a type selector and applies to all instances + * of that type. + * For example, the type selector "Button" + * applies to all instances of Button and its subclasses.

+ * + *

The global selector is similar to a type selector + * and does not start with a period.

+ * + * @param selector The name of the CSS selector. + * @param localOnly Controls whether the returned style declaration is the result of merging + * the properties of this and any parent style managers or if the style declaration is only + * from this style manager. + * + * @return The style declaration whose name matches the selector property. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 4 + */ + public function getMergedStyleDeclaration(selector:String):CSSStyleDeclaration + { + var style:CSSStyleDeclaration = getStyleDeclaration(selector); + var parentStyle:CSSStyleDeclaration = null; + + // If we have a parent, get its style and merge them with our style. + if (parent) + parentStyle = parent.getMergedStyleDeclaration(selector); + + if (style || parentStyle) + { + style = new CSSMergedStyleDeclaration(style, parentStyle, + style ? style.selectorString : parentStyle.selectorString, this, false); + } + + return style; + } + + /** + * Sets the CSSStyleDeclaration object that stores the rules + * for the specified CSS selector. + * + *

If the selector parameter starts with a period (.), + * the specified selector is a class selector and applies only to those instances + * whose styleName property specifies that selector + * (not including the period). + * For example, the class selector ".bigMargins" + * applies to any UIComponent whose styleName + * is "bigMargins".

+ * + *

If the selector parameter does not start with a period, + * the specified selector is a "type selector" and applies to all instances + * of that type. + * For example, the type selector "Button" + * applies to all instances of Button and its subclasses.

+ * + *

The global selector is similar to a type selector + * and does not start with a period.

+ * + *

Note that the provided selector will update the selector and subject + * of the styleDeclaration to keep them in sync.

+ * + * @param selector The name of the CSS selector. + * @param styleDeclaration The new style declaration. + * @param update Set to true to force an immediate update of the styles. + * Set to false to avoid an immediate update of the styles in the application. + * The styles will be updated the next time this method or the clearStyleDeclaration() method + * is called with the update property set to true. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function setStyleDeclaration(selector:String, + styleDeclaration:CSSStyleDeclaration, + update:Boolean):void + { + // For Flex 3 and earlier, if we were passed a selector with a package + // name, such as "mx.controls.Button", strip off the package name + // leaving just "Button" and look for that type selector. + if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0) + { + if (selector.charAt(0) != ".") + { + var index:int = selector.lastIndexOf("."); + if (index != -1) + selector = selector.substr(index + 1); + } + } + + // Populate the selectors Array for this style declaration + styleDeclaration.selectorRefCount++; + styleDeclaration.selectorIndex = selectorIndex++; + _selectors[selector] = styleDeclaration; + + // We also index by subject to help match advanced selectors + var subject:String = styleDeclaration.subject; + if (selector) + { + if (!styleDeclaration.subject) + { + // If the styleDeclaration does not yet have a subject we + // update its selector to keep it in sync with the provided + // selector. + styleDeclaration.selectorString = selector; + subject = styleDeclaration.subject; + } + else if (selector != styleDeclaration.selectorString) + { + // The styleDeclaration does not match the provided selector, so + // we ignore the subject on the styleDeclaration and try to + // determine the subject from the selector + var firstChar:String = selector.charAt(0); + if (firstChar == "." || firstChar == ":" || firstChar == "#") + { + subject = "*"; + } + else + { + // TODO: Support parsing Advanced CSS selectors for a + // subject... + subject = selector; + } + + // Finally, we update the styleDeclaration's selector to keep + // it in sync with the provided selector. + styleDeclaration.selectorString = selector; + } + } + + if (subject != null) + { + // determine the kind of selector and add it to the appropriate + // bin of selectors for this subject + var kind:String = styleDeclaration.selector.conditions ? + styleDeclaration.selector.conditions[0].kind : + "unconditional"; + var declarations:Object = _subjects[subject]; + if (declarations == null) + { + declarations = {}; + declarations[kind] = [styleDeclaration]; + _subjects[subject] = declarations; + } + else + { + var declarationList:Array = declarations[kind] as Array; + if (declarationList == null) + declarations[kind] = [styleDeclaration]; + else + declarationList.push(styleDeclaration); + } + } + + // Also remember subjects that have pseudo-selectors to optimize + // styles during component state changes. + var pseudoCondition:String = styleDeclaration.getPseudoCondition(); + if (pseudoCondition != null) + { + if (_pseudoCSSStates == null) + _pseudoCSSStates = {}; + + _pseudoCSSStates[pseudoCondition] = true; + } + + // Record whether this is an advanced selector so that style declaration + // look up can be optimized for when no advanced selectors have been + // declared + if (styleDeclaration.isAdvanced()) + _hasAdvancedSelectors = true; + + // Flush cache and start over. + if (_typeSelectorCache) + _typeSelectorCache = {}; + + if (update) + styleDeclarationsChanged(); + } + + /** + * Clears the CSSStyleDeclaration object that stores the rules + * for the specified CSS selector. + * + *

If the specified selector is a class selector (for example, ".bigMargins" or ".myStyle"), + * you must be sure to start the + * selector property with a period (.).

+ * + *

If the specified selector is a type selector (for example, "Button"), do not start the + * selector property with a period.

+ * + *

The global selector is similar to a type selector + * and does not start with a period.

+ * + * @param selector The name of the CSS selector to clear. + * @param update Set to true to force an immediate update of the styles. + * Set to false to avoid an immediate update of the styles in the application. + * The styles will be updated the next time this method or the setStyleDeclaration() method is + * called with the update property set to true. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function clearStyleDeclaration(selector:String, + update:Boolean):void + { + var styleDeclaration:CSSStyleDeclaration = + getStyleDeclaration(selector); + + if (styleDeclaration && styleDeclaration.selectorRefCount > 0) + styleDeclaration.selectorRefCount--; + + // Clear out legacy selectors map + delete _selectors[selector]; + + // Clear out matching decls from our selectors stored by subject + var decls:Array; + var i:int; + var decl:CSSStyleDeclaration; + + if (styleDeclaration && styleDeclaration.subject) + { + decls = _subjects[styleDeclaration.subject] as Array; + if (decls) + { + // Work from the back of the array so we can remove elements + // as we go. + for (i = decls.length - 1; i >= 0; i--) + { + decl = decls[i]; + if (decl && decl.selectorString == selector) + { + if (decls.length == 1) + delete _subjects[styleDeclaration.subject]; + else + decls.splice(i, 1); + } + } + } + } + else + { + // Without a subject, we start searching all declarations for this + // selector, clear out matching selectors if found and then assume + // this we can limit our search to this subject and stop looking. + var matchingSubject:Boolean = false; + for each (decls in _subjects) + { + if (decls) + { + // Work from the back of the array so we can remove elements + // as we go. + for (i = decls.length - 1; i >= 0; i--) + { + decl = decls[i]; + if (decl && decl.selectorString == selector) + { + matchingSubject = true; + if (decls.length == 1) + delete _subjects[decl.subject]; + else + decls.splice(i, 1); + } + } + + if (matchingSubject) + break; + } + } + } + + if (update) + styleDeclarationsChanged(); + } + + /** + * @private + * After an entire selector is added, replaced, or removed, + * this method updates all the DisplayList trees. + */ + public function styleDeclarationsChanged():void + { + var sms:Array /* of SystemManager */ = + SystemManagerGlobals.topLevelSystemManagers; + var n:int = sms.length; + for (var i:int = 0; i < n; i++) + { + // Type as Object to avoid dependency on SystemManager or WindowedSystemManager + var sm:ISystemManager = sms[i]; + var cm:Object = sm.getImplementation("mx.managers::ISystemManagerChildManager"); + Object(cm).regenerateStyleCache(true); + Object(cm).notifyStyleChangeInChildren(null, true); + } + } + + /** + * Adds to the list of styles that can inherit values + * from their parents. + * + *

Note: Ensure that you avoid using duplicate style names, as name + * collisions can result in decreased performance if a style that is + * already used becomes inheriting.

+ * + * @param styleName The name of the style that is added to the list of styles that can inherit values. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function registerInheritingStyle(styleName:String):void + { + if (_inheritingStyles[styleName] != true) + { + _inheritingStyles[styleName] = true; + mergedInheritingStylesCache = null; + + if (hasEventListener(FlexChangeEvent.STYLE_MANAGER_CHANGE)) + dispatchInheritingStylesChangeEvent(); + } + } + + /** + * Tests to see if a style is inheriting. + * + * @param styleName The name of the style that you test to see if it is inheriting. + * + * @return Returns true if the specified style is inheriting. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function isInheritingStyle(styleName:String):Boolean + { + if (mergedInheritingStylesCache) + return mergedInheritingStylesCache[styleName] == true; + + if (_inheritingStyles[styleName] == true) + return true; + + if (parent && parent.isInheritingStyle(styleName)) + return true; + + return false; + } + + /** + * Test to see if a TextFormat style is inheriting. + * + * @param styleName The name of the style that you test to see if it is inheriting. + * + * @return Returns true if the specified TextFormat style + * is inheriting. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function isInheritingTextFormatStyle(styleName:String):Boolean + { + if (inheritingTextFormatStyles[styleName] == true) + return true; + + if (parent && parent.isInheritingTextFormatStyle(styleName)) + return true; + + return false; + } + + /** + * Adds to the list of styles which may affect the measured size + * of the component. + * When one of these styles is set with setStyle(), + * the invalidateSize() method is automatically called on the component + * to make its measured size get recalculated later. + * + * @param styleName The name of the style that you add to the list. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function registerSizeInvalidatingStyle(styleName:String):void + { + sizeInvalidatingStyles[styleName] = true; + } + + /** + * Tests to see if a style changes the size of a component. + * + *

When one of these styles is set with the setStyle() method, + * the invalidateSize() method is automatically called on the component + * to make its measured size get recalculated later.

+ * + * @param styleName The name of the style to test. + * + * @return Returns true if the specified style is one + * which may affect the measured size of the component. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function isSizeInvalidatingStyle(styleName:String):Boolean + { + if (sizeInvalidatingStyles[styleName] == true) + return true; + + if (parent && parent.isSizeInvalidatingStyle(styleName)) + return true; + + return false; + } + + /** + * Adds to the list of styles which may affect the measured size + * of the component's parent container. + *

When one of these styles is set with setStyle(), + * the invalidateSize() method is automatically called on the component's + * parent container to make its measured size get recalculated + * later.

+ * + * @param styleName The name of the style to register. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function registerParentSizeInvalidatingStyle(styleName:String):void + { + parentSizeInvalidatingStyles[styleName] = true; + } + + /** + * Tests to see if the style changes the size of the component's parent container. + * + *

When one of these styles is set with setStyle(), + * the invalidateSize() method is automatically called on the component's + * parent container to make its measured size get recalculated + * later.

+ * + * @param styleName The name of the style to test. + * + * @return Returns true if the specified style is one + * which may affect the measured size of the component's + * parent container. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function isParentSizeInvalidatingStyle(styleName:String):Boolean + { + if (parentSizeInvalidatingStyles[styleName] == true) + return true; + + if (parent && parent.isParentSizeInvalidatingStyle(styleName)) + return true; + + return false; + } + + /** + * Adds to the list of styles which may affect the appearance + * or layout of the component's parent container. + * When one of these styles is set with setStyle(), + * the invalidateDisplayList() method is auomatically called on the component's + * parent container to make it redraw and/or relayout its children. + * + * @param styleName The name of the style to register. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function registerParentDisplayListInvalidatingStyle( + styleName:String):void + { + parentDisplayListInvalidatingStyles[styleName] = true; + } + + /** + * Tests to see if this style affects the component's parent container in + * such a way as to require that the parent container redraws itself when this style changes. + * + *

When one of these styles is set with setStyle(), + * the invalidateDisplayList() method is auomatically called on the component's + * parent container to make it redraw and/or relayout its children.

+ * + * @param styleName The name of the style to test. + * + * @return Returns true if the specified style is one + * which may affect the appearance or layout of the component's + * parent container. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function isParentDisplayListInvalidatingStyle( + styleName:String):Boolean + { + if (parentDisplayListInvalidatingStyles[styleName] == true) + return true; + + if (parent && parent.isParentDisplayListInvalidatingStyle(styleName)) + return true; + + return false; + } + + /** + * Adds a color name to the list of aliases for colors. + * + * @param colorName The name of the color to add to the list; for example, "blue". + * If you later access this color name, the value is not case-sensitive. + * + * @param colorValue Color value, for example, 0x0000FF. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function registerColorName(colorName:String, colorValue:uint):void + { + colorNames[colorName.toLowerCase()] = colorValue; + } + + /** + * Tests to see if the given String is an alias for a color value. For example, + * by default, the String "blue" is an alias for 0x0000FF. + * + * @param colorName The color name to test. This parameter is not case-sensitive. + * + * @return Returns true if colorName is an alias + * for a color. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function isColorName(colorName:String):Boolean + { + if (colorNames[colorName.toLowerCase()] !== undefined) + return true; + + if (parent && parent.isColorName(colorName)) + return true; + + return false; + } + + /** + * Returns the numeric RGB color value that corresponds to the + * specified color string. + * The color string can be either a case-insensitive color name + * such as "red", "Blue", or + * "haloGreen", a hexadecimal value such as 0xFF0000, or a #-hexadecimal String + * such as "#FF0000". + * + *

This method returns a uint, such as 4521830, that represents a color. You can convert + * this uint to a hexadecimal value by passing the numeric base (in this case, 16), to + * the uint class's toString() method, as the following example shows:

+ *
+     *  import mx.styles.StyleManager;
+     *  private function getNewColorName():void {
+     *      StyleManager.registerColorName("soylentGreen",0x44FF66);
+     *      trace(StyleManager.getColorName("soylentGreen").toString(16));
+     *  }
+     *  
+ * + * @param colorName The color name. + * + * @return Returns a uint that represents the color value or NOT_A_COLOR + * if the value of the colorName property is not an alias for a color. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getColorName(colorName:Object):uint + { + var n:Number; + + if (colorName is String) + { + if (colorName.charAt(0) == "#") + { + // Map "#77EE11" to 0x77EE11 + n = Number("0x" + colorName.slice(1)); + return isNaN(n) ? StyleManager.NOT_A_COLOR : uint(n); + } + + if (colorName.charAt(1) == "x" && colorName.charAt(0) == '0') + { + // Map "#77EE11" to 0x77EE11 + n = Number(colorName); + return isNaN(n) ? StyleManager.NOT_A_COLOR : uint(n); + } + + // Map "red" or "Red" to 0xFF0000; + // Map "haloGreen" or "HaLoGrEeN" to 0x46FF00. + var c:* = colorNames[colorName.toLowerCase()]; + if (c === undefined) + { + // If not found then try our parent + if (parent) + c = parent.getColorName(colorName); + } + + if (c === undefined) + return StyleManager.NOT_A_COLOR; + + return uint(c); + } + + return uint(colorName); + } + + /** + * Converts each element of the colors Array from a color name + * to a numeric RGB color value. + * Each color String can be either a case-insensitive color name + * such as "red", "Blue", or + * "haloGreen", a hexadecimal value such as 0xFF0000, or a #-hexadecimal String + * such as "#FF0000".. + * + * @param colors An Array of color names. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function getColorNames(colors:Array /* of Number or String */):void + { + if (!colors) + return; + + var n:int = colors.length; + for (var i:int = 0; i < n; i++) + { + if ((colors[i] != null) && isNaN(colors[i])) + { + var colorNumber:uint = getColorName(colors[i]); + if (colorNumber != StyleManager.NOT_A_COLOR) + colors[i] = colorNumber; + } + } + } + + /** + * Determines if a specified parameter is a valid style property. For example: + * + *
+     *  trace(StyleManager.isValidStyleValue(myButton.getStyle("color")).toString());
+     *  
+ * + *

This can be useful because some styles can be set to values + * such as 0, NaN, + * the empty String (""), or null, which can + * cause an if (value) test to fail.

+ * + * @param value The style property to test. + * + * @return If you pass the value returned by a getStyle() method call + * to this method, it returns true if the style + * was set and false if it was not set. + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function isValidStyleValue(value:*):Boolean + { + // By convention, we don't allow style values to be undefined, + // so we can check for this as the "not set" value. + if (value !== undefined) + return true; + + if (parent) + return parent.isValidStyleValue(value); + + return false; + } + + /** + * @private + */ + public function loadStyleDeclarations( + url:String, update:Boolean = true, + trustContent:Boolean = false, + applicationDomain:ApplicationDomain = null, + securityDomain:SecurityDomain = null): + IEventDispatcher + { + return loadStyleDeclarations2(url, update, applicationDomain, securityDomain); + } + + /** + * Loads a style SWF. + * + * @param url Location of the style SWF. + * + * @param update If true, all the DisplayList trees will be updated. + * The default is true. + * + * @return An IEventDispatcher implementation that supports + * StyleEvent.PROGRESS, StyleEvent.COMPLETE, and + * StyleEvent.ERROR. + * + * @see flash.system.SecurityDomain + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function loadStyleDeclarations2( + url:String, update:Boolean = true, + applicationDomain:ApplicationDomain = null, + securityDomain:SecurityDomain = null): + IEventDispatcher + { + var module:IModuleInfo = ModuleManager.getModule(url); + var thisStyleManager:IStyleManager2 = this; + + var readyHandler:Function = function(moduleEvent:ModuleEvent):void + { + var styleModule:IStyleModule = + IStyleModule(moduleEvent.module.factory.create()); + + // Register the style module to use this style manager. + moduleEvent.module.factory.registerImplementation("mx.styles::IStyleManager2", thisStyleManager); + styleModule.setStyleDeclarations(thisStyleManager); + styleModules[moduleEvent.module.url].styleModule = styleModule; + + if (update) + styleDeclarationsChanged(); + }; + + module.addEventListener(ModuleEvent.READY, readyHandler, + false, 0, true); + + var styleEventDispatcher:StyleEventDispatcher = + new StyleEventDispatcher(module); + + var errorHandler:Function = function(moduleEvent:ModuleEvent):void + { + var errorText:String = resourceManager.getString( + "styles", "unableToLoad", [ moduleEvent.errorText, url ]); + + if (styleEventDispatcher.willTrigger(StyleEvent.ERROR)) + { + var styleEvent:StyleEvent = new StyleEvent( + StyleEvent.ERROR, moduleEvent.bubbles, moduleEvent.cancelable); + styleEvent.bytesLoaded = 0; + styleEvent.bytesTotal = 0; + styleEvent.errorText = errorText; + styleEventDispatcher.dispatchEvent(styleEvent); + } + else + { + throw new Error(errorText); + } + }; + + module.addEventListener(ModuleEvent.ERROR, errorHandler, + false, 0, true); + + styleModules[url] = + new StyleModuleInfo(module, readyHandler, errorHandler); + + // This Timer gives the loadStyleDeclarations() caller a chance + // to add event listeners to the return value, before the module + // is loaded. + var timer:Timer = new Timer(0); + var timerHandler:Function = function(event:TimerEvent):void + { + timer.removeEventListener(TimerEvent.TIMER, timerHandler); + timer.stop(); + module.load(applicationDomain, securityDomain); + }; + + timer.addEventListener(TimerEvent.TIMER, timerHandler, false, 0, true); + + timer.start(); + + return styleEventDispatcher; + } + + /** + * Unloads a style SWF. + * + * @param url Location of the style SWF. + * + * @param update If true, all the DisplayList trees will be updated. + * The default is true. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function unloadStyleDeclarations( + url:String, update:Boolean = true):void + { + var styleModuleInfo:StyleModuleInfo = styleModules[url]; + if (styleModuleInfo) + { + styleModuleInfo.styleModule.unload(); + + var module:IModuleInfo = styleModuleInfo.module; + module.unload(); + + module.removeEventListener(ModuleEvent.READY, + styleModuleInfo.readyHandler); + module.removeEventListener(ModuleEvent.ERROR, + styleModuleInfo.errorHandler); + + styleModules[url] = null; + } + + if (update) + styleDeclarationsChanged(); + } + + + /** + * @private + */ + private function dispatchInheritingStylesChangeEvent():void + { + var event:Event = new FlexChangeEvent(FlexChangeEvent.STYLE_MANAGER_CHANGE, + false, false, {property: "inheritingStyles"}); + dispatchEvent(event); + } + + /** + * @private + */ + public function acceptMediaList(value:String):Boolean + { + if (!mqp) + { + mqp = MediaQueryParser.instance; + if (!mqp) + { + mqp = new MediaQueryParser(moduleFactory); + MediaQueryParser.instance = mqp; + } + } + return mqp.parse(value); + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + private function styleManagerChangeHandler(event:FlexChangeEvent):void + { + if (!event.data) + return; // invalid message + + var property:String = event.data["property"]; + + if (property == "inheritingStyles") + { + mergedInheritingStylesCache = null; + } + + if (hasEventListener(FlexChangeEvent.STYLE_MANAGER_CHANGE)) + dispatchEvent(event); + } +} + +} + +import flash.events.EventDispatcher; +import mx.events.ModuleEvent; +import mx.events.StyleEvent; +import mx.modules.IModuleInfo; +import mx.styles.IStyleModule; + +//////////////////////////////////////////////////////////////////////////////// +// +// Helper class: StyleEventDispatcher +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * @private + */ +class StyleEventDispatcher extends EventDispatcher +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function StyleEventDispatcher(moduleInfo:IModuleInfo) + { + super(); + + moduleInfo.addEventListener( + ModuleEvent.PROGRESS, moduleInfo_progressHandler, false, 0, true); + + moduleInfo.addEventListener( + ModuleEvent.READY, moduleInfo_readyHandler, false, 0, true); + } + + //-------------------------------------------------------------------------- + // + // Event handlers + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private function moduleInfo_progressHandler(event:ModuleEvent):void + { + var styleEvent:StyleEvent = new StyleEvent( + StyleEvent.PROGRESS, event.bubbles, event.cancelable); + styleEvent.bytesLoaded = event.bytesLoaded; + styleEvent.bytesTotal = event.bytesTotal; + dispatchEvent(styleEvent); + } + + /** + * @private + */ + private function moduleInfo_readyHandler(event:ModuleEvent):void + { + var styleEvent:StyleEvent = new StyleEvent(StyleEvent.COMPLETE); + styleEvent.bytesLoaded = event.bytesLoaded; + styleEvent.bytesTotal = event.bytesTotal; + dispatchEvent(styleEvent); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Helper class: StyleModuleInfo +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * @private + */ +class StyleModuleInfo +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function StyleModuleInfo(module:IModuleInfo, + readyHandler:Function, + errorHandler:Function) + { + super(); + + this.module = module; + this.readyHandler = readyHandler; + this.errorHandler = errorHandler; + } + + //-------------------------------------------------------------------------- + // + // Properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // errorHandler + //---------------------------------- + + /** + * @private + */ + public var errorHandler:Function; + + //---------------------------------- + // readyHandler + //---------------------------------- + + /** + * @private + */ + public var readyHandler:Function; + + //---------------------------------- + // styleModule + //---------------------------------- + + /** + * @private + */ + public var styleModule:IStyleModule; + + //---------------------------------- + // module + //---------------------------------- + + /** + * @private + */ + public var module:IModuleInfo +} diff --git a/src/mx/utils/DescribeTypeCache.as b/src/mx/utils/DescribeTypeCache.as new file mode 100644 index 00000000..427e6701 --- /dev/null +++ b/src/mx/utils/DescribeTypeCache.as @@ -0,0 +1,165 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils +{ + +import flash.utils.describeType; +import flash.utils.getDefinitionByName; +import flash.utils.getQualifiedClassName; +import mx.binding.BindabilityInfo; + +[ExcludeClass] + +/** + * @private + * DescribeTypeCache is a convenience class that is used to + * cache the return values of flash.utils.describeType() + * so that calls made subsequent times return faster. + * + * This class also lets you set handler functions for specific value types. + * These will get called when the user tries to access these values on + * the DescribeTypeCacheRecord class. + * + * @see mx.utils.DescribeTypeCacheRecord + */ +public class DescribeTypeCache +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class initialization + // + //-------------------------------------------------------------------------- + + registerCacheHandler("bindabilityInfo", bindabilityInfoHandler); + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private static var typeCache:Object = {}; + + /** + * @private + */ + private static var cacheHandlers:Object = {}; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Calls flash.utils.describeType() for the first time and caches + * the return value so that subsequent calls return faster. + * + * @param o Can be either a string describing a fully qualified class name or any + * ActionScript value, including all available ActionScript types, object instances, + * primitive types (such as uint), and class objects. + * + * @return Returns the cached record. + * + * @see flash.utils#describeType() + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function describeType(o:*):DescribeTypeCacheRecord + { + var className:String; + var cacheKey:String; + + if (o is String) + cacheKey = className = o; + else + cacheKey = className = getQualifiedClassName(o); + + //Need separate entries for describeType(Foo) and describeType(myFoo) + if (o is Class) + cacheKey += "$"; + + if (cacheKey in typeCache) + { + return typeCache[cacheKey]; + } + else + { + if (o is String) + { + try + { + o = getDefinitionByName(o); + } + catch (error:ReferenceError) + { + // The o parameter doesn't refer to an ActionScript + // definition, it's just a string value. + } + } + var typeDescription:XML = flash.utils.describeType(o); + var record:DescribeTypeCacheRecord = new DescribeTypeCacheRecord(); + record.typeDescription = typeDescription; + record.typeName = className; + typeCache[cacheKey] = record; + + return record; + } + } + + /** + * registerCacheHandler lets you add function handler for specific strings. + * These functions get called when the user refers to these values on a + * instance of DescribeTypeCacheRecord. + * + * @param valueName String that specifies the value for which the handler must be set. + * @param handler Function that should be called when user references valueName. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function registerCacheHandler(valueName:String, handler:Function):void + { + cacheHandlers[valueName] = handler; + } + + /** + * @private + */ + internal static function extractValue(valueName:String, record:DescribeTypeCacheRecord):* + { + if (valueName in cacheHandlers) + return cacheHandlers[valueName](record); + + return undefined; + } + + /** + * @private + */ + private static function bindabilityInfoHandler(record:DescribeTypeCacheRecord):* + { + return new BindabilityInfo(record.typeDescription); + } +} + +} diff --git a/src/mx/utils/DescribeTypeCacheRecord.as b/src/mx/utils/DescribeTypeCacheRecord.as new file mode 100644 index 00000000..2aae66e8 --- /dev/null +++ b/src/mx/utils/DescribeTypeCacheRecord.as @@ -0,0 +1,121 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils +{ + +import flash.utils.Proxy; +import flash.utils.flash_proxy; + +use namespace flash_proxy; + +[ExcludeClass] + +/** + * @private + * This class represents a single cache entry, this gets created + * as part of the describeType method call on the + * DescribeTypeCache class. + */ + +public dynamic class DescribeTypeCacheRecord extends Proxy +{ + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private var cache:Object = {}; + + //-------------------------------------------------------------------------- + // + // Class properties + // + //-------------------------------------------------------------------------- + + //---------------------------------- + // typeDescription + //---------------------------------- + + /** + * @private + */ + public var typeDescription:XML; + + //---------------------------------- + // typeName + //---------------------------------- + + /** + * @private + */ + public var typeName:String; + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public function DescribeTypeCacheRecord() + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Overridden methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + override flash_proxy function getProperty(name:*):* + { + var result:* = cache[name]; + + if (result === undefined) + { + result = DescribeTypeCache.extractValue(name, this); + cache[name] = result; + } + + return result; + } + + /** + * @private + */ + override flash_proxy function hasProperty(name:*):Boolean + { + if (name in cache) + return true; + + var value:* = DescribeTypeCache.extractValue(name, this); + + if (value === undefined) + return false; + + cache[name] = value; + + return true; + } +} + +} diff --git a/src/mx/utils/IXMLNotifiable.as b/src/mx/utils/IXMLNotifiable.as new file mode 100644 index 00000000..03f14bd9 --- /dev/null +++ b/src/mx/utils/IXMLNotifiable.as @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils +{ + +/** + * The IXMLNotifiable interface. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public interface IXMLNotifiable +{ + /** + * @private + */ + function xmlNotification(currentTarget:Object, + type:String, + target:Object, + value:Object, + detail:Object):void; +} + +} \ No newline at end of file diff --git a/src/mx/utils/MatrixUtil.as b/src/mx/utils/MatrixUtil.as new file mode 100644 index 00000000..90c0a9b4 --- /dev/null +++ b/src/mx/utils/MatrixUtil.as @@ -0,0 +1,1738 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2003-2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils +{ + +import flash.display.DisplayObject; +import flash.geom.Matrix; +import flash.geom.Matrix3D; +import flash.geom.PerspectiveProjection; +import flash.geom.Point; +import flash.geom.Rectangle; +import flash.geom.Utils3D; +import flash.geom.Vector3D; +import flash.system.ApplicationDomain; +import flash.utils.getDefinitionByName; + +import mx.core.mx_internal; + +use namespace mx_internal; + +[ExcludeClass] + +/** + * @private + * The MatrixUtil class is for internal use only. + * Class for matrix and geometric related math routines. + */ +public final class MatrixUtil +{ + include "../core/Version.as"; + + private static const RADIANS_PER_DEGREES:Number = Math.PI / 180; + mx_internal static var SOLUTION_TOLERANCE:Number = 0.1; + mx_internal static var MIN_MAX_TOLERANCE:Number = 0.1; + + private static var staticPoint:Point = new Point(); + + // For use in getConcatenatedMatrix function + private static var fakeDollarParent:QName; + private static var uiComponentClass:Class; + private static var uiMovieClipClass:Class; + private static var usesMarshalling:Object; + private static var lastModuleFactory:Object; + private static var computedMatrixProperty:QName; + private static var $transformProperty:QName; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Returns rotation value clamped between -180 and 180 degreeds. + * This mimicks the Flash player behavior. + */ + public static function clampRotation(value:Number):Number + { + // Flash player doesn't handle values larger than 2^15 - 1 (FP-749). + if (value > 180 || value < -180) + { + value = value % 360; + + if (value > 180) + value = value - 360; + else if (value < -180) + value = value + 360; + } + return value; + } + + /** + * Returns a static Point object with the result. + * If matrix is null, point is untransformed. + */ + public static function transformPoint(x:Number, y:Number, m:Matrix):Point + { + if (!m) + { + staticPoint.x = x; + staticPoint.y = y; + return staticPoint; + } + + staticPoint.x = m.a * x + m.c * y + m.tx; + staticPoint.y = m.b * x + m.d * y + m.ty; + return staticPoint; + } + + public static function composeMatrix(x:Number = 0, + y:Number = 0, + scaleX:Number = 1, + scaleY:Number = 1, + rotation:Number = 0, + transformX:Number = 0, + transformY:Number = 0):Matrix + { + var m:Matrix = new Matrix(); + m.translate(-transformX, -transformY); + m.scale(scaleX, scaleY); + if (rotation != 0) + m.rotate(rotation / 180 * Math.PI); + m.translate(transformX + x, transformY + y); + return m; + } + + /** + * Decompose a matrix into its component scale, rotation, and translation parts. + * The Vector of Numbers passed in the components parameter will be + * populated by this function with the component parts. + * + * @param components Vector which holds the component scale, rotation + * and translation values. + * x = components[0] + * y = components[1] + * rotation = components[2] + * scaleX = components[3] + * scaleY = components[4] + * + * @param matrix The matrix to decompose + * @param transformX The x value of the transform center + * @param transformY The y value of the transform center + */ + public static function decomposeMatrix(components:Vector., + matrix:Matrix, + transformX:Number = 0, + transformY:Number = 0):void + { + // else decompose matrix. Don't use MatrixDecompose(), it can return erronous values + // when negative scales (and therefore skews) are in use. + var Ux:Number; + var Uy:Number; + var Vx:Number; + var Vy:Number; + + Ux = matrix.a; + Uy = matrix.b; + components[3] = Math.sqrt(Ux*Ux + Uy*Uy); + + Vx = matrix.c; + Vy = matrix.d; + components[4] = Math.sqrt(Vx*Vx + Vy*Vy ); + + // sign of the matrix determinant will tell us if the space is inverted by a 180 degree skew or not. + var determinant:Number = Ux*Vy - Uy*Vx; + if (determinant < 0) // if so, choose y-axis scale as the skewed one. Unfortunately, its impossible to tell if it originally was the y or x axis that had the negative scale/skew. + { + components[4] = -(components[4]); + Vx = -Vx; + Vy = -Vy; + } + + components[2] = Math.atan2( Uy, Ux ) / RADIANS_PER_DEGREES; + + if (transformX != 0 || transformY != 0) + { + var postTransformCenter:Point = matrix.transformPoint(new Point(transformX,transformY)); + components[0] = postTransformCenter.x - transformX; + components[1] = postTransformCenter.y - transformY; + } + else + { + components[0] = matrix.tx; + components[1] = matrix.ty; + } + } + + /** + * @return Returns the union of rect and + * Rectangle(left, top, right - left, bottom - top). + * Note that if rect is non-null, it will be updated to reflect the return value. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function rectUnion(left:Number, top:Number, right:Number, bottom:Number, + rect:Rectangle):Rectangle + { + if (!rect) + return new Rectangle(left, top, right - left, bottom - top); + + var minX:Number = Math.min(rect.left, left); + var minY:Number = Math.min(rect.top, top); + var maxX:Number = Math.max(rect.right, right); + var maxY:Number = Math.max(rect.bottom, bottom); + + rect.x = minX; + rect.y = minY; + rect.width = maxX - minX; + rect.height = maxY - minY; + return rect; + } + + /** + * Calculates the bounding box of a post-transformed ellipse. + * + * @param cx The x coordinate of the ellipse's center + * @param cy The y coordinate of the ellipse's center + * @param rx The horizontal radius of the ellipse + * @param ry The vertical radius of the ellipse + * @param matrix The transformation matrix. + * @param rect If non-null, rect will be updated to the union of rect and + * the segment bounding box. + * @return Returns the union of the passed in rect with the + * bounding box of the the post-transformed ellipse. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getEllipseBoundingBox(cx:Number, cy:Number, + rx:Number, ry:Number, + matrix:Matrix, + rect:Rectangle = null):Rectangle + { + var a:Number = matrix.a; + var b:Number = matrix.b; + var c:Number = matrix.c; + var d:Number = matrix.d; + + // Ellipse can be represented by the following parametric equations: + // + // (1) x = cx + rx * cos(t) + // (2) y = cy + ry * sin(t) + // + // After applying transformation with matrix m(a, c, b, d) we get: + // + // (3) x = a * cx + a * cos(t) * rx + c * cy + c * sin(t) * ry + m.tx + // (4) y = b * cx + b * cos(t) * rx + d * cy + d * sin(t) * ry + m.ty + // + // In (3) and (4) x and y are functions of a parameter t. To find the extremums we need + // to find where dx/dt and dy/dt reach zero: + // + // (5) dx/dt = - a * sin(t) * rx + c * cos(t) * ry + // (6) dy/dt = - b * sin(t) * rx + d * cos(t) * ry + // (7) dx/dt = 0 <=> sin(t) / cos(t) = (c * ry) / (a * rx); + // (8) dy/dt = 0 <=> sin(t) / cos(t) = (d * ry) / (b * rx); + + if (rx == 0 && ry == 0) + { + var pt:Point = new Point(cx, cy); + pt = matrix.transformPoint(pt); + return rectUnion(pt.x, pt.y, pt.x, pt.y, rect); + } + + var t:Number; + var t1:Number; + + if (a * rx == 0) + t = Math.PI / 2; + else + t = Math.atan((c * ry) / (a * rx)); + + if (b * rx == 0) + t1 = Math.PI / 2; + else + t1 = Math.atan((d * ry) / (b * rx)); + + var x1:Number = a * Math.cos(t) * rx + c * Math.sin(t) * ry; + var x2:Number = -x1; + x1 += a * cx + c * cy + matrix.tx; + x2 += a * cx + c * cy + matrix.tx; + + var y1:Number = b * Math.cos(t1) * rx + d * Math.sin(t1) * ry; + var y2:Number = -y1; + y1 += b * cx + d * cy + matrix.ty; + y2 += b * cx + d * cy + matrix.ty; + + return rectUnion(Math.min(x1, x2), Math.min(y1, y2), Math.max(x1, x2), Math.max(y1, y2), rect); + } + + /** + * @param x0 x coordinate of the first control point + * @param y0 y coordinate of the first control point + * @param x1 x coordinate of the second control point + * @param y1 y coordinate of the second control point + * @param x2 x coordinate of the third control point + * @param y2 y coordinate of the third control point + * @param sx The pre-transform scale factor for x coordinates. + * @param sy The pre-transform scale factor for y coordinates. + * @param matrix The transformation matrix. Can be null for identity transformation. + * @param rect If non-null, rect will be updated to the union of rect and + * the segment bounding box. + * @return Returns the union of the post-transformed quadratic + * bezier segment's axis aligned bounding box and the passed in rect. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + static public function getQBezierSegmentBBox(x0:Number, y0:Number, + x1:Number, y1:Number, + x2:Number, y2:Number, + sx:Number, sy:Number, + matrix:Matrix, + rect:Rectangle):Rectangle + { + var pt:Point; + pt = MatrixUtil.transformPoint(x0 * sx, y0 * sy, matrix); + x0 = pt.x; + y0 = pt.y; + + pt = MatrixUtil.transformPoint(x1 * sx, y1 * sy, matrix); + x1 = pt.x; + y1 = pt.y; + + pt = MatrixUtil.transformPoint(x2 * sx, y2 * sy, matrix); + x2 = pt.x; + y2 = pt.y; + + var minX:Number = Math.min(x0, x2); + var maxX:Number = Math.max(x0, x2); + + var minY:Number = Math.min(y0, y2); + var maxY:Number = Math.max(y0, y2); + + var txDiv:Number = x0 - 2 * x1 + x2; + if (txDiv != 0) + { + var tx:Number = (x0 - x1) / txDiv; + if (0 <= tx && tx <= 1) + { + var x:Number = (1 - tx) * (1 - tx) * x0 + 2 * tx * (1 - tx) * x1 + tx * tx * x2; + minX = Math.min(x, minX); + maxX = Math.max(x, maxX); + } + } + + var tyDiv:Number = y0 - 2 * y1 + y2; + if (tyDiv != 0) + { + var ty:Number = (y0 - y1) / tyDiv; + if (0 <= ty && ty <= 1) + { + var y:Number = (1 - ty) * (1 - ty) * y0 + 2 * ty * (1 - ty) * y1 + ty * ty * y2; + minY = Math.min(y, minY); + maxY = Math.max(y, maxY); + } + } + + return rectUnion(minX, minY, maxX, maxY, rect); + } + + /** + * @param width The width of the bounds to be transformed. + * @param height The height of the bounds to be transformed. + * @param matrix The transfomration matrix. + * + * @param vec If vec is non-null it will be set to the vector from the + * transformed bounds top left to the untransformed bounds top left + * in the coordinate space defined by matrix. + * This is useful if you want to align the transformed bounds to x,y + * by modifying the object's position. Moving the object by + * x + vec.x and y + vec.y respectively + * will offset the transformed bounds top left corner by x,y. + * + * @return Returns the transformed bounds. Note that the Point object returned will be reused + * by other MatrixUtil methods. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function transformSize(width:Number, height:Number, matrix:Matrix):Point + { + const a:Number = matrix.a; + const b:Number = matrix.b; + const c:Number = matrix.c; + const d:Number = matrix.d; + + // transform point (0,0) + var x1:Number = 0; + var y1:Number = 0; + + // transform point (width, 0) + var x2:Number = width * a; + var y2:Number = width * b; + + // transform point (0, height) + var x3:Number = height * c; + var y3:Number = height * d; + + // transform point (width, height) + var x4:Number = x2 + x3; + var y4:Number = y2 + y3; + + var minX:Number = Math.min(Math.min(x1, x2), Math.min(x3, x4)); + var maxX:Number = Math.max(Math.max(x1, x2), Math.max(x3, x4)); + var minY:Number = Math.min(Math.min(y1, y2), Math.min(y3, y4)); + var maxY:Number = Math.max(Math.max(y1, y2), Math.max(y3, y4)); + + staticPoint.x = maxX - minX; + staticPoint.y = maxY - minY; + return staticPoint; + } + + /** + * @param width The width of the bounds to be transformed. + * @param height The height of the bounds to be transformed. + * @param matrix The transfomration matrix. + * + * @param topleft If topLeft is non-null it will be used as the origin of the bounds + * rectangle to be transformed. On return, it will be set to the top left of the rectangle + * after transformation. + * + * @return Returns the transformed width and height. Note that the Point object returned will be reused + * by other MatrixUtil methods. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function transformBounds(width:Number, height:Number, matrix:Matrix, topLeft:Point = null):Point + { + const a:Number = matrix.a; + const b:Number = matrix.b; + const c:Number = matrix.c; + const d:Number = matrix.d; + + // transform point (0,0) + var x1:Number = 0; + var y1:Number = 0; + + // transform point (width, 0) + var x2:Number = width * a; + var y2:Number = width * b; + + // transform point (0, height) + var x3:Number = height * c; + var y3:Number = height * d; + + // transform point (width, height) + var x4:Number = x2 + x3; + var y4:Number = y2 + y3; + + var minX:Number = Math.min(Math.min(x1, x2), Math.min(x3, x4)); + var maxX:Number = Math.max(Math.max(x1, x2), Math.max(x3, x4)); + var minY:Number = Math.min(Math.min(y1, y2), Math.min(y3, y4)); + var maxY:Number = Math.max(Math.max(y1, y2), Math.max(y3, y4)); + + staticPoint.x = maxX - minX; + staticPoint.y = maxY - minY; + + if (topLeft) + { + const tx:Number = matrix.tx; + const ty:Number = matrix.ty; + const x:Number = topLeft.x; + const y:Number = topLeft.y; + + topLeft.x = minX + a * x + b * y + tx; + topLeft.y = minY + c * x + d * y + ty; + } + return staticPoint; + } + + /** + * Returns the axis aligned bounding box bounds transformed + * with matrix and then projected with projection. + * + * @param bounds The bounds, in child coordinates, to be transformed and projected. + * @param matrix

The transformation matrix. Note that the method will clobber the + * original matrix values.

+ * @param projection The projection. + * @return Returns the bounds parameter that has been updated with the + * transformed and projected bounds. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function projectBounds(bounds:Rectangle, + matrix:Matrix3D, + projection:PerspectiveProjection):Rectangle + { + // Setup the matrix + var centerX:Number = projection.projectionCenter.x; + var centerY:Number = projection.projectionCenter.y; + matrix.appendTranslation(-centerX, -centerY, projection.focalLength); + matrix.append(projection.toMatrix3D()); + + // Project the corner points + var pt1:Vector3D = new Vector3D(bounds.left, bounds.top, 0); + var pt2:Vector3D = new Vector3D(bounds.right, bounds.top, 0) + var pt3:Vector3D = new Vector3D(bounds.left, bounds.bottom, 0); + var pt4:Vector3D = new Vector3D(bounds.right, bounds.bottom, 0); + pt1 = Utils3D.projectVector(matrix, pt1); + pt2 = Utils3D.projectVector(matrix, pt2); + pt3 = Utils3D.projectVector(matrix, pt3); + pt4 = Utils3D.projectVector(matrix, pt4); + + // Find the bounding box in 2D + var maxX:Number = Math.max(Math.max(pt1.x, pt2.x), Math.max(pt3.x, pt4.x)); + var minX:Number = Math.min(Math.min(pt1.x, pt2.x), Math.min(pt3.x, pt4.x)); + var maxY:Number = Math.max(Math.max(pt1.y, pt2.y), Math.max(pt3.y, pt4.y)); + var minY:Number = Math.min(Math.min(pt1.y, pt2.y), Math.min(pt3.y, pt4.y)); + + // Add back the projection center + bounds.x = minX + centerX; + bounds.y = minY + centerY; + bounds.width = maxX - minX; + bounds.height = maxY - minY; + return bounds; + } + + /** + * @param matrix + * @return Returns true when pt == matrix.DeltaTransformPoint(pt) + * for any pt:Point (matrix is identity matrix, + * when disregarding the translation part). + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isDeltaIdentity(matrix:Matrix):Boolean + { + return (matrix.a == 1 && matrix.d == 1 && + matrix.b == 0 && matrix.c == 0); + } + + /** + * fitBounds Calculates a size (x,y) for a bounding box (0,0,x,y) + * such that the bounding box transformed with matrix will fit + * into (0,0,width,height). + * + * @param width This is the width of the bounding box that calculated size + * needs to fit in. + * + * @param height This is the height of the bounding box that the calculated + * size needs to fit in. + * + * @param matrix This defines the transformations that the function will take + * into account when calculating the size. The bounding box (0,0,x,y) of the + * calculated size (x,y) transformed with matrix will fit in the + * specified width and height. + * + * @param explicitWidth Explicit width for the calculated size. The function + * will first try to find a solution using this width. + * + * @param explicitHeight Preferred height for the calculated size. The function + * will first try to find a solution using this height. + * + * @param preferredWidth Preferred width for the calculated size. If possible + * the function will set the calculated size width to this value. + * + * @param preferredHeight Preferred height for the calculated size. If possible + * the function will set the calculated size height to this value. + * + * @param minWidth The minimum allowed value for the calculated size width. + * + * @param minHeight The minimum allowed value for the calculated size height. + * + * @param maxWidth The maximum allowed value for the calculated size width. + * + * @param maxHeight The maximum allowed value for the calculated size height. + * + * @return Returns the size (x,y) such that the bounding box (0,0,x,y) will + * fit into (0,0,width,height) after transformation with matrix. + * Returns null if there is no possible solution. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function fitBounds(width:Number, height:Number, matrix:Matrix, + explicitWidth:Number, explicitHeight:Number, + preferredWidth:Number, preferredHeight:Number, + minWidth:Number, minHeight:Number, + maxWidth:Number, maxHeight:Number):Point + { + if (isNaN(width) && isNaN(height)) + return new Point(preferredWidth, preferredHeight); + + // Allow for precision errors by including tolerance for certain values. + const newMinWidth:Number = (minWidth < MIN_MAX_TOLERANCE) ? 0 : minWidth - MIN_MAX_TOLERANCE; + const newMinHeight:Number = (minHeight < MIN_MAX_TOLERANCE) ? 0 : minHeight - MIN_MAX_TOLERANCE; + const newMaxWidth:Number = maxWidth + MIN_MAX_TOLERANCE; + const newMaxHeight:Number = maxHeight + MIN_MAX_TOLERANCE; + + var actualSize:Point; + + if (!isNaN(width) && !isNaN(height)) + { + actualSize = calcUBoundsToFitTBounds(width, height, matrix, + newMinWidth, newMinHeight, + newMaxWidth, newMaxHeight); + + // If we couldn't fit in both dimensions, try to fit only one and + // don't stick out of the other + if (!actualSize) + { + var actualSize1:Point; + actualSize1 = fitTBoundsWidth(width, matrix, + explicitWidth, explicitHeight, + preferredWidth, preferredHeight, + newMinWidth, newMinHeight, + newMaxWidth, newMaxHeight); + + // If we fit the width, but not the height. + if (actualSize1) + { + var fitHeight:Number = transformSize(actualSize1.x, actualSize1.y, matrix).y; + if (fitHeight - SOLUTION_TOLERANCE > height) + actualSize1 = null; + } + + var actualSize2:Point + actualSize2 = fitTBoundsHeight(height, matrix, + explicitWidth, explicitHeight, + preferredWidth, preferredHeight, + newMinWidth, newMinHeight, + newMaxWidth, newMaxHeight); + + // If we fit the height, but not the width + if (actualSize2) + { + var fitWidth:Number = transformSize(actualSize2.x, actualSize2.y, matrix).x; + if (fitWidth - SOLUTION_TOLERANCE > width) + actualSize2 = null; + } + + if (actualSize1 && actualSize2) + { + // Pick a solution + actualSize = ((actualSize1.x * actualSize1.y) > (actualSize2.x * actualSize2.y)) ? actualSize1 : actualSize2; + } + else if (actualSize1) + { + actualSize = actualSize1; + } + else + { + actualSize = actualSize2; + } + } + return actualSize; + } + else if (!isNaN(width)) + { + return fitTBoundsWidth(width, matrix, + explicitWidth, explicitHeight, + preferredWidth, preferredHeight, + newMinWidth, newMinHeight, + newMaxWidth, newMaxHeight); + } + else + { + return fitTBoundsHeight(height, matrix, + explicitWidth, explicitHeight, + preferredWidth, preferredHeight, + newMinWidth, newMinHeight, + newMaxWidth, newMaxHeight); + } + } + + /** + * @private + * + * fitTBoundsWidth Calculates a size (x,y) for a bounding box (0,0,x,y) + * such that the bounding box transformed with matrix will fit + * into the specified width. + * + * @param width This is the width of the bounding box that calculated size + * needs to fit in. + * + * @param matrix This defines the transformations that the function will take + * into account when calculating the size. The bounding box (0,0,x,y) of the + * calculated size (x,y) transformed with matrix will fit in the + * specified width and height. + * + * @param explicitWidth Explicit width for the calculated size. The function + * will first try to find a solution using this width. + * + * @param explicitHeight Preferred height for the calculated size. The function + * will first try to find a solution using this height. + * + * @param preferredWidth Preferred width for the calculated size. If possible + * the function will set the calculated size width to this value. + * + * @param preferredHeight Preferred height for the calculated size. If possible + * the function will set the calculated size height to this value. + * + * @param minWidth The minimum allowed value for the calculated size width. + * + * @param minHeight The minimum allowed value for the calculated size height. + * + * @param maxWidth The maximum allowed value for the calculated size width. + * + * @param maxHeight The maximum allowed value for the calculated size height. + * + * @return Returns the size (x,y) such that the bounding box (0,0,x,y) will + * fit into (0,0,width,height) after transformation with matrix. + * Returns null if there is no possible solution. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private static function fitTBoundsWidth(width:Number, matrix:Matrix, + explicitWidth:Number, explicitHeight:Number, + preferredWidth:Number, preferredHeight:Number, + minWidth:Number, minHeight:Number, + maxWidth:Number, maxHeight:Number):Point + { + var actualSize:Point; + + // cases 1 and 2: only explicit width or explicit height is specified, + // so we try to find a solution with that hard constraint. + if (!isNaN(explicitWidth) && isNaN(explicitHeight)) + { + actualSize = calcUBoundsToFitTBoundsWidth(width, matrix, + explicitWidth, preferredHeight, + explicitWidth, minHeight, + explicitWidth, maxHeight); + + if (actualSize) + return actualSize; + } + else if (isNaN(explicitWidth) && !isNaN(explicitHeight)) + { + actualSize = calcUBoundsToFitTBoundsWidth(width, matrix, + preferredWidth, explicitHeight, + minWidth, explicitHeight, + maxWidth, explicitHeight); + if (actualSize) + return actualSize; + } + + // case 3: default case. When explicitWidth, explicitHeight are both set + // or not set, we use the preferred size since calcUBoundsToFitTBoundsWidth + // will just pick one. + actualSize = calcUBoundsToFitTBoundsWidth(width, matrix, + preferredWidth, preferredHeight, + minWidth, minHeight, + maxWidth, maxHeight); + + return actualSize; + } + + /** + * @private + * + * fitTBoundsWidth Calculates a size (x,y) for a bounding box (0,0,x,y) + * such that the bounding box transformed with matrix will fit + * into the specified height. + * + * @param height This is the height of the bounding box that the calculated + * size needs to fit in. + * + * @param matrix This defines the transformations that the function will take + * into account when calculating the size. The bounding box (0,0,x,y) of the + * calculated size (x,y) transformed with matrix will fit in the + * specified width and height. + * + * @param explicitWidth Explicit width for the calculated size. The function + * will first try to find a solution using this width. + * + * @param explicitHeight Preferred height for the calculated size. The function + * will first try to find a solution using this height. + * + * @param preferredWidth Preferred width for the calculated size. If possible + * the function will set the calculated size width to this value. + * + * @param preferredHeight Preferred height for the calculated size. If possible + * the function will set the calculated size height to this value. + * + * @param minWidth The minimum allowed value for the calculated size width. + * + * @param minHeight The minimum allowed value for the calculated size height. + * + * @param maxWidth The maximum allowed value for the calculated size width. + * + * @param maxHeight The maximum allowed value for the calculated size height. + * + * @return Returns the size (x,y) such that the bounding box (0,0,x,y) will + * fit into (0,0,width,height) after transformation with matrix. + * Returns null if there is no possible solution. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private static function fitTBoundsHeight(height:Number, matrix:Matrix, + explicitWidth:Number, explicitHeight:Number, + preferredWidth:Number, preferredHeight:Number, + minWidth:Number, minHeight:Number, + maxWidth:Number, maxHeight:Number):Point + { + var actualSize:Point; + + // cases 1 and 2: only explicit width or explicit height is specified, + // so we try to find a solution with that hard constraint. + if (!isNaN(explicitWidth) && isNaN(explicitHeight)) + { + actualSize = calcUBoundsToFitTBoundsHeight(height, matrix, + explicitWidth, preferredHeight, + explicitWidth, minHeight, + explicitWidth, maxHeight); + + if (actualSize) + return actualSize; + } + else if (isNaN(explicitWidth) && !isNaN(explicitHeight)) + { + actualSize = calcUBoundsToFitTBoundsHeight(height, matrix, + preferredWidth, explicitHeight, + minWidth, explicitHeight, + maxWidth, explicitHeight); + if (actualSize) + return actualSize; + } + + // case 3: default case. When explicitWidth, explicitHeight are both set + // or not set, we use the preferred size since calcUBoundsToFitTBoundsWidth + // will just pick one. + actualSize = calcUBoundsToFitTBoundsHeight(height, matrix, + preferredWidth, preferredHeight, + minWidth, minHeight, + maxWidth, maxHeight); + + return actualSize; + } + + /** + * Calculates (x,y) such that the bounding box (0,0,x,y) transformed + * with matrix will have bounding box with + * height equal to h. + * x and y are restricted by minX, maxX and + * minY, maxY. + * + * If possible x will be set to preferredX or + * y will be set to preferredY. + * + * When there are multiple solutions, the function picks the one that + * minimizes the bounding box area of transformed (0,0,x,y). + * + * The functon assumes minX >= 0 and minY >= 0 + * (solution components x and y are non-negative). + * + * @return Returns Point(x,y) or null if no solution exists. + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + static public function calcUBoundsToFitTBoundsHeight(h:Number, + matrix:Matrix, + preferredX:Number, + preferredY:Number, + minX:Number, + minY:Number, + maxX:Number, + maxY:Number):Point + { + // Untransformed bounds size is (x,y). The corners of the untransformed + // bounding box are p1(0,0) p2(x,0) p3(0,y) p4(x,y). + // Matrix is | a c tx | + // | b d ty | + // + // After transfomation with the matrix those four points are: + // t1 = (0, 0) = matrix.deltaTransformPoint(p1) + // t2 = (ax, bx) = matrix.deltaTransformPoint(p2) + // t3 = (cy, dy) = matrix.deltaTransformPoint(p3) + // t4 = (ax + cy, cx + dy) = matrix.deltaTransformPoint(p4) + // + // The transformed bounds bounding box dimensions are (w,h): + // (1) w = max( t1.x, t2.x, t3.x, t4.x ) - min( t1.x, t2.x, t3.x, t4.x) + // (2) h = max( t1.y, t2.y, t3.y, t4.y ) - min( t1.y, t2.y, t3.y, t4.y) + // + // Looking at all the possible cases for min and max functions above, + // we can construct and solve simple linear systems for x and y. + // For example in the case of + // t1.x <= t2.x <= t3.x <= t4.x + // our first equation is + // (1) w = t4.x - t1.x <==> w = ax + cy + // + // To minimize the cases we're looking at we can take advantage of + // the limits we have: x >= 0, y >= 0; + // Taking into account these limits we deduce that: + // a*c >= 0 gives us (1) w = abs( t4.x - t1.x ) = abs( ax + cy ) + // a*c < 0 gives us (1) w = abs( t2.x - t3.x ) = abs( ax - cy ) + // b*d >= 0 gives us (2) h = abs( t4.y - t1.y ) = abs( bx + dy ) + // b*d < 0 gives us (2) h = abs( t2.y - t3.y ) = abs( bx - dy ) + // + // If we do a substitution such that + // c1 = a*c >= 0 ? c : -c + // d1 = b*d >= 0 ? d : -d + // we get the following linear system: + // (1) w = abs( ax + c1y ) + // (2) h = abs( bx + d1y ) + // + // Since we're matching height we only care about (2) + + var b:Number = matrix.b; + var d:Number = matrix.d; + + // If components are very close to zero, zero them out to handle the special cases + if (-1.0e-9 < b && b < +1.0e-9) + b = 0; + if (-1.0e-9 < d && d < +1.0e-9) + d = 0; + + if (b == 0 && d == 0) + return null; // No solution + + // Handle special cases first + if (b == 0 && d == 0) + return null; // No solution + + if (b == 0) + return new Point( preferredX, h / Math.abs(d) ); + else if (d == 0) + return new Point( h / Math.abs(b), preferredY ); + + const d1:Number = (b*d >= 0) ? d : -d; + // Now we have the following linear sytesm: + // (1) x = preferredX or y = preferredY + // (2) h = abs( bx + d1y ) + + var s:Point; + var x:Number; + var y:Number; + + if (d1 != 0 && preferredX > 0) + { + const invD1:Number = 1 / d1; + preferredX = Math.max(minX, Math.min(maxX, preferredX)); + x = preferredX; + + // Case1: + // bx + d1y >= 0 + // x = preferredX + y = (h - b * x) * invD1; + if (minY <= y && y <= maxY && + b * x + d1 * y >= 0 ) // Satisfy Case1 + { + s = new Point(x, y); + } + + // Case2: + // bx + d1y < 0 + // x = preferredX + y = (-h - b * x) * invD1; + if (minY <= y && y <= maxY && + b * x + d1 * y < 0 ) // Satisfy Case2 + { + // If there is no solution, or the new solution yields smaller value, pick the new solution. + if (!s || transformSize(s.x, s.y, matrix).x > transformSize(x, y, matrix).x) + s = new Point(x, y); + } + } + + if (b != 0 && preferredY > 0) + { + const invB:Number = 1 / b; + preferredY = Math.max(minY, Math.min(maxY, preferredY)); + y = preferredY; + + // Case3: + // bx + d1y >= 0 + // y = preferredY + x = ( h - d1 * y ) * invB; + if (minX <= x && x <= maxX && + b * x + d1 * y >= 0) // Satisfy Case3 + { + // If there is no solution, or the new solution yields smaller value, pick the new solution. + if (!s || transformSize(s.x, s.y, matrix).x > transformSize(x, y, matrix).x) + s = new Point(x, y); + } + + // Case4: + // bx + d1y < 0 + // y = preferredY + x = ( -h - d1 * y ) * invB; + if (minX <= x && x <= maxX && + b * x + d1 * y < 0) // Satisfy Case4 + { + // If there is no solution, or the new solution yields smaller value, pick the new solution. + if (!s || transformSize(s.x, s.y, matrix).x > transformSize(x, y, matrix).x) + s = new Point(x, y); + } + } + + // If there's already a solution that matches preferred dimention, return + if (s) + return s; + + // Find a solution that matches the width and minimizes the height: + const a:Number = matrix.a; + const c:Number = matrix.c; + const c1:Number = ( a*c >= 0 ) ? c : -c; + return solveEquation(b, d1, h, minX, minY, maxX, maxY, a, c1); + } + + /** + * Calculates (x,y) such that the bounding box (0,0,x,y) transformed + * with matrix will have bounding box with + * width equal to w. + * x and y are restricted by minX, maxX and + * minY, maxY. + * + * If possible x will be set to preferredX or + * y will be set to preferredY. + * + * When there are multiple solutions, the function picks the one that + * minimizes the bounding box area of transformed (0,0,x,y). + * + * The functon assumes minX >= 0 and minY >= 0 + * (solution components x and y are non-negative). + * + * @return Returns Point(x,y) or null if no solution exists. + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + static public function calcUBoundsToFitTBoundsWidth(w:Number, + matrix:Matrix, + preferredX:Number, + preferredY:Number, + minX:Number, + minY:Number, + maxX:Number, + maxY:Number):Point + { + // Untransformed bounds size is (x,y). The corners of the untransformed + // bounding box are p1(0,0) p2(x,0) p3(0,y) p4(x,y). + // Matrix is | a c tx | + // | b d ty | + // + // After transfomation with the matrix those four points are: + // t1 = (0, 0) = matrix.deltaTransformPoint(p1) + // t2 = (ax, bx) = matrix.deltaTransformPoint(p2) + // t3 = (cy, dy) = matrix.deltaTransformPoint(p3) + // t4 = (ax + cy, cx + dy) = matrix.deltaTransformPoint(p4) + // + // The transformed bounds bounding box dimensions are (w,h): + // (1) w = max( t1.x, t2.x, t3.x, t4.x ) - min( t1.x, t2.x, t3.x, t4.x) + // (2) h = max( t1.y, t2.y, t3.y, t4.y ) - min( t1.y, t2.y, t3.y, t4.y) + // + // Looking at all the possible cases for min and max functions above, + // we can construct and solve simple linear systems for x and y. + // For example in the case of + // t1.x <= t2.x <= t3.x <= t4.x + // our first equation is + // (1) w = t4.x - t1.x <==> w = ax + cy + // + // To minimize the cases we're looking at we can take advantage of + // the limits we have: x >= 0, y >= 0; + // Taking into account these limits we deduce that: + // a*c >= 0 gives us (1) w = abs( t4.x - t1.x ) = abs( ax + cy ) + // a*c < 0 gives us (1) w = abs( t2.x - t3.x ) = abs( ax - cy ) + // b*d >= 0 gives us (2) h = abs( t4.y - t1.y ) = abs( bx + dy ) + // b*d < 0 gives us (2) h = abs( t2.y - t3.y ) = abs( bx - dy ) + // + // If we do a substitution such that + // c1 = a*c >= 0 ? c : -c + // d1 = b*d >= 0 ? d : -d + // we get the following linear system: + // (1) w = abs( ax + c1y ) + // (2) h = abs( bx + d1y ) + // + // Since we're matching width we only care about (1) + + var a:Number = matrix.a; + var c:Number = matrix.c; + + // If components are very close to zero, zero them out to handle the special cases + if (-1.0e-9 < a && a < +1.0e-9) + a = 0; + if (-1.0e-9 < c && c < +1.0e-9) + c = 0; + + // Handle special cases first + if (a == 0 && c == 0) + return null; // No solution + + if (a == 0) + return new Point( preferredX, w / Math.abs(c) ); + else if (c == 0) + return new Point( w / Math.abs(a), preferredY ); + + const c1:Number = ( a*c >= 0 ) ? c : -c; + // Now we have the following linear sytesm: + // (1) w = abs( ax + c1y ) + // (2) x = preferredX or y = preferredY + + var s:Point; + var x:Number; + var y:Number; + + if (c1 != 0 && preferredX > 0) + { + const invC1:Number = 1 / c1; + preferredX = Math.max(minX, Math.min(maxX, preferredX)); + x = preferredX; + + // Case1: + // a * x + c1 * y >= 0 + // x = preferredX + y = (w - a * x) * invC1; + if (minY <= y && y <= maxY && + a * x + c1 * y >= 0 ) // Satisfy Case1 + { + s = new Point(x, y); + } + + // Case2: + // a * x + c1 * y < 0 + // x = preferredX + y = (-w - a * x) * invC1; + if (minY <= y && y <= maxY && + a * x + c1 * y < 0 ) // Satisfy Case2 + { + // If there is no solution, or the new solution yields smaller value, pick the new solution. + if (!s || transformSize(s.x, s.y, matrix).y > transformSize(x, y, matrix).y) + s = new Point(x, y); + } + } + + if (a != 0 && preferredY > 0) + { + const invA:Number = 1 / a; + preferredY = Math.max(minY, Math.min(maxY, preferredY)); + y = preferredY; + + // Case3: + // a * x + c1 * y >= 0 + // y = preferredY + x = (w - c1 * y ) * invA; + if (minX <= x && x <= maxX && + a * x + c1 * y >= 0) // Satisfy Case3 + { + // If there is no solution, or the new solution yields smaller value, pick the new solution. + if (!s || transformSize(s.x, s.y, matrix).y > transformSize(x, y, matrix).y) + s = new Point(x, y); + } + + // Case4: + // a * x + c1 * y < 0 + // y = preferredY + x = (-w - c1 * y ) * invA; + if (minX <= x && x <= maxX && + a * x + c1 * y < 0) // Satisfy Case4 + { + // If there is no solution, or the new solution yields smaller value, pick the new solution. + if (!s || transformSize(s.x, s.y, matrix).y > transformSize(x, y, matrix).y) + s = new Point(x, y); + } + } + + // If there's already a solution that matches preferred dimention, return + if (s) + return s; + + // Find a solution that matches the width and minimizes the height: + const b:Number = matrix.b; + const d:Number = matrix.d; + const d1:Number = (b*d >= 0) ? d : -d; + return solveEquation(a, c1, w, minX, minY, maxX, maxY, b, d1); + } + + /** + * Finds a solution (x,y) for the equation abs(a*x + c*y) = w such that + * abs(b*x +d*y) is minimized. + * If there is infinite number of solutions, x and y are picked to be + * as close as possible. + * + * Doesn't handle cases where a or c are zero. + * + * @return Returns Point(x,y) + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + static private function solveEquation(a:Number, + c:Number, + w:Number, + minX:Number, + minY:Number, + maxX:Number, + maxY:Number, + b:Number, + d:Number):Point + { + if (a == 0 || c == 0) + return null; // x and y are not co-dependent + + // (1) w = abs( ax + cy ) + // Find the range of solutsion for y and pick: + var x:Number; + var y:Number; + var s:Point; + + // Case1: ax + cy >= 0, from (1) above we get: + // (1) x = (w - cy) / a + // + // Lets find the possible range of values for y: + // We know that + // (3) minX <= x <= maxX + // + // Substitute x with (w - cy)/a in (3): + // (3) minX - w/a <= -cy/a <= maxX - w/a + // (3) min( A, B ) <= y <= max( A, B ), where + // A = (minX - w/a) * (-a/c) + // B = (maxX - w/a) * (-a/c) + + var A:Number = (w - minX * a) / c; + var B:Number = (w - maxX * a) / c; + var rangeMinY:Number = Math.max(minY, Math.min(A, B)); + var rangeMaxY:Number = Math.min(maxY, Math.max(A, B)); + const det:Number = (b * c - a * d); + + // We have a possible solution for Case1 if the range for y is valid + if (rangeMinY <= rangeMaxY) + { + // Now that we have a valid range for y, we need to pick a value within + // that range. + // + // We calculate the value based on a custom condition. + // + // The custom condition that we use could be anything that defines + // another equation for x and y. Some examples are: + // "make x and y as close as possible": y = w / ( a + c ); + // "minimize abs(bx + dy)": y = b * w / det + // "preserve aspect ratio": y = w / ( a * preferredX / preferredY + c ); + if (Math.abs(det) < 1.0e-9) + { + // There is infinite number of solutions, lets pick x == y + y = w / ( a + c ); + } + else + { + // Minimize abs(bx + dy) - we need to solve: + // abs( b * ( w - c * y ) / a + d * y ) = 0 + // which gives us: + y = b * w / det; + } + + // Now that we have the y value calculated from the custom condition, + // we clamp with the range. This gives us a solution with + // values as close as possible to satisfy our custom condition when + // the condition is a linear function of x and y (in our case it is). + y = Math.max(rangeMinY, Math.min(y, rangeMaxY)); + + x = (w - c * y) / a; + return new Point(x, y); + } + + // Case2: ax + cy < 0, from (1) above we get: + // (1) x = (-w - cy) / a + // + // Lets find the possible range of values for y: + // We know that + // (3) minX <= x <= maxX + // + // Substitute x with (-w - cy)/a in (3): + // (3) minX + w/a <= -cy/a <= maxX + w/a + // (3) min( A, B ) <= y <= max( A, B ), where + // A = (minX + w/a) * (-a/c) + // B = (maxX + w/a) * (-a/c) + + A = -(minX * a + w) / c; + B = -(maxX * a + w) / c; + rangeMinY = Math.max(minY, Math.min(A, B)); + rangeMaxY = Math.min(maxY, Math.max(A, B)); + + // We have a possible solution for Case2 if the range for y is valid + if (rangeMinY <= rangeMaxY) + { + // Now that we have a valid range for y, we need to pick a value within + // that range. + // + // We calculate the value based on a custom condition. + // + // The custom condition that we use could be anything that defines + // another equation for x and y. Some examples are: + // "make x and y as close as possible": y = -w / ( a + c ); + // "minimize abs(bx + dy)": y = -b * w / det + // "preserve aspect ratio": y = w / ( a * preferredX / preferredY + c ); + if (Math.abs(det) < 1.0e-9) + { + // There is infinite number of solutions, lets pick x == y + y = -w / ( a + c ); + } + else + { + // Minimize abs(bx + dy) - we need to solve: + // abs( b * ( -w - c * y ) / a + d * y ) = 0 + // which gives us: + y = -b * w / det; + } + + // Now that we have the y value calculated from the custom condition, + // we clamp with the range. This gives us a solution with + // values as close as possible to satisfy our custom condition when + // the condition is a linear function of x and y (in our case it is). + y = Math.max(rangeMinY, Math.min(y, rangeMaxY)); + x = (-w - c * y) / a; + return new Point(x, y); + + } + return null; // No solution + } + + /** + * Calculates (x,y) such that the bounding box (0,0,x,y) transformed + * with matrix will have bounding box (0,0,w,h). + * x and y are restricted by minX, maxX and + * minY, maxY. + * + * When there is infinite number of solutions, the function will + * calculate x and y to be as close as possible. + * + * The functon assumes minX >= 0 and minY >= 0 + * (solution components x and y are non-negative). + * + * @return Point(x,y) or null if no solution exists. + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + static public function calcUBoundsToFitTBounds(w:Number, + h:Number, + matrix:Matrix, + minX:Number, + minY:Number, + maxX:Number, + maxY:Number):Point + { + // Untransformed bounds size is (x,y). The corners of the untransformed + // bounding box are p1(0,0) p2(x,0) p3(0,y) p4(x,y). + // Matrix is | a c tx | + // | b d ty | + // + // After transfomation with the matrix those four points are: + // t1 = (0, 0) = matrix.deltaTransformPoint(p1) + // t2 = (ax, bx) = matrix.deltaTransformPoint(p2) + // t3 = (cy, dy) = matrix.deltaTransformPoint(p3) + // t4 = (ax + cy, cx + dy) = matrix.deltaTransformPoint(p4) + // + // The transformed bounds bounding box dimensions are (w,h): + // (1) w = max( t1.x, t2.x, t3.x, t4.x ) - min( t1.x, t2.x, t3.x, t4.x) + // (2) h = max( t1.y, t2.y, t3.y, t4.y ) - min( t1.y, t2.y, t3.y, t4.y) + // + // Looking at all the possible cases for min and max functions above, + // we can construct and solve simple linear systems for x and y. + // For example in the case of + // t1.x <= t2.x <= t3.x <= t4.x + // our first equation is + // (1) w = t4.x - t1.x <==> w = ax + cy + // + // To minimize the cases we're looking at we can take advantage of + // the limits we have: x >= 0, y >= 0; + // Taking into account these limits we deduce that: + // a*c >= 0 gives us (1) w = abs( t4.x - t1.x ) = abs( ax + cy ) + // a*c < 0 gives us (1) w = abs( t2.x - t3.x ) = abs( ax - cy ) + // b*d >= 0 gives us (2) h = abs( t4.y - t1.y ) = abs( bx + dy ) + // b*d < 0 gives us (2) h = abs( t2.y - t3.y ) = abs( bx - dy ) + // + // If we do a substitution such that + // c1 = a*c >= 0 ? c : -c + // d1 = b*d >= 0 ? d : -d + // we get the following linear system: + // (1) w = abs( ax + c1y ) + // (2) h = abs( bx + d1y ) + // + + var a:Number = matrix.a; + var b:Number = matrix.b; + var c:Number = matrix.c; + var d:Number = matrix.d; + + // If components are very close to zero, zero them out to handle the special cases + if (-1.0e-9 < a && a < +1.0e-9) + a = 0; + if (-1.0e-9 < b && b < +1.0e-9) + b = 0; + if (-1.0e-9 < c && c < +1.0e-9) + c = 0; + if (-1.0e-9 < d && d < +1.0e-9) + d = 0; + + // Handle special cases. + if (b == 0 && c == 0) + { + // No solution in the following cases since the matrix collapses + // all points into a line. + if (a == 0 || d == 0) + return null; + + // (1) w = abs( ax + cy ) <=> w = abs( ax ) <=> w = abs(a)x + // (2) h = abs( bx + dy ) <=> h = abs( dy ) <=> h = abs(d)y + return new Point(w / Math.abs(a), h / Math.abs(d)); + } + + if (a == 0 && d == 0) + { + // No solution in the following cases since the matrix collapses + // all points into a line. + if (b == 0 || c == 0) + return null; + + // (1) w = abs( ax + cy ) <=> w = abs( cy ) <=> w = abs(c)y + // (2) h = abs( bx + dy ) <=> h = abs( bx ) <=> h = abs(b)x + return new Point(h / Math.abs(b), w / Math.abs(c)); + } + + // Handle general cases. + const c1:Number = ( a*c >= 0 ) ? c : -c; + const d1:Number = ( b*d >= 0 ) ? d : -d; + // we get the following linear system: + // (1) w = abs( ax + c1y ) + // (2) h = abs( bx + d1y ) + + // Calculate the determinant of the system + const det:Number = a * d1 - b * c1; + if (Math.abs(det) < 1.0e-9) + { + // No solution in these cases since the matrix + // collapses all points into a line. + if (c1 == 0 || a == 0 || a == -c1) + return null; + + if (Math.abs(a * h - b * w) > 1.0e-9) + return null; // No solution in this case + + // Determinant is zero, the equations (1) & (2) are equivalent and + // we have only one equation: + // (1) w = abs( ax + c1y ) + // + // Solve it finding x and y as close as possible: + return solveEquation(a, c1, w, minX, minX, maxX, maxY, b, d1); + } + + // Pre-multiply w & h by the inverse dteterminant + const invDet:Number = 1 / det; + w *= invDet; + h *= invDet; + + // Case 1: + // a * x + c1 * y >= 0 + // b * x + d1 * y >= 0 + var s:Point; + s = solveSystem(a, c1, b, d1, w, h); + if (s && + minX <= s.x && s.x <= maxX && minY <= s.y && s.y <= maxY && + a * s.x + c1 * s.x >= 0 && + b * s.x + d1 * s.y >= 0) + return s; + + // Case 2: + // a * x + c1 * y >= 0 + // b * x + d1 * y < 0 + s = solveSystem( a, c1, b, d1, w, -h); + if (s && + minX <= s.x && s.x <= maxX && minY <= s.y && s.y <= maxY && + a * s.x + c1 * s.x >= 0 && + b * s.x + d1 * s.y < 0) + return s; + + // Case 3: + // a * x + c1 * y < 0 + // b * x + d1 * y >= 0 + s = solveSystem( a, c1, b, d1, -w, h); + if (s && + minX <= s.x && s.x <= maxX && minY <= s.y && s.y <= maxY && + a * s.x + c1 * s.x < 0 && + b * s.x + d1 * s.y >= 0) + return s; + + // Case 4: + // a * x + c1 * y < 0 + // b * x + d1 * y < 0 + s = solveSystem( a, c1, b, d1, -w, -h); + if (s && + minX <= s.x && s.x <= maxX && minY <= s.y && s.y <= maxY && + a * s.x + c1 * s.x < 0 && + b * s.x + d1 * s.y < 0) + return s; + + return null; // No solution. + } + + /** + * Determine if two Matrix instances are equal. + * + * @return true if the matrices are equal. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isEqual(m1:Matrix, m2:Matrix):Boolean + { + return ((m1 && m2 && + m1.a == m2.a && + m1.b == m2.b && + m1.c == m2.c && + m1.d == m2.d && + m1.tx == m2.tx && + m1.ty == m2.ty) || + (!m1 && !m2)); + } + + /** + * Determine if two Matrix3D instances are equal. + * + * @return true if the matrices are equal. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isEqual3D(m1:Matrix3D, m2:Matrix3D):Boolean + { + if (m1 && m2 && m1.rawData.length == m2.rawData.length) + { + var r1:Vector. = m1.rawData; + var r2:Vector. = m2.rawData; + + return (r1[0] == r2[0] && + r1[1] == r2[1] && + r1[2] == r2[2] && + r1[3] == r2[3] && + r1[4] == r2[4] && + r1[5] == r2[5] && + r1[6] == r2[6] && + r1[7] == r2[7] && + r1[8] == r2[8] && + r1[9] == r2[9] && + r1[10] == r2[10] && + r1[11] == r2[11] && + r1[12] == r2[12] && + r1[13] == r2[13] && + r1[14] == r2[14] && + r1[15] == r2[15]); + } + + return (!m1 && !m2); + } + + /** + * Calculates (x,y) such as to satisfy the linear system: + * | a * x + c * y = m + * | b * x + d * y = n + * + * @param mOverDet mOverDet must be equal to m / (a*d - b*c) + * @param nOverDet mOverDet must be equal to n / (a*d - b*c) + * + * @return returns Point(x,y) + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + static private function solveSystem(a:Number, + c:Number, + b:Number, + d:Number, + mOverDet:Number, + nOverDet:Number):Point + { + return new Point(d * mOverDet - c * nOverDet, + a * nOverDet - b * mOverDet); + } + + /** + * Workaround for player's concatenatedMatrix being wrong in some situations, such + * as when there is a filter or a scrollRect somewhere in the object's container + * hierarchy. Walk the parent tree manually, calculating the matrix manually. + * + * @param displayObject Calculate the concatenatedMatrix for this displayObject + * + * @param topParent

When specified, the matrix is computed up to the topParent, + * excluding topParent's concatenated matrix. This is useful when computing a transform + * in order to move an object to a different parent but the object's transform needs + * to be adjusted in order to keep its original position on screen.

+ * + * @return The concatenatedMatrix for the displayObject + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static function getConcatenatedMatrix(displayObject:DisplayObject, topParent:DisplayObject):Matrix + { + return getConcatenatedMatrixHelper(displayObject, false, topParent); + } + + /** + * Workaround for player's concatenatedMatrix being wrong in some situations, such + * as when there is a filter or a scrollRect somewhere in the object's container + * hierarchy. Walk the parent tree manually, calculating the matrix manually. + * + * This function differs from getConcatenatedMatrix in that it combines the + * computedMatrix of each ancestor. The computedMatrix includes transform offsets. + * + * @param displayObject Calculate the concatenatedMatrix for this displayObject + * + * @param topParent

When specified, the matrix is computed up to the topParent, + * excluding topParent's concatenated matrix. This is useful when computing a transform + * in order to move an object to a different parent but the object's transform needs + * to be adjusted in order to keep its original position on screen.

+ * + * @return The concatenatedMatrix for the displayObject + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4 + */ + public static function getConcatenatedComputedMatrix(displayObject:DisplayObject, topParent:DisplayObject):Matrix + { + return getConcatenatedMatrixHelper(displayObject, true, topParent); + } + + /** + * @private + */ + private static function getConcatenatedMatrixHelper(displayObject:DisplayObject, useComputedMatrix:Boolean, topParent:DisplayObject):Matrix + { + var m:Matrix = new Matrix(); + + // This check should be made once per top-level ApplicationDomain + if (usesMarshalling == null) + { + // Check if marshalling support has been turned on + usesMarshalling = ApplicationDomain.currentDomain.hasDefinition("mx.managers.systemClasses.MarshallingSupport"); + + // If we aren't using marshalling, then we only have one ApplicationDomain and thus one class + // definition for UIComponent + if (!usesMarshalling && ApplicationDomain.currentDomain.hasDefinition("mx.core.UIComponent")) + uiComponentClass = Class(ApplicationDomain.currentDomain.getDefinition("mx.core.UIComponent")); + // same thing for UIMovieClip + if (!usesMarshalling && ApplicationDomain.currentDomain.hasDefinition("mx.flash.UIMovieClip")) + uiMovieClipClass = Class(ApplicationDomain.currentDomain.getDefinition("mx.flash.UIMovieClip")); + } + + // Note, root will be "null" if the displayObject is off the display list. In particular, + // during start-up, before applicationComplete is dispatched, root will be null. + // Note that getConcatenatedMatrixHelper() with topParent == sandboxRoot will still work + // correctly in those cases as we use ".$parent" to walk up the parent chain and during start-up + // $parent will be null for the application before applicationComplete has been dispatched. + + if (fakeDollarParent == null) + fakeDollarParent = new QName(mx_internal, "$parent"); + + if (useComputedMatrix && computedMatrixProperty == null) + computedMatrixProperty = new QName(mx_internal, "computedMatrix"); + + if ($transformProperty == null) + $transformProperty = new QName(mx_internal, "$transform"); + + while (displayObject && displayObject.transform.matrix && displayObject != topParent) + { + var scrollRect:Rectangle = displayObject.scrollRect; + if (scrollRect != null) + m.translate(-scrollRect.x, -scrollRect.y); + + // If we are using marshalling, we can have multiple class definitions of UIComponent + if (usesMarshalling && "moduleFactory" in displayObject) + { + var moduleFactory:Object = displayObject["moduleFactory"]; + // If the module factory has changed, then we are in a different ApplicationDomain + if (moduleFactory && moduleFactory !== lastModuleFactory && "info" in moduleFactory) + { + var appDomain:ApplicationDomain; + + appDomain = moduleFactory["info"]()["currentDomain"]; + // Get the class definition for UIComponent in the current ApplicationDomain + if (appDomain && appDomain.hasDefinition("mx.core.UIComponent")) + uiComponentClass = Class(appDomain.getDefinition("mx.core.UIComponent")); + // same thing for UIMovieClip + if (appDomain && appDomain.hasDefinition("mx.flash.UIMovieClip")) + uiMovieClipClass = Class(appDomain.getDefinition("mx.flash.UIMovieClip")); + + lastModuleFactory = moduleFactory; + } + } + + var isUIComponent:Boolean = uiComponentClass && displayObject is uiComponentClass; + var isUIMovieClip:Boolean = uiMovieClipClass && displayObject is uiMovieClipClass; + + if (useComputedMatrix && isUIComponent) + m.concat(displayObject[computedMatrixProperty]); + else if (isUIMovieClip) + m.concat(displayObject[$transformProperty].matrix); + else + m.concat(displayObject.transform.matrix); + + // Try to access $parent, which is the true display list parent + if (isUIComponent) + displayObject = displayObject[fakeDollarParent] as DisplayObject; + else + displayObject = displayObject.parent as DisplayObject; + } + return m; + } + +} + +} diff --git a/src/mx/utils/NameUtil.as b/src/mx/utils/NameUtil.as new file mode 100644 index 00000000..66ad1536 --- /dev/null +++ b/src/mx/utils/NameUtil.as @@ -0,0 +1,179 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils +{ + +import flash.display.DisplayObject; +import flash.utils.getQualifiedClassName; +//import mx.core.IRepeaterClient; + +/** + * The NameUtil utility class defines static methods for + * creating names for Flex objects. + * You do not create instances of NameUtil; + * instead you call static methods of the class, such as + * the NameUtil.createName() method. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class NameUtil +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + private static var counter:int = 0; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Creates a unique name for any Object instance, such as "Button12", by + * combining the unqualified class name with an incrementing counter. + * + * @param object Object requiring a name. + * + * @return String containing the unique name. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function createUniqueName(object:Object):String + { + if (!object) + return null; + + var name:String = getQualifiedClassName(object); + + // If there is a package name, strip it off. + var index:int = name.indexOf("::"); + if (index != -1) + name = name.substr(index + 2); + + // If the class name ends with a digit (which some autogenerated + // classes do), then append an underscore before appending + // the counter. + var charCode:int = name.charCodeAt(name.length - 1); + if (charCode >= 48 && charCode <= 57) + name += "_"; + + return name + counter++; + } + + /** + * Returns a string, such as + * "MyApplication0.addressForm.lastName.TextField17", + * for a DisplayObject object that indicates its position in the + * hierarchy of DisplayObject objects in an application. + * + * @param displayObject A DisplayObject object whose hierarchy in the application + * is desired. + * + * @return String containing the position of displayObject + * in the hierarchy of DisplayObject objects in an application. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function displayObjectToString( + displayObject:DisplayObject):String + { + var result:String; + + // Start at the specified object and walk up the parent chain + // to build up the string to return. + try + { + for (var o:DisplayObject = displayObject; + o != null; + o = o.parent) + { + // If this object is in the display tree, + // stop after we've prepended the topmost Application instance. + if (o.parent && o.stage && o.parent == o.stage) + break; + + // Prefer id over name if specified. + var s:String = "id" in o && o["id"] ? o["id"] : o.name; + /* + if (o is IRepeaterClient) + { + var indices:Array = IRepeaterClient(o).instanceIndices; + if (indices) + s += "[" + indices.join("][") + "]"; + } + */ + result = result == null ? s : s + "." + result; + } + } + catch (e:SecurityError) + { + // Ignore error and continue with what we have. + // We may not have access to our parent if we are loaded into a sandbox. + } + + return result; + } + + /** + * Returns the name of the specified object's class, + * such as "Button" + * + *

This string does not include the package name. + * If you need the package name as well, call the + * getQualifiedClassName() method in the flash.utils package. + * It will return a string such as "mx.controls::Button".

+ * + * @param object The object. + * + * @return The name of the specified object's class. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getUnqualifiedClassName(object:Object):String + { + var name:String; + if (object is String) + name = object as String; + else + name = getQualifiedClassName(object); + + // If there is a package name, strip it off. + var index:int = name.indexOf("::"); + if (index != -1) + name = name.substr(index + 2); + + return name; + } +} + +} diff --git a/src/mx/utils/StringUtil.as b/src/mx/utils/StringUtil.as new file mode 100644 index 00000000..8feb1dbb --- /dev/null +++ b/src/mx/utils/StringUtil.as @@ -0,0 +1,346 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils +{ + +/** + * The StringUtil utility class is an all-static class with methods for + * working with String objects within Flex. + * You do not create instances of StringUtil; + * instead you call methods such as + * the StringUtil.substitute() method. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class StringUtil +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Removes all whitespace characters from the beginning and end + * of the specified string. + * + * @param str The String whose whitespace should be trimmed. + * + * @return Updated String where whitespace was removed from the + * beginning and end. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function trim(str:String):String + { + if (str == null) return ''; + + var startIndex:int = 0; + while (isWhitespace(str.charAt(startIndex))) + ++startIndex; + + var endIndex:int = str.length - 1; + while (isWhitespace(str.charAt(endIndex))) + --endIndex; + + if (endIndex >= startIndex) + return str.slice(startIndex, endIndex + 1); + else + return ""; + } + + /** + * Removes all whitespace characters from the beginning and end + * of each element in an Array, where the Array is stored as a String. + * + * @param value The String whose whitespace should be trimmed. + * + * @param separator The String that delimits each Array element in the string. + * + * @return Updated String where whitespace was removed from the + * beginning and end of each element. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function trimArrayElements(value:String, delimiter:String):String + { + if (value != "" && value != null) + { + var items:Array = value.split(delimiter); + + var len:int = items.length; + for (var i:int = 0; i < len; i++) + { + items[i] = StringUtil.trim(items[i]); + } + + if (len > 0) + { + value = items.join(delimiter); + } + } + + return value; + } + + /** + * Returns true if the specified string is + * a single space, tab, carriage return, newline, or formfeed character. + * + * @param str The String that is is being queried. + * + * @return true if the specified string is + * a single space, tab, carriage return, newline, or formfeed character. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isWhitespace(character:String):Boolean + { + switch (character) + { + case " ": + case "\t": + case "\r": + case "\n": + case "\f": + return true; + + default: + return false; + } + } + + /** + * Substitutes "{n}" tokens within the specified string + * with the respective arguments passed in. + * + * @param str The string to make substitutions in. + * This string can contain special tokens of the form + * {n}, where n is a zero based index, + * that will be replaced with the additional parameters + * found at that index if specified. + * + * @param rest Additional parameters that can be substituted + * in the str parameter at each {n} + * location, where n is an integer (zero based) + * index value into the array of values specified. + * If the first parameter is an array this array will be used as + * a parameter list. + * This allows reuse of this routine in other methods that want to + * use the ... rest signature. + * For example
+     *     public function myTracer(str:String, ... rest):void
+     *     { 
+     *         label.text += StringUtil.substitute(str, rest) + "\n";
+     *     } 
+ * + * @return New string with all of the {n} tokens + * replaced with the respective arguments specified. + * + * @example + * + * var str:String = "here is some info '{0}' and {1}"; + * trace(StringUtil.substitute(str, 15.4, true)); + * + * // this will output the following string: + * // "here is some info '15.4' and true" + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function substitute(str:String, ... rest):String + { + if (str == null) return ''; + + // Replace all of the parameters in the msg string. + var len:uint = rest.length; + var args:Array; + if (len == 1 && rest[0] is Array) + { + args = rest[0] as Array; + len = args.length; + } + else + { + args = rest; + } + + for (var i:int = 0; i < len; i++) + { + str = str.replace(new RegExp("\\{"+i+"\\}", "g"), args[i]); + } + + return str; + } + + /** + * Returns a string consisting of a specified string + * concatenated with itself a specified number of times. + * + * @param str The string to be repeated. + * + * @param n The repeat count. + * + * @return The repeated string. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + public static function repeat(str:String, n:int):String + { + if (n == 0) + return ""; + + var s:String = str; + for (var i:int = 1; i < n; i++) + { + s += str; + } + return s; + } + + /** + * Removes "unallowed" characters from a string. + * A "restriction string" such as "A-Z0-9" + * is used to specify which characters are allowed. + * This method uses the same logic as the restrict + * property of TextField. + * + * @param str The input string. + * + * @param restrict The restriction string. + * + * @return The input string, minus any characters + * that are not allowed by the restriction string. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 1.5 + * @productversion Flex 4.1 + */ + public static function restrict(str:String, restrict:String):String + { + // A null 'restrict' string means all characters are allowed. + if (restrict == null) + return str; + + // An empty 'restrict' string means no characters are allowed. + if (restrict == "") + return ""; + + // Otherwise, we need to test each character in 'str' + // to determine whether the 'restrict' string allows it. + var charCodes:Array = []; + + var n:int = str.length; + for (var i:int = 0; i < n; i++) + { + var charCode:uint = str.charCodeAt(i); + if (testCharacter(charCode, restrict)) + charCodes.push(charCode); + } + + return String.fromCharCode.apply(null, charCodes); + } + + /** + * @private + * Helper method used by restrict() to test each character + * in the input string against the restriction string. + * The logic in this method implements the same algorithm + * as in TextField's 'restrict' property (which is quirky, + * such as how it handles a '-' at the beginning of the + * restriction string). + */ + private static function testCharacter(charCode:uint, + restrict:String):Boolean + { + var allowIt:Boolean = false; + + var inBackSlash:Boolean = false; + var inRange:Boolean = false; + var setFlag:Boolean = true; + var lastCode:uint = 0; + + var n:int = restrict.length; + var code:uint; + + if (n > 0) + { + code = restrict.charCodeAt(0); + if (code == 94) // caret + allowIt = true; + } + + for (var i:int = 0; i < n; i++) + { + code = restrict.charCodeAt(i) + + var acceptCode:Boolean = false; + if (!inBackSlash) + { + if (code == 45) // hyphen + inRange = true; + else if (code == 94) // caret + setFlag = !setFlag; + else if (code == 92) // backslash + inBackSlash = true; + else + acceptCode = true; + } + else + { + acceptCode = true; + inBackSlash = false; + } + + if (acceptCode) + { + if (inRange) + { + if (lastCode <= charCode && charCode <= code) + allowIt = setFlag; + inRange = false; + lastCode = 0; + } + else + { + if (charCode == code) + allowIt = setFlag; + lastCode = code; + } + } + } + + return allowIt; + } +} + +} diff --git a/src/mx/utils/UIDUtil.as b/src/mx/utils/UIDUtil.as new file mode 100644 index 00000000..9a310def --- /dev/null +++ b/src/mx/utils/UIDUtil.as @@ -0,0 +1,420 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils +{ + +import flash.utils.ByteArray; +import flash.utils.Dictionary; + +//import mx.core.IPropertyChangeNotifier; +//import mx.core.IUIComponent; +//import mx.core.IUID; +import mx.core.mx_internal; + +use namespace mx_internal; + +/** + * The UIDUtil class is an all-static class + * with methods for working with UIDs (unique identifiers) within Flex. + * You do not create instances of UIDUtil; + * instead you simply call static methods such as the + * UIDUtil.createUID() method. + * + *

Note: If you have a dynamic object that has no [Bindable] properties + * (which force the object to implement the IUID interface), Flex adds an + * mx_internal_uid property that contains a UID to the object. + * To avoid having this field + * in your dynamic object, make it [Bindable], implement the IUID interface + * in the object class, or set a uid property with a value.

+ * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class UIDUtil +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class constants + // + //-------------------------------------------------------------------------- + + /** + * @private + * Char codes for 0123456789ABCDEF + */ + private static const ALPHA_CHAR_CODES:Array = [48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 65, 66, 67, 68, 69, 70]; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * This Dictionary records all generated uids for all existing items. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + private static var uidDictionary:Dictionary = new Dictionary(true); + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Generates a UID (unique identifier) based on ActionScript's + * pseudo-random number generator and the current time. + * + *

The UID has the form + * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + * where X is a hexadecimal digit (0-9, A-F).

+ * + *

This UID will not be truly globally unique; but it is the best + * we can do without player support for UID generation.

+ * + * @return The newly-generated UID. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function createUID():String + { + var uid:Array = new Array(36); + var index:int = 0; + + var i:int; + var j:int; + + for (i = 0; i < 8; i++) + { + uid[index++] = ALPHA_CHAR_CODES[Math.floor(Math.random() * 16)]; + } + + for (i = 0; i < 3; i++) + { + uid[index++] = 45; // charCode for "-" + + for (j = 0; j < 4; j++) + { + uid[index++] = ALPHA_CHAR_CODES[Math.floor(Math.random() * 16)]; + } + } + + uid[index++] = 45; // charCode for "-" + + var time:Number = new Date().getTime(); + // Note: time is the number of milliseconds since 1970, + // which is currently more than one trillion. + // We use the low 8 hex digits of this number in the UID. + // Just in case the system clock has been reset to + // Jan 1-4, 1970 (in which case this number could have only + // 1-7 hex digits), we pad on the left with 7 zeros + // before taking the low digits. + var timeString:String = ("0000000" + time.toString(16).toUpperCase()).substr(-8); + + for (i = 0; i < 8; i++) + { + uid[index++] = timeString.charCodeAt(i); + } + + for (i = 0; i < 4; i++) + { + uid[index++] = ALPHA_CHAR_CODES[Math.floor(Math.random() * 16)]; + } + + return String.fromCharCode.apply(null, uid); + } + + /** + * Converts a 128-bit UID encoded as a ByteArray to a String representation. + * The format matches that generated by createUID. If a suitable ByteArray + * is not provided, null is returned. + * + * @param ba ByteArray 16 bytes in length representing a 128-bit UID. + * + * @return String representation of the UID, or null if an invalid + * ByteArray is provided. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function fromByteArray(ba:ByteArray):String + { + if (ba != null && ba.length >= 16 && ba.bytesAvailable >= 16) + { + var chars:Array = new Array(36); + var index:uint = 0; + for (var i:uint = 0; i < 16; i++) + { + if (i == 4 || i == 6 || i == 8 || i == 10) + chars[index++] = 45; // Hyphen char code + + var b:int = ba.readByte(); + chars[index++] = ALPHA_CHAR_CODES[(b & 0xF0) >>> 4]; + chars[index++] = ALPHA_CHAR_CODES[(b & 0x0F)]; + } + return String.fromCharCode.apply(null, chars); + } + + return null; + } + + /** + * A utility method to check whether a String value represents a + * correctly formatted UID value. UID values are expected to be + * in the format generated by createUID(), implying that only + * capitalized A-F characters in addition to 0-9 digits are + * supported. + * + * @param uid The value to test whether it is formatted as a UID. + * + * @return Returns true if the value is formatted as a UID. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function isUID(uid:String):Boolean + { + if (uid != null && uid.length == 36) + { + for (var i:uint = 0; i < 36; i++) + { + var c:Number = uid.charCodeAt(i); + + // Check for correctly placed hyphens + if (i == 8 || i == 13 || i == 18 || i == 23) + { + if (c != 45) + { + return false; + } + } + // We allow capital alpha-numeric hex digits only + else if (c < 48 || c > 70 || (c > 57 && c < 65)) + { + return false; + } + } + + return true; + } + + return false; + } + + /** + * Converts a UID formatted String to a ByteArray. The UID must be in the + * format generated by createUID, otherwise null is returned. + * + * @param String representing a 128-bit UID + * + * @return ByteArray 16 bytes in length representing the 128-bits of the + * UID or null if the uid could not be converted. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function toByteArray(uid:String):ByteArray + { + if (isUID(uid)) + { + var result:ByteArray = new ByteArray(); + + for (var i:uint = 0; i < uid.length; i++) + { + var c:String = uid.charAt(i); + if (c == "-") + continue; + var h1:uint = getDigit(c); + i++; + var h2:uint = getDigit(uid.charAt(i)); + result.writeByte(((h1 << 4) | h2) & 0xFF); + } + result.position = 0; + return result; + } + + return null; + } + + /** + * Returns the UID (unique identifier) for the specified object. + * If the specified object doesn't have an UID + * then the method assigns one to it. + * If a map is specified this method will use the map + * to construct the UID. + * As a special case, if the item passed in is null, + * this method returns a null UID. + * + * @param item Object that we need to find the UID for. + * + * @return The UID that was either found or generated. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + *//* + public static function getUID(item:Object):String + { + var result:String = null; + + if (item == null) + return result; + + if (item is IUID) + { + result = IUID(item).uid; + if (result == null || result.length == 0) + { + result = createUID(); + IUID(item).uid = result; + } + } + else if ((item is IPropertyChangeNotifier) && + !(item is IUIComponent)) + { + result = IPropertyChangeNotifier(item).uid; + if (result == null || result.length == 0) + { + result = createUID(); + IPropertyChangeNotifier(item).uid = result; + } + } + else if (item is String) + { + return item as String; + } + else + { + try + { + // We don't create uids for XMLLists, but if + // there's only a single XML node, we'll extract it. + if (item is XMLList && item.length == 1) + item = item[0]; + + if (item is XML) + { + // XML nodes carry their UID on the + // function-that-is-a-hashtable they can carry around. + // To decorate an XML node with a UID, + // we need to first initialize it for notification. + // There is a potential performance issue here, + // since notification does have a cost, + // but most use cases for needing a UID on an XML node also + // require listening for change notifications on the node. + var xitem:XML = XML(item); + var nodeKind:String = xitem.nodeKind(); + if (nodeKind == "text" || nodeKind == "attribute") + return xitem.toString(); + + var notificationFunction:Function = xitem.notification(); + if (!(notificationFunction is Function)) + { + // The xml node hasn't already been initialized + // for notification, so do so now. + notificationFunction = + XMLNotifier.initializeXMLForNotification(); + xitem.setNotification(notificationFunction); + } + + // Generate a new uid for the node if necessary. + if (notificationFunction["uid"] == undefined) + result = notificationFunction["uid"] = createUID(); + + result = notificationFunction["uid"]; + } + else + { + if ("mx_internal_uid" in item) + return item.mx_internal_uid; + + if ("uid" in item) + return item.uid; + + result = uidDictionary[item]; + + if (!result) + { + result = createUID(); + try + { + item.mx_internal_uid = result; + } + catch(e:Error) + { + uidDictionary[item] = result; + } + } + } + } + catch(e:Error) + { + result = item.toString(); + } + } + + return result; + } +*/ + /** + * Returns the decimal representation of a hex digit. + * @private + */ + private static function getDigit(hex:String):uint + { + switch (hex) + { + case "A": + case "a": + return 10; + case "B": + case "b": + return 11; + case "C": + case "c": + return 12; + case "D": + case "d": + return 13; + case "E": + case "e": + return 14; + case "F": + case "f": + return 15; + default: + return new uint(hex); + } + } +} + +} diff --git a/src/mx/utils/XMLNotifier.as b/src/mx/utils/XMLNotifier.as new file mode 100644 index 00000000..58e74583 --- /dev/null +++ b/src/mx/utils/XMLNotifier.as @@ -0,0 +1,256 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2005-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.utils +{ + +import flash.utils.Dictionary; +import mx.core.mx_internal; +import mx.utils.IXMLNotifiable; + +use namespace mx_internal; + +/** + * Used for watching changes to XML and XMLList objects. + * Those objects are not EventDispatchers, so if multiple elements + * want to watch for changes they need to go through this mechanism. + * Call watchXML(), passing in the same notification + * function that you would pass to XML.notification. + * Use unwatchXML() to remove that notification. + * + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ +public class XMLNotifier +{ + include "../core/Version.as"; + + //-------------------------------------------------------------------------- + // + // Class variables + // + //-------------------------------------------------------------------------- + + /** + * @private + * XMLNotifier is a singleton. + */ + private static var instance:XMLNotifier; + + //-------------------------------------------------------------------------- + // + // Class methods + // + //-------------------------------------------------------------------------- + + /** + * Get the singleton instance of the XMLNotifier. + * + * @return The XMLNotifier object. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getInstance():XMLNotifier + { + if (!instance) + instance = new XMLNotifier(new XMLNotifierSingleton()); + + return instance; + } + + /** + * @private + * Decorates an XML node with a notification function + * that can fan out to multiple targets. + */ + mx_internal static function initializeXMLForNotification():Function + { + var notificationFunction:Function = function(currentTarget:Object, + ty:String, + tar:Object, + value:Object, + detail:Object):void + { + var xmlWatchers:Dictionary = arguments.callee.watched; + if (xmlWatchers != null) + { + for (var notifiable:Object in xmlWatchers) + { + IXMLNotifiable(notifiable).xmlNotification(currentTarget, ty, tar, value, detail); + } + } + } + + return notificationFunction; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * XMLNotifier is a singleton class, so you do not use + * the new operator to create multiple instances of it. + * Instead, call the static method XMLNotifider.getInstance() + * to get the sole instance of this class. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function XMLNotifier(x:XMLNotifierSingleton) + { + super(); + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Given an XML or XMLList, add the notification function + * to watch for changes. + * + * @param xml XML/XMLList object to watch. + * @param notification Function that needs to be called. + * @param optional UID for object + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function watchXML(xml:Object, notifiable:IXMLNotifiable, uid:String = null):void + { + if ((xml is XMLList) && xml.length() > 1) + { + for each(var item:Object in xml) + { + watchXML(item, notifiable, uid); + } + } + else + { + // An XMLList object behaves like XML when it contains one + // XML object. Casting to an XML object is necessary to + // access the notification() function. + var xmlItem:XML = XML(xml); + + // First make sure the xml node has a notification function. + var watcherFunction:Object = xmlItem.notification(); + + if (!(watcherFunction is Function)) + { + watcherFunction = initializeXMLForNotification(); + xmlItem.setNotification(watcherFunction as Function); + if (uid && watcherFunction["uid"] == null) + watcherFunction["uid"] = uid; + } + + // Watch lists are maintained on the notification function. + var xmlWatchers:Dictionary; + if (watcherFunction["watched"] == undefined) + watcherFunction["watched"] = xmlWatchers = new Dictionary(true); + else + xmlWatchers = watcherFunction["watched"]; + + xmlWatchers[notifiable] = true; + } + } + + /** + * Given an XML or XMLList, remove the specified notification function. + * + * @param xml XML/XMLList object to un-watch. + * @param notification Function notification function. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function unwatchXML(xml:Object, notifiable:IXMLNotifiable):void + { + if ((xml is XMLList) && xml.length() > 1) + { + for each(var item:Object in xml) + { + unwatchXML(item, notifiable); + } + } + else + { + // An XMLList object behaves like XML when it contains one + // XML object. Casting to an XML object is necessary to + // access the notification() function. + var xmlItem:XML = XML(xml); + + var watcherFunction:Object = xmlItem.notification(); + + if (!(watcherFunction is Function)) + return; + + var xmlWatchers:Dictionary; + + if (watcherFunction["watched"] != undefined) + { + xmlWatchers = watcherFunction["watched"]; + delete xmlWatchers[notifiable]; + } + } + } +} + +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Helper class: XMLNotifierSingleton +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * @private + */ +class XMLNotifierSingleton +{ + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * Constructor. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function XMLNotifierSingleton() + { + super(); + } +} diff --git a/src/reflex/animation/AnimationToken.as b/src/reflex/animation/AnimationToken.as new file mode 100644 index 00000000..0160c262 --- /dev/null +++ b/src/reflex/animation/AnimationToken.as @@ -0,0 +1,28 @@ +package reflex.animation +{ + public dynamic class AnimationToken + { + + public var x:Number; + public var y:Number; + public var z:Number; + + public var width:Number; + public var height:Number; + + public var rotationX:Number = 0; + public var rotationY:Number = 0; + public var rotationZ:Number = 0; + + //public var alpha:Number = 1.0; + + public function AnimationToken(x:Number, y:Number, width:Number, height:Number) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + } +} \ No newline at end of file diff --git a/src/reflex/animation/AnimationType.as b/src/reflex/animation/AnimationType.as new file mode 100644 index 00000000..a41273fe --- /dev/null +++ b/src/reflex/animation/AnimationType.as @@ -0,0 +1,20 @@ +package reflex.animation +{ + public class AnimationType + { + + static public const GENERIC:String = "generic"; + static public const LAYOUT:String = "layout"; + static public const RESIZE:String = "resize"; + static public const ADD:String = "add"; + static public const REMOVE:String = "remove"; + static public const REPLACE:String = "replace"; + static public const RESET:String = "reset"; + static public const SCROLL:String = "scroll"; + static public const DRAG:String = "drag"; + + public function AnimationType() + { + } + } +} \ No newline at end of file diff --git a/src/reflex/animation/Animator.as b/src/reflex/animation/Animator.as new file mode 100644 index 00000000..5ecef7b6 --- /dev/null +++ b/src/reflex/animation/Animator.as @@ -0,0 +1,54 @@ +package reflex.animation +{ + + import reflex.framework.IMeasurable; + + public class Animator implements IAnimator + { + public function Animator() + { + } + + public function createAnimationToken(renderer:Object):AnimationToken { + try { // todo: sometimes renderer is still data - but it shouldn't be - check container content validate() call + var token:AnimationToken = new AnimationToken(renderer.x, renderer.y, renderer.width, renderer.height); + } catch(error:Error) { + return new AnimationToken(0, 0, 0, 0); + } + //token.alpha = renderer.alpha; + //if(renderer is DisplayObject) { token.matrix = renderer.transform.matrix; } + return token; + } + + + public function begin():void {} + + public function moveItem(item:Object, token:AnimationToken, type:String):void + { + move(item, token); + } + + public function end():void {} + + private function move(item:Object, token:AnimationToken):void { + try { // todo: sometimes renderer is still data - but it shouldn't be - check container content validate() call + item.x = token.x; + item.y = token.y; + + if(item is IMeasurable) { + (item as IMeasurable).setSize(token.width, token.height); + } else { + item.width = token.width; + item.height = token.height; + } + //item.rotationZ = token.rotation; + //if(item is DisplayObject) { + //item.transform.matrix = token.matrix; + //} + } catch (e:Error) { + + } + } + + } +} \ No newline at end of file diff --git a/src/reflex/animation/IAnimator.as b/src/reflex/animation/IAnimator.as new file mode 100644 index 00000000..445da780 --- /dev/null +++ b/src/reflex/animation/IAnimator.as @@ -0,0 +1,36 @@ +package reflex.animation +{ + import flash.display.DisplayObject; + + import reflex.animation.AnimationToken; + + public interface IAnimator + { + + function createAnimationToken(renderer:Object):AnimationToken; + + /** + * attaches this animator to a given container + */ + //function attach(container:Object):void; + //function detach(container:Object):void; + + + /** + * Called before a series of addItem, moveItem, or removeItem calls. + */ + function begin():void; + + // we might break these up into seperate interfaces later + //function addItem(item:Object):void; + function moveItem(item:Object, token:AnimationToken, type:String):void; + //function adjustItem(item:Object, token:AnimationToken):void; // for scrolling, drag reactions etc. + //function removeItem(item:Object, callback:Function):void; + + /** + * Called after a series of addItem, moveItem, or removeItem calls. + */ + function end():void; + + } +} \ No newline at end of file diff --git a/src/reflex/behaviors/Behavior.as b/src/reflex/behaviors/Behavior.as index 8ce3df1c..4542745c 100644 --- a/src/reflex/behaviors/Behavior.as +++ b/src/reflex/behaviors/Behavior.as @@ -1,17 +1,14 @@ -package reflex.behaviors +package reflex.behaviors { - import flash.display.InteractiveObject; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; - import flight.binding.Bind; - import flight.utils.Type; - + import reflex.display.PropertyDispatcher; + import reflex.events.DataChangeEvent; import reflex.metadata.resolveBindings; + import reflex.metadata.resolveDataListeners; import reflex.metadata.resolveEventListeners; - import reflex.metadata.resolvePropertyListeners; - import reflex.skins.ISkinnable; /** * Behavior is a convenient base class for various behavior implementations. @@ -19,121 +16,31 @@ package reflex.behaviors * InteractiveObject. Behavior takes advantage of the skin of an ISkinnable * target by syncing skin parts and setting state. * - * Reflex component behaviors can be broken into 3 types - - * 1) a components single base behavior - core implementation with which the - * particular component would be useless without (eg ScrollBarBehavior) - * 2) a components addon behaviors - additional functionality specefic to - * the component (eg ReorderTabBehavior) - * 3) common addon behaviors - general solutions for all components, or all - * components of a type (eg TooltipBehavior) * @alpha */ - public class Behavior extends EventDispatcher implements IBehavior + public class Behavior extends PropertyDispatcher implements IBehavior { + + private var _target:IEventDispatcher; + /** * The object this behavior acts upon. */ - [Bindable] - public var target:IEventDispatcher; + [Bindable(event="targetChange")] + public function get target():IEventDispatcher { return _target; } + public function set target(value:IEventDispatcher):void { + notify("target", _target, _target = value); + } + - // TODO: add SkinParts with support for adding child behaviors to them - // registration of Behavior instances (via styling?) for instantiation - // skins ability to pull behavior data for state and other use - // skins also need data such as labels and images? (localization?) - // and dynamic data for it's content-area (component children) public function Behavior(target:IEventDispatcher = null) { - this.target = target; + super(this); + _target = target; // binding reflex.metadata.resolveBindings(this); - reflex.metadata.resolvePropertyListeners(this); + reflex.metadata.resolveDataListeners(this); reflex.metadata.resolveEventListeners(this); } - protected function getSkinPart(part:String):InteractiveObject - { - if (target is ISkinnable && ISkinnable(target).skin != null) { - return ISkinnable(target).skin.getSkinPart(part) as InteractiveObject; - } else if (part in target) { - return target[part] as InteractiveObject; - } else { - return null; - } - } - /* - protected function bindProperty(target:String, source:String):void - { - Bind.addBinding(this, target, this, source, true); - } - - protected function bindPropertyListener(target:String, listener:Function):void - { - Bind.addListener(this, listener, this, target); - } - - protected function bindEventListener(type:String, target:String, listener:Function, - useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = true):void - { - Bind.bindEventListener(type, listener, this, target, useCapture, priority, useWeakReference); - } - */ - /* - // parses [Binding(target="target.path")] metadata - public static function describeBindings(behavior:IBehavior):void - { - var desc:XMLList = Type.describeProperties(behavior, "Binding"); - - for each (var prop:XML in desc) { - var meta:XMLList = prop.metadata.(@name == "Binding"); - - // to support multiple Binding metadata tags on a single property - for each (var tag:XML in meta) { - var targ:String = ( tag.arg.(@key == "target").length() > 0 ) ? - tag.arg.(@key == "target").@value : - tag.arg.@value; - - Bind.addBinding(behavior, targ, behavior, prop.@name, true); - } - } - } - - // parses [PropertyListener(target="target.path)] metadata - public static function describePropertyListeners(behavior:IBehavior):void - { - var desc:XMLList = Type.describeMethods(behavior, "PropertyListener"); - - for each (var meth:XML in desc) { - var meta:XMLList = meth.metadata.(@name == "PropertyListener"); - - // to support multiple PropertyListener metadata tags on a single method - for each (var tag:XML in meta) { - var targ:String = ( tag.arg.(@key == "target").length() > 0 ) ? - tag.arg.(@key == "target").@value : - tag.arg.@value; - - Bind.addListener(behavior as IEventDispatcher, behavior[meth.@name], behavior, targ); - } - } - } - - // parses [EventListener(type="eventType", target="target.path")] metadata - public static function describeEventListeners(behavior:IBehavior):void - { - var desc:XMLList = Type.describeMethods(behavior, "EventListener"); - - for each (var meth:XML in desc) { - var meta:XMLList = meth.metadata.(@name == "EventListener"); - - // to support multiple EventListener metadata tags on a single method - for each (var tag:XML in meta) { - var type:String = ( tag.arg.(@key == "type").length() > 0 ) ? - tag.arg.(@key == "type").@value : - tag.arg.@value; - var targ:String = tag.arg.(@key == "target").@value; - - Bind.bindEventListener(type, behavior[meth.@name], behavior, targ); - } - } - } - */ } } \ No newline at end of file diff --git a/src/reflex/behaviors/ButtonBehavior.as b/src/reflex/behaviors/ButtonBehavior.as index 37ff2aa8..3c43cc6b 100644 --- a/src/reflex/behaviors/ButtonBehavior.as +++ b/src/reflex/behaviors/ButtonBehavior.as @@ -1,18 +1,17 @@ package reflex.behaviors { - import flash.display.DisplayObjectContainer; - import flash.display.InteractiveObject; + import flash.events.IEventDispatcher; import flash.events.MouseEvent; - import flight.binding.Bind; - - import reflex.events.ButtonEvent; - import reflex.skins.ISkinnable; + import reflex.skins.ISkin; + + //import reflex.events.ButtonEvent; [SkinState("up")] [SkinState("over")] [SkinState("down")] + [SkinState("disabled")] [Event(name="buttonDown", type="mx.events.FlexEvent")] @@ -21,56 +20,104 @@ package reflex.behaviors */ public class ButtonBehavior extends Behavior implements IBehavior { + public static const UP:String = "up"; public static const OVER:String = "over"; public static const DOWN:String = "down"; + public static const DISABLED:String = "disabled"; + + //public static const SELECTED_UP:String = "upAndSelected"; + //public static const SELECTED_OVER:String = "overAndSelected"; + //public static const SELECTED_DOWN:String = "downAndSelected"; + + + private var _currentState:String = UP; + private var _enabled:Boolean = true; + private var _selected:Boolean = false; + private var mouseState:String = UP; + private var _skin:ISkin; + private var _mouseEnabled:Boolean = true; - [Bindable] + [Bindable(event="currentStateChange")] [Binding(target="target.currentState")] - public var currentState:String = UP; + public function get currentState():String { return _currentState; } + public function set currentState(value:String):void { + notify("currentState", _currentState, _currentState = value); + } - public function ButtonBehavior(target:InteractiveObject = null) - { - super(target); + [Bindable(event="enabledChange")] + [Binding(target="target.enabled")] + public function get enabled():Boolean { return _enabled; } + public function set enabled(value:Boolean):void { + notify("enabled", _enabled, _enabled = value); + currentState = resolveState(mouseState); } - override public function set target(value:IEventDispatcher):void - { - if (value != null) { - if (value is DisplayObjectContainer) { - DisplayObjectContainer(value).mouseChildren = false; - } - ButtonEvent.initialize(value); - } - super.target = value; + [Bindable(event="selectedChange")] + [Binding(target="target.selected")] + public function get selected():Boolean { return _selected; } + public function set selected(value:Boolean):void { + notify("selected", _selected, _selected = value); + currentState = resolveState(mouseState); } - public function click():void - { - target.dispatchEvent(new MouseEvent(MouseEvent.CLICK)); + [Bindable(event="skinChange")] + [Binding(target="target.skin")] + public function get skin():ISkin { return _skin; } + public function set skin(value:ISkin):void { + notify("skin", _skin, _skin = value); + currentState = resolveState(mouseState); + } + + [Bindable(event="mouseEnabledChange")] + public function get mouseEnabled():Boolean { return _mouseEnabled; } + public function set mouseEnabled(value:Boolean):void { + notify("mouseEnabled", _mouseEnabled, _mouseEnabled = value); + } + + public function ButtonBehavior(target:IEventDispatcher = null) { + super(target); } // ====== Event Listeners ====== // - [EventListener(type="stateUp", target="target")] + [EventListener(event="rollOut", target="target.display")] + [EventListener(event="releaseOutside", target="target.display.stage")] public function onStateUp(event:MouseEvent):void { - currentState = UP; - event.updateAfterEvent(); + currentState = resolveState(mouseState = UP); } - [EventListener(type="stateOver", target="target")] + [EventListener(event="rollOver", target="target.display")] + [EventListener(event="mouseUp", target="target.display")] public function onStateOver(event:MouseEvent):void { - currentState = OVER; - event.updateAfterEvent(); + if(_mouseEnabled) { + currentState = resolveState(mouseState = OVER); + } } - [EventListener(type="stateDown", target="target")] + [EventListener(event="mouseDown", target="target.display")] public function onStateDown(event:MouseEvent):void { - currentState = DOWN; - event.updateAfterEvent(); + if(_mouseEnabled) { + currentState = resolveState(mouseState = DOWN); + } + } + + private function resolveState(mouseState:String):String { + if(_skin) { + if(!_enabled && _skin.hasState(DISABLED)) { + return DISABLED; + } + if(_selected && _skin.hasState(mouseState + "AndSelected")) { + return mouseState + "AndSelected"; + } + if(_skin.hasState(mouseState)) { + return mouseState; + } + } + return _selected ? mouseState + "AndSelected" : mouseState; } } diff --git a/src/reflex/behaviors/CompositeBehavior.as b/src/reflex/behaviors/CompositeBehavior.as deleted file mode 100644 index 8d1140b7..00000000 --- a/src/reflex/behaviors/CompositeBehavior.as +++ /dev/null @@ -1,258 +0,0 @@ -package reflex.behaviors -{ - import flash.display.InteractiveObject; - import flash.events.Event; - import flash.events.EventDispatcher; - import flash.events.IEventDispatcher; - import flash.utils.Dictionary; - import flash.utils.Proxy; - import flash.utils.flash_proxy; - - - use namespace flash_proxy; - - /** - * A dynamic proxy for compositing multiple behaviors. The CompositeBehavior - * may be iterated through just as an Array might be. - */ - public dynamic class CompositeBehavior extends Proxy implements IBehavior, IEventDispatcher - { - - private var _target:IEventDispatcher; - - private var behaviors:Array; - private var dictionary:Dictionary; - private var dispatcher:EventDispatcher; - - - /** - * Constructor. - * - * @param The target for this behavior map. - */ - public function CompositeBehavior(target:IEventDispatcher = null) - { - _target = target; - behaviors = []; - dictionary = new Dictionary(false); - } - - - [Bindable("targetChange")] - /** - * The object which the behaviors in this map will act upon. - */ - public function get target():IEventDispatcher - { - return _target; - } - - public function set target(value:IEventDispatcher):void - { - if (value == _target) return; - _target = value; - for each (var behavior:IBehavior in dictionary) { - behavior.target = value; - } - dispatch("targetChange"); - } - - - public function get length():uint { - return behaviors.length; - } - public function set length(value:uint):void { - // remove target from truncated items? - behaviors.length = value; - } - - //[ArrayElementType("reflex.core.IBehavior")] - public function add(item:Object):uint - { - if(item is IBehavior) { - (item as IBehavior).target = _target; - behaviors.push(item as IBehavior); - } else if(item is Array) { - for each (var behavior:IBehavior in item) { - behavior.target = _target; - behaviors.push(behavior); - } - } - return behaviors.length; - } - - public function clear():Array { - for each(var behavior:IBehavior in behaviors) { - behavior.target = null; - //delete dictionary[behavior]; - } - behaviors = []; - dictionary = new Dictionary(); // delete keys instead - return null; - } - - /** - * Proxy method to return a requested property. - */ - override flash_proxy function getProperty(name:*):* - { - return behaviors[name]; - } - - - /** - * Proxy method to set a property. Accepts a Class object for an - * IBehavior or an IBehavior object itself. - */ - override flash_proxy function setProperty(name:*, value:*):void - { - var oldBehavior:IBehavior = behaviors[name] as IBehavior; - if (value is Class) { // allow classes to be used to set behaviors - if (oldBehavior is value) return; - value = new value(); - } - - if (value == oldBehavior) return; - if (value == null) { - deleteProperty(name); - return; - } - - if (value != null && !(value is IBehavior) ) { - throw new ArgumentError("Only IBehavior objects can be added to behaviors."); - } - - if (oldBehavior) { - oldBehavior.target = null; - } - - var behavior:IBehavior = value as IBehavior; - - if (behavior) { - behavior.target = target; - } - behaviors[name] = behavior; - dispatch(name + "Change"); - } - - - /** - * Proxy method to delete the specified property. - */ - override flash_proxy function deleteProperty(name:*):Boolean - { - var behavior:IBehavior = behaviors[name] as IBehavior; - if (behavior) { - behavior.target = null; - } - return delete behaviors[name]; - } - - - /** - * Proxy method to check if the specified property exists. - */ - override flash_proxy function hasProperty(name:*):Boolean - { - return (name in behaviors); - } - - - /** - * to be determined - */ - override flash_proxy function callProperty(name:*, ...rest):* - { - return null; - } - - - private var props:Array; - - /** - * Proxy method allowing looping through the map. - */ - override flash_proxy function nextName(index:int):String - { - return (index - 1).toString(); - } - - /** - * Proxy method allowing looping through the map. - */ - override flash_proxy function nextValue(index:int):* - { - return behaviors[index-1]; - } - - /** - * Proxy method allowing looping through the map. - */ - override flash_proxy function nextNameIndex(index:int):int - { - if(index < behaviors.length) { - return index + 1; - } else { - return 0; - } - //return (index + 1) % (props.length + 1); // returns 0 when it hits length - } - - - - // ========== Dispatcher Methods ========== // - - public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void - { - if (dispatcher == null) { - dispatcher = new EventDispatcher(this); - } - - dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference); - } - - - public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void - { - if (dispatcher != null) { - dispatcher.removeEventListener(type, listener, useCapture); - } - } - - - public function dispatchEvent(event:Event):Boolean - { - if (dispatcher != null && dispatcher.hasEventListener(event.type)) { - return dispatcher.dispatchEvent(event); - } - return false; - } - - - public function hasEventListener(type:String):Boolean - { - if (dispatcher != null) { - return dispatcher.hasEventListener(type); - } - return false; - } - - - public function willTrigger(type:String):Boolean - { - if (dispatcher != null) { - return dispatcher.willTrigger(type); - } - return false; - } - - - protected function dispatch(type:String):Boolean - { - if (dispatcher != null && dispatcher.hasEventListener(type)) { - return dispatcher.dispatchEvent( new Event(type) ); - } - return false; - } - } -} \ No newline at end of file diff --git a/src/reflex/behaviors/CursorBehavior.as b/src/reflex/behaviors/CursorBehavior.as deleted file mode 100644 index 6de02985..00000000 --- a/src/reflex/behaviors/CursorBehavior.as +++ /dev/null @@ -1,35 +0,0 @@ -package reflex.behaviors -{ - import flash.display.DisplayObject; - import flash.display.InteractiveObject; - - import flight.binding.Bind; - - import reflex.cursors.Cursor; - - public class CursorBehavior extends Behavior - { - [Bindable] - public var cursor:Object; - - public function CursorBehavior(target:IEventDispatcher=null) - { - super(target); - Bind.addListener(this, cursorChange, this, "cursor"); - Bind.addListener(this, targetChange, this, "target"); - } - - protected function cursorChange(cursor:Object):void - { - if (target) { - Cursor.useCursor(target, cursor); - } - } - - protected function targetChange(oldTarget:InteractiveObject, newTarget:InteractiveObject):void - { - if (oldTarget && cursor) Cursor.useCursor(oldTarget, null); - if (newTarget && cursor) Cursor.useCursor(newTarget, cursor); - } - } -} \ No newline at end of file diff --git a/src/reflex/behaviors/DragBehavior.as b/src/reflex/behaviors/DragBehavior.as deleted file mode 100644 index 2e3a14c3..00000000 --- a/src/reflex/behaviors/DragBehavior.as +++ /dev/null @@ -1,54 +0,0 @@ -package reflex.behaviors -{ - import flash.display.DisplayObjectContainer; - import flash.display.InteractiveObject; - import flash.events.IEventDispatcher; - import flash.events.MouseEvent; - import flash.geom.Point; - - import reflex.events.ButtonEvent; - - public class DragBehavior extends Behavior - { - private var startMouse:Point; - private var startPosition:Point; - - public function DragBehavior(target:InteractiveObject=null) - { - super(target); - } - - override public function set target(value:IEventDispatcher):void - { - if (value != null) { - ButtonEvent.initialize(value); - } - super.target = value; - } - - - // ====== Event Listeners ====== // - - [EventListener(type="mouseDown", target="target")] - public function onMouseDown(event:MouseEvent):void - { - var item:InteractiveObject = event.currentTarget as InteractiveObject; - startMouse = new Point(event.stageX, event.stageY); - startPosition = new Point(item.x, item.y); - startPosition = item.parent.localToGlobal(startPosition); - } - - [EventListener(type="drag", target="target")] - public function onDrag(event:MouseEvent):void - { - var item:InteractiveObject = event.currentTarget as InteractiveObject; - var mouse:Point = new Point(event.stageX, event.stageY); - var delta:Point = mouse.subtract(startMouse); - var position:Point = startPosition.add(delta); - position = item.parent.globalToLocal(position); - item.x = position.x; - item.y = position.y; - event.updateAfterEvent(); - } - } -} \ No newline at end of file diff --git a/src/reflex/behaviors/DragListBehavior.as b/src/reflex/behaviors/DragListBehavior.as new file mode 100644 index 00000000..86bc2243 --- /dev/null +++ b/src/reflex/behaviors/DragListBehavior.as @@ -0,0 +1,74 @@ +package reflex.behaviors +{ + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + + import reflex.events.ContainerEvent; + import reflex.features.IDragManager; + import reflex.framework.IDataContainer; + + public class DragListBehavior extends Behavior + { + + private var _container:IDataContainer; + private var renderers:Array; + + [Inject] + public var drag:IDragManager; + + [Bindable(event="containerChange")] + [Binding(target="target.skin.container")] + public function get container():IDataContainer { return _container; } + public function set container(value:IDataContainer):void { + var renderer:IEventDispatcher; + if(_container) { + (_container as IEventDispatcher).removeEventListener(ContainerEvent.ITEM_ADDED, onItemAdded, false); + while(renderers.length > 0) { + renderer = renderers.pop() as IEventDispatcher; + renderer.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false); + } + } + notify("container", _container, _container = value); + if(_container) { + (_container as IEventDispatcher).addEventListener(ContainerEvent.ITEM_ADDED, onItemAdded, false, 0, true); + renderers = _container.getRenderers(); + + for each(renderer in renderers) { + renderer.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 0, true); + } + } + } + + [EventListener(event="itemAdded", target="container")] + public function onItemAdded(event:ContainerEvent):void + { + var renderer:IEventDispatcher = event.renderer as IEventDispatcher; + if(renderer) { renderer.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 0, true); } + } + + private var renderer:Object; + private function onMouseDown(event:MouseEvent):void { + renderer = event.currentTarget; + var stage:IEventDispatcher = renderer.display.stage; + stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove, false, 0, true); + stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 0, true); + } + + private function onMouseMove(event:MouseEvent):void { + + var item:* = _container.getItemForRenderer(renderer); + drag.doDrag(renderer, item); + var stage:IEventDispatcher = event.currentTarget as IEventDispatcher; + stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove, false); + //stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseMove, false); + } + + private function onMouseUp(event:MouseEvent):void { + var stage:IEventDispatcher = event.currentTarget as IEventDispatcher; + stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove, false); + stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseMove, false); + drag.endDrag(); + } + + } +} \ No newline at end of file diff --git a/src/reflex/behaviors/DragStepBehavior.as b/src/reflex/behaviors/DragStepBehavior.as index 364e4f05..b2a08645 100644 --- a/src/reflex/behaviors/DragStepBehavior.as +++ b/src/reflex/behaviors/DragStepBehavior.as @@ -1,23 +1,29 @@ package reflex.behaviors { - import flash.display.InteractiveObject; - import flash.display.MovieClip; + + import flash.events.Event; import flash.events.IEventDispatcher; import flash.events.MouseEvent; - import flight.binding.Bind; - import flight.position.IPosition; - import flight.position.Position; - - import reflex.events.ButtonEvent; + import reflex.data.IPosition; public class DragStepBehavior extends Behavior { - [Bindable] + + private var _position:IPosition; + + [Bindable(event="positionChange")] [Binding(target="target.position")] - public var position:IPosition = new Position(); // TODO: implement lazy instantiation of position + public function get position():IPosition { return _position; } + public function set position(value:IPosition):void { + notify("position", _position, _position = value); + } + + public var increment:Number = 1; + public var dragTolerance:Number = 3; + public var dragging:Boolean = false; - protected var startDragPosition:Number; + private var startDragPosition:Number; public function DragStepBehavior(target:IEventDispatcher = null) { @@ -32,19 +38,24 @@ package reflex.behaviors return; } - ButtonEvent.initialize(target); + //ButtonEvent.initialize(target); } - [EventListener(type="mouseDown", target="target")] + [EventListener(event="mouseDown", target="target")] public function onDragStart(event:MouseEvent):void { startDragPosition = position.value; } - [EventListener(type="drag", target="target")] - public function onDrag(event:ButtonEvent):void + [EventListener(event="drag", target="target")] + public function onDrag(event:Event):void { - position.value = startDragPosition + event.deltaX; + if (dragging) { + //position.value = Math.round( (startDragPosition + event.deltaX) / increment) * increment; + //} else if ( Math.abs(startDragPosition - event.deltaX) > dragTolerance) { + //position.value = Math.round( (startDragPosition + event.deltaX) / increment) * increment; + //dragging = true; + } } } diff --git a/src/reflex/behaviors/DropListBehavior.as b/src/reflex/behaviors/DropListBehavior.as new file mode 100644 index 00000000..3f6c8172 --- /dev/null +++ b/src/reflex/behaviors/DropListBehavior.as @@ -0,0 +1,39 @@ +package reflex.behaviors +{ + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + + import reflex.containers.IContainer; + import reflex.features.IDragManager; + + public class DropListBehavior extends Behavior + { + + private var _container:IContainer; + + [Inject] + public var drag:IDragManager; + + [Bindable(event="containerChange")] + [Binding(target="target.skin.container")] + public function get container():IContainer { return _container; } + public function set container(value:IContainer):void { + var renderer:IEventDispatcher; + if(_container) { + //(_container as IEventDispatcher).removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver, false); + } + notify("container", _container, _container = value); + if(_container) { + //(_container as IEventDispatcher).addEventListener(MouseEvent.MOUSE_OVER, onMouseOver, false, 0, true); + } + } + /* + private function onMouseOver(event:MouseEvent):void { + if(drag.isDragging()) { + //this.addEventListener(DragEvent.DRAG_DROP, onDragDrop, false, 0, true); + drag.acceptDragDrop(container); + } + } + */ + } +} \ No newline at end of file diff --git a/src/reflex/behaviors/IBehavior.as b/src/reflex/behaviors/IBehavior.as index f9be732a..6f74655b 100644 --- a/src/reflex/behaviors/IBehavior.as +++ b/src/reflex/behaviors/IBehavior.as @@ -1,6 +1,6 @@ package reflex.behaviors { - import flash.display.InteractiveObject; + import flash.events.IEventDispatcher; /** diff --git a/src/reflex/behaviors/IBehavioral.as b/src/reflex/behaviors/IBehavioral.as deleted file mode 100644 index b031b3ab..00000000 --- a/src/reflex/behaviors/IBehavioral.as +++ /dev/null @@ -1,9 +0,0 @@ -package reflex.behaviors -{ - - public interface IBehavioral - { - function get behaviors():CompositeBehavior; - function set behaviors(value:*):void; - } -} \ No newline at end of file diff --git a/src/reflex/behaviors/ListBehavior.as b/src/reflex/behaviors/ListBehavior.as index 83c921d2..441149e5 100644 --- a/src/reflex/behaviors/ListBehavior.as +++ b/src/reflex/behaviors/ListBehavior.as @@ -2,9 +2,6 @@ package reflex.behaviors { import flash.display.InteractiveObject; import flash.events.IEventDispatcher; - - import reflex.skins.ISkin; - import reflex.skins.ISkinnable; public class ListBehavior extends Behavior { @@ -28,8 +25,8 @@ package reflex.behaviors //var skin:ISkin = ISkinnable(target).skin; - hScrollBar = getSkinPart('hScrollBar'); - vScrollBar = getSkinPart('vScrollBar'); + //hScrollBar = getSkinPart('hScrollBar'); + //vScrollBar = getSkinPart('vScrollBar'); hSlideBehavior = new SlideBehavior(); vSlideBehavior = new SlideBehavior(); diff --git a/src/reflex/behaviors/ListSelectBehavior.as b/src/reflex/behaviors/ListSelectBehavior.as deleted file mode 100644 index 5e32565c..00000000 --- a/src/reflex/behaviors/ListSelectBehavior.as +++ /dev/null @@ -1,9 +0,0 @@ -package reflex.behaviors -{ - public class ListSelectBehavior - { - public function ListSelectBehavior() - { - } - } -} \ No newline at end of file diff --git a/src/reflex/behaviors/ListSelectionBehavior.as b/src/reflex/behaviors/ListSelectionBehavior.as new file mode 100644 index 00000000..f9757a9a --- /dev/null +++ b/src/reflex/behaviors/ListSelectionBehavior.as @@ -0,0 +1,162 @@ +package reflex.behaviors +{ + + import flash.display.DisplayObjectContainer; + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + import flash.ui.Mouse; + + import mx.collections.IList; + import mx.core.IDataRenderer; + import mx.events.CollectionEvent; + import mx.events.CollectionEventKind; + + import reflex.containers.IContainer; + import reflex.data.ISelection; + + public class ListSelectionBehavior extends Behavior + { + + private var _container:IContainer; + private var _selection:ISelection; + + protected var _renderers:IList; + /* + [Bindable(event="containerChanged")] + [Binding(target="target.skin.container")] + public function get container():IContainer { return _container; } + public function set container(value:IContainer):void { + addContainer(value); + notify("container", _container, _container = value); + } + */ + + [Bindable(event="renderersChanged")] + [Binding(target="target.skin.container.renderers")] + public function get renderers():IList { return _renderers; } + public function set renderers(value:IList):void { + notify("renderers", _renderers, _renderers = value); + addItems(_renderers ? _renderers.toArray() : [], 0); + _renderers.addEventListener(CollectionEvent.COLLECTION_CHANGE, onChildrenChange, false, 0, true); + } + + [Bindable(event="selectionChanged")] + [Binding(target="target.selection")] + public function get selection():ISelection{ return _selection; } + public function set selection(value:ISelection):void { + notify("selection", _selection, _selection= value); + } + + public function ListSelectionBehavior(target:IEventDispatcher=null):void { + super(target); + } + + private var _cancel:Boolean; + public function cancel():void { + _cancel = true; + //_container.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 0, true); + } + /* + //[EventListener(event="added", target="container")] + public function onRendererAdded(event:Event):void { + var renderer:Object = event.target; + if(renderer.parent == container || renderer.parent.parent == container) { + addRenderer(renderer as IEventDispatcher) + } + } + + public function onRendererRemoved(event:Event):void { + var renderer:IEventDispatcher= event.target as IEventDispatcher + removeRenderer(renderer) + } + + private function addContainer(container:IContainer):void { + if(container) { + var renderers:Array = container.g + var length:int = container.numChildren; + for(var i:int = 0; i < length; i++) { + var renderer:IEventDispatcher = container.getChildAt(i); + addRenderer(renderer); + } + container.addEventListener(Event.ADDED, onRendererAdded); + } + } + + private function addRenderer(renderer:IEventDispatcher):void { + renderer.addEventListener(MouseEvent.CLICK, onRendererClick, false, 0, true); + renderers.push(renderer); + } + + private function removeRenderer(renderer:IEventDispatcher):void { + renderer.removeEventListener(MouseEvent.CLICK, onRendererClick, false); + var index:int = renderers.indexOf(renderer); + renderers.splice(index, 1); + } + */ + + private function onChildrenChange(event:CollectionEvent):void + { + switch (event.kind) { + case CollectionEventKind.ADD : + addItems(event.items, event.location); + break; + case CollectionEventKind.REMOVE : + removeItems(event.items, event.oldLocation); + break; + /*case CollectionEventKind.REPLACE : + animationType = AnimationType.REPLACE; + helper.removeChild(display, event.items[1]); + //addChildAt(event.items[0], loc); + break;*/ + case CollectionEventKind.RESET : + default: + removeItems(event.items, 0); + addItems(_renderers ? _renderers.toArray() : [], 0); + break; + } + } + + private function addItems(items:Array, index:int):void { + var length:int = items ? items.length : 0; + for(var i:int = 0; i < length; i++) { + var item:IEventDispatcher = items[i]; + item.addEventListener(MouseEvent.CLICK, onRendererClick, false, 0, true); + } + } + + private function removeItems(items:Array, index:int):void { + for each (var item:IEventDispatcher in items) { + item.removeEventListener(MouseEvent.CLICK, onRendererClick, false); + } + } + + private function onRendererClick(event:MouseEvent):void { + if(!_cancel) { + // just assuming IDataRenderer for now. this will need to be smarter later + var renderer:Object = event.currentTarget; + var item:Object = (event.currentTarget is IDataRenderer) ? (event.currentTarget as IDataRenderer).data : event.currentTarget; + if(event.ctrlKey) { + _selection.selectedItems.addItem(item); + } else { + _selection.selectedItem = item; + } + + // making wild assumptions about the existence of selected properties here + for each(var r:Object in renderers) { + //r.selected = false; + } + //(renderer as Object).selected = !(renderer as Object).selected; + } + _cancel = false; + } + + private function onMouseUp(event:MouseEvent):void { + //_container.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp, false); + //if(!_container.hitTestPoint(_container.stage.mouseX, _container.stage.mouseY)) { + // _cancel = false; + //} + } + + } +} \ No newline at end of file diff --git a/src/reflex/behaviors/MovieClipSkinBehavior.as b/src/reflex/behaviors/MovieClipSkinBehavior.as deleted file mode 100644 index 50fe70b4..00000000 --- a/src/reflex/behaviors/MovieClipSkinBehavior.as +++ /dev/null @@ -1,67 +0,0 @@ -package reflex.behaviors -{ - import flash.display.DisplayObject; - import flash.display.FrameLabel; - import flash.display.MovieClip; - import flash.events.IEventDispatcher; - - [SkinState("up")] - [SkinState("over")] - [SkinState("down")] - - public class MovieClipSkinBehavior extends Behavior - { - - public static const UP:String = "up"; - public static const OVER:String = "over"; - public static const DOWN:String = "down"; - - [Bindable] - [Binding(target="target.currentState")] - public var currentState:String = UP; - - [Bindable] - [Binding(target="target.skin")] - public var movieclip:MovieClip; - - public function MovieClipSkinBehavior(target:IEventDispatcher=null) - { - super(target); - } - - // ====== Event Listeners ====== // - - [PropertyListener(target="movieclip")] - public function onMovieClip(object:MovieClip):void - { - // trace(object); - } - - [PropertyListener(target="currentState")] - public function onState(object:String):void - { - if(movieclip) { - gotoState(movieclip, object); - } - } - - // we'll update this for animated/play animations later - private function gotoState(clip:MovieClip, state:String):void { - var frames:Array = clip.currentLabels; - for each(var label:FrameLabel in frames) { - if(label.name == state) { - clip.gotoAndStop(label.frame); - } - } - - var length:int = clip.numChildren; // recurse (for now) - for(var i:int = 0; i < length; i++) { - var child:DisplayObject = clip.getChildAt(i); - if(child is MovieClip) { - gotoState(child as MovieClip, state); - } - } - } - - } -} \ No newline at end of file diff --git a/src/reflex/behaviors/PopoutBehavior.as b/src/reflex/behaviors/PopoutBehavior.as deleted file mode 100644 index e7ead8fd..00000000 --- a/src/reflex/behaviors/PopoutBehavior.as +++ /dev/null @@ -1,9 +0,0 @@ -package reflex.behaviors -{ - public class PopoutBehavior - { - public function PopoutBehavior() - { - } - } -} \ No newline at end of file diff --git a/src/reflex/behaviors/ScrollerBehavior.as b/src/reflex/behaviors/ScrollerBehavior.as new file mode 100644 index 00000000..970701d6 --- /dev/null +++ b/src/reflex/behaviors/ScrollerBehavior.as @@ -0,0 +1,63 @@ +package reflex.behaviors +{ + import flash.display.DisplayObject; + import flash.events.Event; + import flash.events.IEventDispatcher; + + import reflex.data.IPosition; + + public class ScrollerBehavior extends Behavior + { + + private var _horizontalPosition:IPosition; + private var _verticalPosition:IPosition; + private var _container:DisplayObject; + + [Bindable(event="horizontalPositionChange")] + [Binding(target="target.horizontalPosition")] + public function get horizontalPosition():IPosition { return _horizontalPosition; } + public function set horizontalPosition(value:IPosition):void { + notify("horizontalPosition", _horizontalPosition, _horizontalPosition = value); + } + + [Bindable(event="verticalPositionChange")] + [Binding(target="target.verticalPosition")] + public function get verticalPosition():IPosition { return _verticalPosition; } + public function set verticalPosition(value:IPosition):void { + notify("verticalPosition", _verticalPosition, _verticalPosition = value); + } + + [Bindable(event="containerChange")] + [Binding(target="target.skin.container")] + public function get container():DisplayObject { return _container; } + public function set container(value:DisplayObject):void { + notify("container", _container, _container = value); + } + + public function ScrollerBehavior(target:IEventDispatcher=null) + { + super(target); + } + + [EventListener(event="widthChange", target="container")] + [EventListener(event="valueChange", target="horizontalPosition")] + [EventListener(event="minimumChange", target="horizontalPosition")] + [EventListener(event="maximumChange", target="horizontalPosition")] + public function onHorizontalChange(event:Event):void { + var percent:Number = (horizontalPosition.value-horizontalPosition.minimum)/(horizontalPosition.maximum-horizontalPosition.minimum); + var potential:Number = (_container as Object).measured.width-(target as Object).width; + _container.x = Math.round(potential*percent*-1); + } + + [EventListener(event="heightChange", target="container")] + [EventListener(event="valueChange", target="verticalPosition")] + [EventListener(event="minimumChange", target="verticalPosition")] + [EventListener(event="maximumChange", target="verticalPosition")] + public function onVerticalChange(event:Event):void { + var percent:Number = (verticalPosition.value-verticalPosition.minimum)/(verticalPosition.maximum-verticalPosition.minimum); + var potential:Number = (_container as Object).measured.height-(target as Object).height; + _container.y = Math.round(potential*percent*-1); + } + + } +} \ No newline at end of file diff --git a/src/reflex/behaviors/SelectBehavior.as b/src/reflex/behaviors/SelectBehavior.as new file mode 100644 index 00000000..e8c5bf93 --- /dev/null +++ b/src/reflex/behaviors/SelectBehavior.as @@ -0,0 +1,38 @@ +package reflex.behaviors +{ + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + + + /** + * @description Adds selection toggling functionality to the target. When clicked, the target's selected property will be flipped. + * @alpha + */ + public class SelectBehavior extends Behavior implements IBehavior + { + + private var _selected:Boolean; + + [Bindable(event="selectedChange")] + [Binding(target="target.selected")] + public function get selected():Boolean { return _selected; } + public function set selected(value:Boolean):void { + notify("selected", _selected, _selected = value); + } + + public function SelectBehavior(target:IEventDispatcher= null) + { + super(target); + } + + /** + * @private + */ + [EventListener(event="click", target="target")] + public function onClick(event:MouseEvent):void + { + selected = !selected; + } + + } +} \ No newline at end of file diff --git a/src/reflex/behaviors/SelectableBehavior.as b/src/reflex/behaviors/SelectableBehavior.as deleted file mode 100644 index 86b48248..00000000 --- a/src/reflex/behaviors/SelectableBehavior.as +++ /dev/null @@ -1,32 +0,0 @@ -package reflex.behaviors -{ - import flash.display.InteractiveObject; - import flash.events.MouseEvent; - - /** - * @description Adds selection toggling functionality to the target. When clicked, the target's selected property will be flipped. - * @alpha - */ - public class SelectableBehavior extends Behavior implements IBehavior - { - [Bindable] - [Binding(target="target.selected")] - public var selected:Boolean; - - public function SelectableBehavior(target:InteractiveObject = null) - { - super(target); - } - - /** - * @private - */ - [EventListener(type="click", target="target")] - public function onClick(event:MouseEvent):void - { - selected = !selected; - event.updateAfterEvent(); - } - - } -} \ No newline at end of file diff --git a/src/reflex/behaviors/SlideBehavior.as b/src/reflex/behaviors/SlideBehavior.as index 8371ce94..6150d0b0 100644 --- a/src/reflex/behaviors/SlideBehavior.as +++ b/src/reflex/behaviors/SlideBehavior.as @@ -1,175 +1,185 @@ package reflex.behaviors { - import flash.display.InteractiveObject; + import flash.events.Event; import flash.events.IEventDispatcher; - import flash.geom.Point; - - import flight.position.IPosition; - import flight.position.Position; + import flash.events.MouseEvent; - import reflex.events.ButtonEvent; - import reflex.measurement.resolveHeight; + import reflex.data.IPagingPosition; + import reflex.data.IPosition; public class SlideBehavior extends Behavior// extends StepBehavior { - [Bindable] + static public const HORIZONTAL:String = "horizontal"; + static public const VERTICAL:String = "vertical"; + + private var _track:Object; + private var _thumb:Object; + private var _progress:Object; + private var _position:IPosition; + private var _mouseEnabled:Boolean = true; + + public var page:Boolean = false; + public var layoutChildren:Boolean = true; + public var direction:String = HORIZONTAL; + + [Bindable(event="trackChange")] [Binding(target="target.skin.track")] - public var track:InteractiveObject; + public function get track():Object { return _track; } + public function set track(value:Object):void { + notify("track", _track, _track = value); + } - [Bindable] + [Bindable(event="thumbChange")] [Binding(target="target.skin.thumb")] - public var thumb:InteractiveObject; + public function get thumb():Object { return _thumb; } + public function set thumb(value:Object):void { + notify("thumb", _thumb, _thumb = value); + //(value as IEventDispatcher).addEventListener(MouseEvent.MOUSE_DOWN, onThumbDown, false, 0, true); + } - [Bindable] - [Binding(target="target.horizontal")] - public var horizontal:Boolean = false; + [Bindable(event="progressChange")] + [Binding(target="target.skin.progress")] + public function get progress():Object { return _progress; } + public function set progress(value:Object):void { + notify("progress", _progress, _progress = value); + } - [Bindable] + [Bindable(event="positionChange")] [Binding(target="target.position")] - public var position:IPosition = new Position(); // TODO: implement lazy instantiation of position + public function get position():IPosition { return _position; } + public function set position(value:IPosition):void { + notify("position", _position, _position = value); + } - private var _percent:Number = 0; - private var dragPercent:Number; - private var dragPoint:Number; - private var dragging:Boolean; - private var forwardPress:Boolean; + [Bindable(event="mouseEnabledChange")] + public function get mouseEnabled():Boolean { return _mouseEnabled; } + public function set mouseEnabled(value:Boolean):void { + notify("mouseEnabled", _mouseEnabled, _mouseEnabled = value); + } - public function SlideBehavior(target:InteractiveObject = null) - { + public function SlideBehavior(target:IEventDispatcher = null, direction:String = "horizontal", page:Boolean = false) { super(target); + this.direction = direction; + this.page = page; + //updateUILayout(); } - [Bindable(event="percentChange")] - public function get percent():Number - { - return _percent; - } + // behavior - override public function set target(value:IEventDispatcher):void + [EventListener(event="click", target="track")] + public function onTrackPress(event:MouseEvent):void { - super.target = value; - - if (target == null) { - return; - } - - track = getSkinPart("track"); - thumb = getSkinPart("thumb"); - if(track) { ButtonEvent.initialize(track); } - if(thumb) { ButtonEvent.initialize(thumb); } - - if (track && track.width > track.height) { - horizontal = true; - } - if(track && thumb) { - updatePosition(); + if(_mouseEnabled) { + var t:Object = target as Object; + if(page) { + if(direction == HORIZONTAL) { + pagePosition(event.localX - track.x, track.width); + } else if(direction == VERTICAL) { + pagePosition(event.localY - track.y, track.height); + } + } else { + if(direction == HORIZONTAL) { + jumpToPosition(event.localX - track.x, track.width); + } else if(direction == VERTICAL) { + jumpToPosition(event.localY - track.y, track.height); + } + } + updateUIPosition(); } } - [PropertyListener(target="position.percent")] - public function onPosition(percent:Number):void + + [EventListener(event="mouseDown", target="thumb")] + public function onThumbDown(event:MouseEvent):void { - if (thumb == null || track == null) { - return; - } - - if (!dragging) { - _percent = position.percent; - updatePosition(); - dispatchEvent(new Event("percentChange")); + if(_mouseEnabled) { + (target as Object).display.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true); + (target as Object).display.stage.addEventListener(MouseEvent.MOUSE_UP, onThumbUp, false, 0, true); + (target as Object).display.stage.addEventListener(Event.MOUSE_LEAVE, onThumbUp, false, 0, true); + //(target as Object).stage.addEventListener(MouseEvent.MOUSE_UP, onThumbUp, false, 0, true); + //(target as Object).stage.addEventListener(Event.MOUSE_LEAVE, onThumbUp, false, 0, true); } } - [EventListener(type="press", target="track")] - public function onTrackPress(event:ButtonEvent):void - { - var size:Number = horizontal ? track.width : track.height; - forwardPress = (horizontal ? track.parent.mouseX - track.x : track.parent.mouseY - track.y) > (size * position.percent); - - if (forwardPress) { - position.skipForward(); - } else { - position.skipBackward(); - } - event.updateAfterEvent(); + private function onThumbUp(event:MouseEvent):void { + (target as Object).display.stage.removeEventListener(Event.ENTER_FRAME, onEnterFrame, false); + (target as Object).display.stage.removeEventListener(MouseEvent.MOUSE_UP, onThumbUp, false); + (target as Object).display.stage.removeEventListener(Event.MOUSE_LEAVE, onThumbUp, false); + //(target as Object).stage.removeEventListener(MouseEvent.MOUSE_UP, onThumbUp, false); + //(target as Object).stage.removeEventListener(Event.MOUSE_LEAVE, onThumbUp, false); } - [EventListener(type="hold", target="track")] - public function onTrackHold(event:ButtonEvent):void - { - var size:Number = horizontal ? track.width : track.height; - var forwardHold:Boolean = (horizontal ? track.parent.mouseX - track.x : track.parent.mouseY - track.y) > (size * position.percent); - - if (forwardPress != forwardHold) { - return; - } - - if (forwardPress) { - position.skipForward(); - } else { - position.skipBackward(); + private function onEnterFrame(event:Event):void { + var percent:Number = 0; + var t:Object = (target as Object).display; + if(direction == HORIZONTAL) { + percent = (t.mouseX - track.x)/track.width; + } else if(direction == VERTICAL) { + percent = (t.mouseY - track.y)/track.height; } - event.updateAfterEvent(); + var value:Number = (position.maximum-position.minimum)*percent + position.minimum; + position.value = Math.max(position.minimum, Math.min(position.maximum, value)); + updateUIPosition(); } - [EventListener(type="press", target="thumb")] - public function onThumbPress(event:ButtonEvent):void - { - dragging = true; - dragPoint = horizontal ? thumb.parent.mouseX : thumb.parent.mouseY; - dragPercent = _percent; + + // skinpart positioning + + //[CommitProperties(properties="position.min, position.max, position.position, position.pageSize")] + [EventListener(event="valueChange", target="position")] + public function onPositionChange(event:Event):void { + updateUIPosition(); } - [EventListener(type="drag", target="thumb")] - public function onThumbDrag(event:ButtonEvent):void - { - var mousePoint:Number = horizontal ? thumb.parent.mouseX : thumb.parent.mouseY; - var size:Number = horizontal ? track.width - thumb.width : track.height - thumb.height; - var delta:Number = (mousePoint - dragPoint) / size; - _percent = dragPercent + delta; - _percent = _percent <= 0 ? 0 : (_percent >= 1 ? 1 : _percent); - position.percent = _percent; - updatePosition(); - dispatchEvent(new Event("percentChange")); - - event.updateAfterEvent(); - } - - [EventListener(type="release", target="thumb")] - [EventListener(type="releaseOutside", target="thumb")] - public function onThumbRelease(event:ButtonEvent):void - { - dragging = false; + [EventListener(event="heightChange", target="target")] + public function onSizeChange(event:Event):void { + updateUILayout(); } - [PropertyListener(target="target.width")] - [PropertyListener(target="target.height")] - public function onResize(size:Number):void - { - updatePosition(); + private function pagePosition(v:Number, length:Number):void { + var scroll:IPagingPosition = position as IPagingPosition; + if(scroll) { + var center:Number = length/2; + if(v < center) { + scroll.value -= scroll.pageSize; + } else { + scroll.value += scroll.pageSize; + } + } } + private function jumpToPosition(v:Number, length:Number):void { + var percent:Number = v/length; + position.value = (position.maximum-position.minimum)*percent + position.minimum; + } - protected function updatePosition():void - { - if(track && thumb) { - var p:Point = new Point(); - - if (horizontal) { - p.x = (track.width - thumb.width) * _percent + track.x; - p = thumb.parent.globalToLocal( track.parent.localToGlobal(p) ); - thumb.x = Math.round(p.x); - } else { - var trackHeight:Number = reflex.measurement.resolveHeight(track); - var thumbHeight:Number = reflex.measurement.resolveHeight(thumb); - p.y = (trackHeight - thumbHeight) * _percent + track.y; - p = thumb.parent.globalToLocal( track.parent.localToGlobal(p) ); - thumb.y = Math.round(p.y); + private function updateUIPosition():void { + var percent:Number = (position.value-position.minimum)/(position.maximum-position.minimum); + if(target) { + if(direction == HORIZONTAL) { + if(thumb && track) { thumb.x = track.x + (track.width-thumb.width) * percent; } + if(progress) { progress.width = track.width * percent; } + } else if(direction == VERTICAL) { + if(thumb && track) { thumb.y = track.y + (track.height-thumb.height) * percent; } + if(progress) { progress.height = track.height * percent; } } } } + private function updateUILayout():void { + if(target) { + if(direction == HORIZONTAL) { + var h2:Number = (target as Object).height/2; + if(track) { track.y = h2 - track.height/2; } + if(thumb) { thumb.y = h2 - thumb.height/2; } + } else { + var w2:Number = (target as Object).width/2; + if(track) { track.x = w2 - track.width/2; } + if(thumb) { thumb.x = w2 - thumb.width/2; } + } + } + } } } diff --git a/src/reflex/behaviors/StepBehavior.as b/src/reflex/behaviors/StepBehavior.as index bda00449..2ac960c5 100644 --- a/src/reflex/behaviors/StepBehavior.as +++ b/src/reflex/behaviors/StepBehavior.as @@ -1,78 +1,58 @@ package reflex.behaviors { - import flash.display.InteractiveObject; - import flash.display.MovieClip; - import flash.events.IEventDispatcher; - import flight.binding.Bind; - import flight.position.IPosition; - import flight.position.Position; + import flash.events.IEventDispatcher; - import reflex.events.ButtonEvent; + import reflex.data.ISteppingPosition; + + import flash.events.Event; + + //import reflex.events.ButtonEvent; public class StepBehavior extends Behavior { - public var fwdBehavior:ButtonBehavior; - public var bwdBehavior:ButtonBehavior; - [Bindable] - public var fwdBtn:InteractiveObject; - [Bindable] - public var bwdBtn:InteractiveObject; + private var _incrementButton:Object; + private var _decrementButton:Object; + private var _position:ISteppingPosition; [Bindable] - [Binding(target="target.position")] - public var position:IPosition = new Position(); // TODO: implement lazy instantiation of position - - public function StepBehavior(target:InteractiveObject = null) - { - super(target); + [Binding(target="target.skin.incrementButton")] + public function get incrementButton():Object { return _incrementButton; } + public function set incrementButton(value:Object):void { + notify("incrementButton", _incrementButton, _incrementButton = value); } - override public function set target(value:IEventDispatcher):void - { - super.target = value; - - if (target == null) { - return; - } - - fwdBtn = getSkinPart("fwdBtn"); - bwdBtn = getSkinPart("bwdBtn"); - fwdBehavior = new ButtonBehavior(fwdBtn); - bwdBehavior = new ButtonBehavior(bwdBtn); - if (fwdBtn is MovieClip) { - Bind.addListener(this, onFwdStateChange, fwdBehavior, "state"); - } - if (bwdBtn is MovieClip) { - Bind.addListener(this, onBwdStateChange, bwdBehavior, "state"); - } + [Bindable] + [Binding(target="target.skin.decrementButton")] + public function get decrementButton():Object { return _decrementButton; } + public function set decrementButton(value:Object):void { + notify("decrementButton", _decrementButton, _decrementButton = value); } - [EventListener(type="press", target="fwdBtn")] - [EventListener(type="hold", target="fwdBtn")] - public function onFwdPress(event:ButtonEvent):void - { - position.forward(); - event.updateAfterEvent(); + [Bindable] + [Binding(target="target.position")] + public function get position():ISteppingPosition { return _position; } + public function set position(value:ISteppingPosition):void { + notify("position", _position, _position = value); } - [EventListener(type="press", target="bwdBtn")] - [EventListener(type="hold", target="bwdBtn")] - public function onBwdPress(event:ButtonEvent):void + public function StepBehavior(target:IEventDispatcher = null) { - position.backward(); - event.updateAfterEvent(); + super(target); } - protected function onFwdStateChange(state:String):void + [EventListener(event="click", target="incrementButton")] + public function onFwdPress(event:Event):void { - MovieClip(fwdBtn).gotoAndStop(state); + _position.value += _position.stepSize; } - protected function onBwdStateChange(state:String):void + [EventListener(event="click", target="decrementButton")] + public function onBwdPress(event:Event):void { - MovieClip(bwdBtn).gotoAndStop(state); + _position.value -= _position.stepSize; } + } } \ No newline at end of file diff --git a/src/reflex/behaviors/TextAreaBehavior.as b/src/reflex/behaviors/TextAreaBehavior.as new file mode 100644 index 00000000..f744d344 --- /dev/null +++ b/src/reflex/behaviors/TextAreaBehavior.as @@ -0,0 +1,73 @@ +package reflex.behaviors +{ + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.text.TextField; + + import reflex.data.IPosition; + + public class TextAreaBehavior extends Behavior + { + + private var _horizontalPosition:IPosition; + private var _verticalPosition:IPosition; + private var _textField:TextField; + + [Bindable(event="horizontalPositionChange")] + [Binding(target="target.horizontalPosition")] + public function get horizontalPosition():IPosition { return _horizontalPosition; } + public function set horizontalPosition(value:IPosition):void { + notify("horizontalPosition", _horizontalPosition, _horizontalPosition = value); + } + + [Bindable(event="verticalPositionChange")] + [Binding(target="target.verticalPosition")] + public function get verticalPosition():IPosition { return _verticalPosition; } + public function set verticalPosition(value:IPosition):void { + notify("verticalPosition", _verticalPosition, _verticalPosition = value); + } + + [Bindable(event="textFieldChange")] + [Binding(target="target.skin.textField")] + public function get textField():TextField { return _textField; } + public function set textField(value:TextField):void { + notify("textField", _textField, _textField = value); + } + + public function TextAreaBehavior(target:IEventDispatcher=null) + { + super(target); + } + + [EventListener(event="valueChange", target="horizontalPosition")] + [EventListener(event="minimumChange", target="horizontalPosition")] + [EventListener(event="maximumChange", target="horizontalPosition")] + public function onHorizontalChange(event:Event):void { + var percent:Number = (horizontalPosition.value-horizontalPosition.minimum)/(horizontalPosition.maximum-horizontalPosition.minimum); + //_container.x = (_container.width-(target as Object).width)*percent*-1; + _textField.scrollH = (_textField.maxScrollV)*percent; + } + + [EventListener(event="valueChange", target="verticalPosition")] + [EventListener(event="minimumChange", target="verticalPosition")] + [EventListener(event="maximumChange", target="verticalPosition")] + public function onVerticalChange(event:Event):void { + var percent:Number = (verticalPosition.value-verticalPosition.minimum)/(verticalPosition.maximum-verticalPosition.minimum); + //_container.y = (_container.height-(target as Object).height)*percent*-1; + _textField.scrollV = (_textField.maxScrollV)*percent; + } + + [EventListener(event="scroll", target="textField")] + [EventListener(event="change", target="textField")] + [EventListener(event="widthChange", target="target")] + [EventListener(event="heightChange", target="target")] + public function onScrollChange(event:Event):void { + if(_textField && _textField.maxScrollV > 1) { + var percent:Number = _textField.scrollV/_textField.maxScrollV; + var range:Number = _verticalPosition.maximum - _verticalPosition.minimum; + _verticalPosition.value = _verticalPosition.minimum + range*percent; + } + } + + } +} \ No newline at end of file diff --git a/src/reflex/behaviors/TouchScrollBehavior.as b/src/reflex/behaviors/TouchScrollBehavior.as new file mode 100755 index 00000000..b330e743 --- /dev/null +++ b/src/reflex/behaviors/TouchScrollBehavior.as @@ -0,0 +1,112 @@ +package reflex.behaviors +{ + import flash.display.DisplayObject; + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + import flash.geom.Matrix3D; + import flash.geom.Point; + import flash.geom.Transform; + import flash.geom.Vector3D; + + import reflex.behaviors.Behavior; + import reflex.data.IPosition; + import reflex.measurement.resolveHeight; + import reflex.measurement.resolveWidth; + + //[HostComponent("flash.display.DisplayObject")] + public class TouchScrollBehavior extends Behavior + { + + [Binding(target="target")] + public var hostComponent:DisplayObject; + + private var point:Point; + private var speed:Point; + private var drag:Number = 0.95; + + [Bindable] + public var round:Boolean = false; + + [Bindable] + [Binding(target="target.verticalPosition")] + public var verticalPosition:IPosition; + + [Bindable] + [Binding(target="target.horizontalPosition")] + public var horizontalPosition:IPosition; + + [Bindable] + [Binding(target="target.skin.container")] + public var container:Object; + + public var horizontalControl:Boolean = true; + public var verticalControl:Boolean = true; + + public function TouchScrollBehavior(target:IEventDispatcher=null, horizontalControl:Boolean = true, verticalControl:Boolean = true) + { + super(target); + this.horizontalControl = horizontalControl; + this.verticalControl = verticalControl; + point = new Point(); + speed = new Point(); + } + + [EventListener(event="mouseDown", target="target")] + public function onMouseDown(event:MouseEvent):void { + point.x = hostComponent.mouseX; + point.y = hostComponent.mouseY; + hostComponent.removeEventListener(Event.ENTER_FRAME, animation_enterFrameHandler, false); + hostComponent.addEventListener(Event.ENTER_FRAME, scrolling_enterFrameHandler, false, 0, true); + hostComponent.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 0, true); + drag = 0.95; + } + + private function onMouseUp(event:MouseEvent):void { + speed.x = point.x - hostComponent.mouseX; + speed.y = point.y - hostComponent.mouseY; + point.x = hostComponent.mouseX; + point.y = hostComponent.mouseY; + //(target as DisplayObject).removeEventListener(MouseEvent.MOUSE_MOVE, scrolling_enterFrameHandler, false); + hostComponent.removeEventListener(Event.ENTER_FRAME, scrolling_enterFrameHandler, false); + hostComponent.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp, false); + hostComponent.addEventListener(Event.ENTER_FRAME, animation_enterFrameHandler, false, 0, true); + } + + private function scrolling_enterFrameHandler(event:Event):void { + speed.x = point.x - hostComponent.mouseX; + speed.y = point.y - hostComponent.mouseY; + point.x = hostComponent.mouseX; + point.y = hostComponent.mouseY; + updatePositions(); + } + + private function animation_enterFrameHandler(event:Event):void { + + updatePositions(); + speed.x = speed.x * drag; + speed.y = speed.y * drag; + drag -= 0.005; + if(drag < 0.1) { + hostComponent.removeEventListener(Event.ENTER_FRAME, animation_enterFrameHandler, false); + } + + } + + private function updatePositions():void { + if(horizontalPosition && horizontalControl) { + var percentX:Number = speed.x/(container.measured.width-hostComponent.height); + var potentialX:Number = percentX*(horizontalPosition.maximum - horizontalPosition.minimum); + var px:Number = horizontalPosition.value + potentialX; + horizontalPosition.value = Math.max(horizontalPosition.minimum, Math.min(horizontalPosition.maximum, px)); + } + if(verticalPosition && verticalControl) { + var percentY:Number = speed.y/(container.measured.height-hostComponent.height); + var potentialY:Number = percentY*(verticalPosition.maximum - verticalPosition.minimum); + var py:Number = verticalPosition.value + potentialY; + verticalPosition.value = Math.max(verticalPosition.minimum, Math.min(verticalPosition.maximum, py)); + } + } + + } +} \ No newline at end of file diff --git a/src/reflex/binding/Bind.as b/src/reflex/binding/Bind.as new file mode 100644 index 00000000..10885571 --- /dev/null +++ b/src/reflex/binding/Bind.as @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2009-2010 the original author or authors + * + * Permission is hereby granted to use, modify, and distribute this file + * in accordance with the terms of the license agreement accompanying it. + */ + +package reflex.binding +{ + import flash.events.IEventDispatcher; + import flash.utils.Dictionary; + + import mx.core.IMXMLObject; + + /** + * The Bind class stands as the primary API for data binding, whether through Bind instances + * that represent a target property bound to some data source, or through it's static methods + * that allow global binding access. Bind's can be instantiated via ActionScript or MXML and + * simplify management of a single binding, allowing target and source to be changed anytime. + */ + public class Bind implements IMXMLObject + { + public var twoWay:Boolean; + public var target:String; + public var source:String; + + public function initialized(document:Object, id:String):void + { + if (target && source) { + Bind.addBinding(document, target, document, source, twoWay); + } + } + + + protected static var items:Dictionary = new Dictionary(true); + protected static var listeners:Dictionary = new Dictionary(true); + protected static var eventListeners:Dictionary = new Dictionary(true); + protected static var pool:Array = []; + protected static var holdInMemory:Function = updateListener; + + /** + * Global utility method binding a target end point to a data source. Once a target is bound + * to the source, their values will synchronize immediately and on each subsequent change on + * the source. When enabling a two-way bind the source will also update to match the target. + * + * @param target A reference to the initial object in the target end point, the + * recipient of binding updates. + * @param targetPath A property or dot-separated property chain to be resolved in the + * target end point. + * @param source A reference to the initial object in the source end point, the + * initiator of binding updates. + * @param sourcePath A property or dot-separated property chain to be resolved in the + * source end point. + * @param twoWay When enabled, two-way binding updates both target and + * source upon changes to either. + * + * @return Considered successful if the binding has not already been established. + */ + public static function addBinding(target:Object, targetPath:String, source:Object, sourcePath:String, twoWay:Boolean = false):Binding + { + return createBinding(target, targetPath, source, sourcePath, twoWay); + } + + /** + * Global utility method destroying bindings made via addBinding. Once the binding + * is no longer in effect the properties will not be synchronized. However, source and target + * values will not be changed by removing the binding, so they will still match initially. + * + * @param target A reference to the initial object in the target end point, the + * recipient of binding updates. + * @param targetPath A property or dot-separated property chain to be resolved in the + * target end point. + * @param source A reference to the initial object in the source end point, the + * initiator of binding updates. + * @param sourcePath A property or dot-separated property chain to be resolved in the + * source end point. + * + * @return Considered successful if the specified binding was available. + */ + public static function removeBinding(target:Object, targetPath:Object, source:Object, sourcePath:Object, twoWay:Boolean = false):Boolean + { + var binding:Binding = getBinding(target, targetPath, source, sourcePath, twoWay); + if (binding) { + releaseBinding(binding); + return true; + } + return false; + } + + /** + * Removes all bindings, listeners, and bind event listeners that + * reference target object. + */ + public static function removeAllBindings(target:Object):void + { + // bindings and listeners + var bindings:Array = items[target]; + if (!bindings) { + return; + } + for each (var binding:Binding in bindings) { + releaseBinding(binding); + } + delete items[target]; + + // event listeners + delete eventListeners[target]; + } + + /** + * Global utility method binding an event listener to a data source. Once a listener is bound + * to the source, it will receive notification when source values change. + * + * @param listener An event listener object to be registered with the source binding. + * @param source A reference to the initial object in the source end point, the + * initiator of binding updates. + * @param sourcePath A property or dot-separated property chain to be resolved in the + * source end point. + * @param useWeakReference Determines whether the reference to the listener is strong or weak. + * A weak reference (the default) allows your listener to be garbage- + * collected. A strong reference does not. + */ + public static function addListener(target:IEventDispatcher, listener:Function, source:Object, sourcePath:String):Binding + { + if (target) { + target.addEventListener("_", listener); + } + return createBinding(target, listener, source, sourcePath); + } + + /** + * Global utility method removing an event listener from a source binding. Once the binding + * is no longer in effect the listener will not receive notification when source values change. + * + * @param listener An event listener object to be removed from the source binding. + * @param source A reference to the initial object in the source end point, the + * initiator of binding updates. + * @param sourcePath A property or dot-separated property chain to be resolved in the + * source end point. + */ + public static function removeListener(target:IEventDispatcher, listener:Function, source:Object, sourcePath:String):Boolean + { + return removeBinding(target, listener, source, sourcePath); + } + + // NOTE: weakReference specifies how the listener is added to the endpoint dispatcher, but the listener is held in memory by the binding + // TODO: refactor to allow the listener to be weakReference + public static function bindEventListener(type:String, listener:Function, source:Object, sourcePath:String, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = true):Binding + { + var binding:Binding = createBinding(source, updateListener, source, sourcePath); + + // store the listener and other properties in a dictionary + var sourceListeners:Dictionary = eventListeners[source]; + if (!sourceListeners) { + eventListeners[source] = sourceListeners = new Dictionary(true); + } + + sourceListeners[binding] = [type, listener, useCapture, priority, useWeakReference]; + + // force update now since the first time couldn't register + if (binding.value) { + updateListener(binding, null, null, binding.value); + } + return binding; + } + + public static function unbindEventListener(type:String, listener:Function, source:Object, sourcePath:String, useCapture:Boolean = false):Boolean + { + var binding:Binding = getBinding(source, updateListener, source, sourcePath); + + if (!binding) { + return false; + } + + var sourceListeners:Dictionary = eventListeners[source]; + if (!sourceListeners) { + return false; + } + + var args:Array = sourceListeners[binding]; + if (!args) { + return false; + } + + updateListener(binding, null, binding.value, null); + delete sourceListeners[binding]; + releaseBinding(binding); + return true; + } + + protected static function createBinding(target:Object, targetPath:Object, source:Object, sourcePath:Object, twoWay:Boolean = false):Binding + { + var binding:Binding = newBinding(target, targetPath, source, sourcePath, twoWay); + + // store the binding in relation to the source and target so that + // they can be manually released. + var bindings:Array = items[target]; + if (!bindings) { + items[target] = bindings = []; + } + bindings.push(binding); + + // if target and source are the same no need to store it twice + if (target != source) { + bindings = items[source]; + if (!bindings) { + items[source] = bindings = []; + } + } + return binding; + } + + protected static function getBinding(target:Object, targetPath:Object, source:Object, sourcePath:Object, twoWay:Boolean = false):Binding + { + var bindings:Array = items[target] || items[source]; + if (bindings) { + for each (var binding:Binding in bindings) { + if (binding.target == target && binding.source == source + && pathEquals(binding.targetPath, targetPath) && pathEquals(binding.sourcePath, sourcePath) + && binding.twoWay == twoWay) { + return binding; + } + } + } + return null; + } + + protected static function updateListener(binding:Binding, item:Object, oldValue:*, newValue:*):void + { + if (!(binding.source in eventListeners && binding in eventListeners[binding.source])) { + return; // the first update happens before storing in listeners dict + } + + var args:Array = eventListeners[binding.source][binding]; + var dispatcher:IEventDispatcher; + + dispatcher = oldValue as IEventDispatcher; + if (dispatcher != null) { + dispatcher.removeEventListener(args[0], args[1], args[2]); + } + + dispatcher = newValue as IEventDispatcher; + if (dispatcher != null) { + dispatcher.addEventListener(args[0], args[1], args[2], args[3], args[4]); + } + } + + + protected static function releaseBinding(binding:Binding):void + { + var index:int; + var bindings:Array; + var source:Object, target:Object; + source = binding.source; + target = binding.target; + + // remove from source + if (source) { + bindings = items[source]; + if (bindings) { + index = bindings.indexOf(binding); + if (index != -1) { + bindings.splice(index, 1); + } + } + } + + // remove from target + if (target && target != source) { + bindings = items[target]; + if (bindings) { + index = bindings.indexOf(binding); + if (index != -1) { + bindings.splice(index, 1); + } + } + } + + binding.release(); + pool.push(binding); + } + + protected static function newBinding(target:Object, targetPath:Object, source:Object, sourcePath:Object, twoWay:Boolean = false):Binding + { + if (pool.length) { + var binding:Binding = pool.pop(); + binding.reset(target, targetPath, source, sourcePath, twoWay); + return binding; + } else { + return new Binding(target, targetPath, source, sourcePath, twoWay); + } + } + + protected static function pathEquals(path1:Object, path2:Object):Boolean + { + // if they are not the same type + if (path1.constructor != path2.constructor) return false; + + if (path1 is Array) { + var arr1:Array = path1 as Array, arr2:Array = path2 as Array; + if (arr1.length != arr2.length) return false; + var len:int = arr1.length; + for (var i:int = 0; i < len; i++) { + if (!pathEquals(arr1[i], arr2[i])) return false; + } + return true; + } else if (path1 is String) { + return path1 == path2; + } else if (path1 is Function) { + return path1 == path2; + } else { + for (var j:String in path1) { + if (path1[j] != path2[j]) return false; + } + return true; + } + } + } +} diff --git a/src/reflex/binding/Binding.as b/src/reflex/binding/Binding.as new file mode 100644 index 00000000..72792e7f --- /dev/null +++ b/src/reflex/binding/Binding.as @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2009-2010 the original author or authors + * + * Permission is hereby granted to use, modify, and distribute this file + * in accordance with the terms of the license agreement accompanying it. + */ + +package reflex.binding +{ + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.utils.Dictionary; + + import mx.events.PropertyChangeEvent; + + import reflex.metadata.Type; + import reflex.metadata.getClassName; + import reflex.metadata.getType; + + [ExcludeClass] + + /** + * Binding will bind two properties together. They can be shallow or deep + * and one way or two way. + */ + public class Binding + { + protected static const SOURCE:String = "source"; + protected static const TARGET:String = "target"; + + protected var sourceIndices:Dictionary = new Dictionary(true); + protected var targetIndices:Dictionary = new Dictionary(true); + + protected var _sourcePath:Array; + protected var _targetPath:Array; + + protected var _twoWay:Boolean; + protected var _value:*; + + protected var sourceResolved:Boolean; + protected var targetResolved:Boolean; + protected var updating:Boolean; + + protected var changeFunction:Function = propertyChange; + + /** + * + */ + public function Binding(target:Object = null, targetPath:Object = null, source:Object = null, sourcePath:Object = null, twoWay:Boolean = false) + { + if (target && targetPath && source && sourcePath) { + reset(target, targetPath, source, sourcePath, twoWay); + } + } + + /** + * Indicates whether this binding has dropped either the source or the + * target. If either were dropped out of memory the binding is no longer + * valid and shoudl be released appropriatly. + */ + public function get isInvalid():Boolean + { + var i:Object; + for (i in sourceIndices) { + for (i in targetIndices) return false; + } + return true; + } + + public function get target():Object + { + for (var i:Object in targetIndices) { + if (targetIndices[i] == 0) return i; + } + return null; + } + + public function get source():Object + { + for (var i:Object in sourceIndices) { + if (sourceIndices[i] == 0) return i; + } + return null; + } + + public function get targetPath():String + { + return _targetPath.join("."); + } + + public function get sourcePath():String + { + return _sourcePath.join("."); + } + + public function get twoWay():Boolean + { + return _twoWay; + } + + public function get value():* + { + return _value; + } + + /** + * + */ + public function release():void + { + unbindPath(SOURCE, 0); + unbindPath(TARGET, 0); + _sourcePath = null; + _targetPath = null; + _twoWay = false; + sourceResolved = false; + targetResolved = false; + _value = undefined; + } + + public function reset(target:Object, targetPath:Object, source:Object, sourcePath:Object, twoWay:Boolean):void + { + release(); + _twoWay = twoWay; + + _sourcePath = makePath(sourcePath); + _targetPath = makePath(targetPath); + + bindPath(TARGET, target, 0); + update(SOURCE, source, 0); + } + + protected function update(type:String, item:Object, pathIndex:int = 0):void + { + var indices:Dictionary = this[type + "Indices"]; + var path:Array = this["_" + type + "Path"]; + + var oldValue:* = _value; + _value = bindPath(type, item, pathIndex); // udpate full path + + if (oldValue === _value) return; + + updating = true; + var otherType:String = (type == SOURCE ? TARGET : SOURCE); + var resolved:Boolean = this[otherType + "Resolved"]; + if (resolved) { + var otherPath:Array = this["_" + otherType + "Path"]; + var otherItem:Object = getItem(otherType, otherPath.length - 1); // item + path.length + if (otherItem) { + var prop:Object = otherPath[otherPath.length - 1]; + setProp(otherItem, prop, oldValue, _value); + } + } + updating = false; + } + + /** + * Bind a path up starting from the given index. + */ + protected function bindPath(type:String, item:Object, pathIndex:int):* + { + var indices:Dictionary = this[type + "Indices"]; + var path:Array = this["_" + type + "Path"]; + var onPropertyChange:Function = this[type + "ChangeHandler"]; + + unbindPath(type, pathIndex); + + var resolved:Boolean; + var prop:Object; + var len:int = path.length; + for (pathIndex; pathIndex < len; pathIndex++) { + + if (item == null) { + break; + } + + indices[item] = pathIndex; + + prop = path[pathIndex]; + var propName:String = getPropName(prop); + + if (propName && _twoWay || type == SOURCE || pathIndex < len-1) { + var changeEvents:Array = getBindingEvents(item, propName); + if (changeEvents == null || changeEvents[0] == "observable") { + //PropertyChange.addObserver(item, propName, null, propertyChange); + } else if (item is IEventDispatcher) { + for each (var changeEvent:String in changeEvents) { + IEventDispatcher(item).addEventListener(changeEvent, onPropertyChange, false, 100, true); + } + } else { + trace("Warning: Property '" + propName + "' is not bindable in " + getClassName(item) + "."); + } + } + + try { + item = getProp(item, prop); + } catch (e:Error) { + item = null; + } + } + + // if we've reached the end of the chain successfully (item + path - 1) + this[type + "Resolved"] = resolved = Boolean(pathIndex == len || item != null); + if (!resolved) { + return null; + } + + return item; + } + + /** + * Removes all event listeners from a certain point (index) in the path + * on up. A pathIndex of 0 will remove all listeners for the given type. + */ + protected function unbindPath(type:String, pathIndex:int):void + { + var indices:Dictionary = this[type + "Indices"]; + var path:Array = this["_" + type + "Path"]; + var onPropertyChange:Function = this[type + "ChangeHandler"]; + + for (var item:* in indices) { + var index:int = indices[item]; + if (index < pathIndex) { + continue; + } + + var propName:String = getPropName(path[index]); + var changeEvents:Array = getBindingEvents(item, propName); + if (changeEvents == null || changeEvents[0] == "observable") { + //PropertyChange.removeObserver(item, propName, propertyChange); + } else if (item is IEventDispatcher) { + for each (var changeEvent:String in changeEvents) { + IEventDispatcher(item).removeEventListener(changeEvent, onPropertyChange); + } + } + delete indices[item]; + } + } + + + protected function getItem(type:String, pathIndex:int = 0):Object + { + var indices:Dictionary = this[type + "Indices"]; + + for (var item:* in indices) { + if (indices[item] != pathIndex) { + continue; + } + return item; + } + + return null; + } + + protected function makePath(value:Object):Array + { + if (value is String) { + return String(value).split("."); + } else if (value is Array) { + return value as Array; + } else if (value != null) { + return [value]; + } + return []; + } + + protected function getPropName(prop:Object):String + { + if (prop is String) return prop as String; + if ("name" in prop) return prop.name; + return null; + } + + protected function getProp(item:Object, prop:Object):* + { + var func:Function; + + if ((func = prop as Function) + || ("getter" in prop && (func = prop.getter as Function)) + || (prop in item && (func = item[prop] as Function))) + { + var params:Array = [item]; + params.length = Math.min(func.length, 1); + return func.apply(null, params); + } + + try { + return item[prop]; + } catch (e:Error){} + + return null; + } + + protected function setProp(item:Object, prop:Object, oldValue:*, value:*):void + { + var func:Function; + + if ((func = prop as Function) + || ("getter" in prop && (func = prop.getter as Function)) + || (prop in item && (func = item[prop] as Function))) + { + var params:Array = [_value, oldValue, item, this]; + params.length = Math.min(func.length, 4); + func.apply(null, params.reverse()); + return; + } + + try { + item[prop] = value; + } catch (e:Error){} + } + + public function propertyChange(target:Object, name:String, oldValue:*, newValue:*):void + { + if (updating) return; + var pathIndex:int, prop:String; + + if (target in sourceIndices) { + pathIndex = sourceIndices[target]; + prop = _sourcePath[pathIndex]; + if (prop == name) { + update(SOURCE, target[prop], pathIndex + 1); + return; // done + } + } + + if (target in targetIndices) { + pathIndex = targetIndices[target]; + prop = _targetPath[pathIndex]; + + if (prop == name) { + if (_twoWay) { + update(TARGET, target[prop], pathIndex + 1); + } else { + bindPath(TARGET, target[prop], pathIndex + 1); + if (sourceResolved && targetResolved) { + target = getItem(TARGET, _targetPath.length - 1); + prop = _targetPath[_targetPath.length - 1]; + try { + target[prop] = _value; + } catch (e:Error) {} + } + } + } + } + } + + protected function sourceChangeHandler(event:Event):void + { + if (updating) return; + var source:Object = event.target; + var pathIndex:int = sourceIndices[source]; + var prop:String = _sourcePath[pathIndex]; + if (event is PropertyChangeEvent && PropertyChangeEvent(event).property != prop) { + return; + } + + update(SOURCE, source[prop], pathIndex + 1); + } + + protected function targetChangeHandler(event:Event):void + { + if (updating) return; + var target:Object = event.target; + var pathIndex:int = targetIndices[target]; + var prop:String = _targetPath[pathIndex]; + if (event is PropertyChangeEvent && PropertyChangeEvent(event).property != prop) { + return; + } + + if (_twoWay) { + update(TARGET, target[prop], pathIndex + 1); + } else { + bindPath(TARGET, target[prop], pathIndex + 1); + if (sourceResolved && targetResolved) { + target = getItem(TARGET, _targetPath.length - 1); + prop = _targetPath[_targetPath.length - 1]; + try { + target[prop] = _value; + } catch (e:Error) {} + } + } + } + + + protected static var descCache:Dictionary = new Dictionary(); + + protected static function getBindingEvents(target:Object, property:String):Array + { + var bindings:Object = describeBindings(target); + return bindings[property]; + } + + protected static function describeBindings(value:Object):Object + { + if ( !(value is Class) ) { + value = getType(value); + } + + if (descCache[value] == null) { + var desc:XMLList = Type.describeProperties(value, "Bindable"); + var bindings:Object = descCache[value] = {}; + + for each (var prop:XML in desc) { + var property:String = prop.@name; + var changeEvents:Array = []; + var bindable:XMLList = prop.metadata.(@name == "Bindable"); + + for each (var bind:XML in bindable) { + if (bind.arg.(@value == "observable").length()) { + changeEvents.length = 0; + changeEvents.push("observable"); + break; + } else { + var changeEvent:String = (bind.arg.(@key == "event").length() != 0) ? + bind.arg.(@key == "event").@value : + changeEvent = bind.arg.@value; + + changeEvents.push(changeEvent); + } + } + + bindings[property] = changeEvents; + } + } + + return descCache[value]; + } + + } +} diff --git a/src/reflex/collections/SimpleCollection.as b/src/reflex/collections/SimpleCollection.as new file mode 100644 index 00000000..2631658c --- /dev/null +++ b/src/reflex/collections/SimpleCollection.as @@ -0,0 +1,96 @@ +package reflex.collections +{ + import flash.events.EventDispatcher; + + import mx.collections.IList; + import mx.events.CollectionEvent; + import mx.events.CollectionEventKind; + + [DefaultProperty("source")] + public class SimpleCollection extends EventDispatcher implements IList + { + + private var _source:Array; + + public function get source():Array { return _source; } + public function set source(value:Array):void { + _source = value; + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE, false, false, CollectionEventKind.RESET, -1, -1, (_source == null) ? null : _source.concat()); + dispatchEvent(event); + } + + public function SimpleCollection(source:Array = null) + { + super(); + if (source == null) { + source = []; + } + _source = source; + } + + [Bindable(event="collectionChange")] + public function get length():int + { + return _source.length; + } + + public function addItem(item:Object):void + { + var index:uint = _source.push(item)-1; + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE, false, false, CollectionEventKind.ADD, index, -1, [item]); + dispatchEvent(event); + } + + public function addItemAt(item:Object, index:int):void + { + _source.splice(index, 0, item); + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE, false, false, CollectionEventKind.ADD, index, -1, [item]); + dispatchEvent(event); + } + + public function getItemAt(index:int, prefetch:int=0):Object + { + // todo: implement prefetch + return _source[index]; + } + + public function getItemIndex(item:Object):int + { + return _source.indexOf(item); + } + + public function itemUpdated(item:Object, property:Object=null, oldValue:Object=null, newValue:Object=null):void + { + // bah + } + + public function removeAll():void + { + var items:Array = _source; + _source = []; + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE, false, false, CollectionEventKind.RESET, -1, -1, items); + dispatchEvent(event); + } + + public function removeItemAt(index:int):Object + { + var item:Object = _source.splice(index, 1); + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE, false, false, CollectionEventKind.REMOVE, index, index, item as Array); + dispatchEvent(event); + return item[0]; + } + + public function setItemAt(item:Object, index:int):Object + { + var oldItem:Object = _source.splice(index, 1, item); + var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE, false, false, CollectionEventKind.REPLACE, index, index, [item]); + dispatchEvent(event); + return oldItem; + } + + public function toArray():Array + { + return _source.concat(); + } + } +} \ No newline at end of file diff --git a/src/reflex/collections/convertIndexedObjectToArray.as b/src/reflex/collections/convertIndexedObjectToArray.as new file mode 100644 index 00000000..59030823 --- /dev/null +++ b/src/reflex/collections/convertIndexedObjectToArray.as @@ -0,0 +1,14 @@ +package reflex.collections { + + public function convertIndexedObjectToArray(indexedObject:Object):Array { + var array:Array = []; + + var indexedObjectLength:int = indexedObject.length; + + for (var i:int = 0; i < indexedObjectLength; i++ ) { + array[i] = indexedObject[i]; + } + + return array; + } +} \ No newline at end of file diff --git a/src/reflex/collections/convertToIList.as b/src/reflex/collections/convertToIList.as new file mode 100644 index 00000000..8041c93a --- /dev/null +++ b/src/reflex/collections/convertToIList.as @@ -0,0 +1,21 @@ +package reflex.collections { + import mx.collections.IList; + + public function convertToIList(value:*):IList { + var ilist:IList; + + if (value == null) { + ilist = null; + } else if (value is IList) { + ilist = value as IList; + } else if (value is Array) { + ilist = new SimpleCollection(value); + } else if (reflex.collections.isVector(value)) { + ilist = new SimpleCollection(reflex.collections.convertIndexedObjectToArray(value)); + } else { + ilist = new SimpleCollection([value]); + } + + return ilist; + } +} \ No newline at end of file diff --git a/src/reflex/collections/isVector.as b/src/reflex/collections/isVector.as new file mode 100644 index 00000000..71f9eb33 --- /dev/null +++ b/src/reflex/collections/isVector.as @@ -0,0 +1,14 @@ +package reflex.collections { + + public function isVector(value:*):Boolean { + var valueIsVector:Boolean = true; + + try { + var vector:Vector. = Vector.(value); + } catch (e:Error) { + valueIsVector = false; + } + + return valueIsVector; + } +} \ No newline at end of file diff --git a/src/reflex/components/Application.as b/src/reflex/components/Application.as deleted file mode 100644 index 0c52c0dd..00000000 --- a/src/reflex/components/Application.as +++ /dev/null @@ -1,51 +0,0 @@ -package reflex.components -{ - import flash.display.StageAlign; - import flash.display.StageQuality; - import flash.display.StageScaleMode; - import flash.events.Event; - import flash.geom.PerspectiveProjection; - import flash.geom.Point; - import flash.ui.ContextMenu; - - import reflex.display.Container; - import reflex.events.InvalidationEvent; - import reflex.layout.LayoutWrapper; - - //[Frame(factoryClass="reflex.tools.flashbuilder.ReflexApplicationLoader")] - [SWF(widthPercent="100%", heightPercent="100%", frameRate="30")] - - /** - * @alpha - */ - public class Application extends Container - { - - public function Application() - { - if (stage == null) { - return; - } - - //contextMenu = new ContextMenu(); - //contextMenu.hideBuiltInItems(); - stage.scaleMode = StageScaleMode.NO_SCALE; - stage.align = StageAlign.TOP_LEFT; - stage.addEventListener(Event.RESIZE, onStageResize, false, 0, true); - - onStageResize(null); - } - - private function onStageResize(event:Event):void - { - width = stage.stageWidth; - height = stage.stageHeight; - //setSize(stage.stageWidth, stage.stageHeight); - /*var perspective:PerspectiveProjection = new PerspectiveProjection(); - perspective.projectionCenter = new Point(stage.stageWidth/2, stage.stageHeight/2); - stage.transform.perspectiveProjection = perspective;*/ - //InvalidationEvent.invalidate(this, MEASURE); - //InvalidationEvent.invalidate(this, LAYOUT); - } - } -} \ No newline at end of file diff --git a/src/reflex/components/Button.as b/src/reflex/components/Button.as index 86845e8a..08e7d9f3 100644 --- a/src/reflex/components/Button.as +++ b/src/reflex/components/Button.as @@ -1,28 +1,48 @@ package reflex.components { - import reflex.behaviors.ButtonBehavior; - import reflex.behaviors.MovieClipSkinBehavior; - import reflex.behaviors.SelectableBehavior; - import reflex.skins.ButtonSkin; - /** - * @alpha - **/ + import flash.events.Event; + + import reflex.binding.Bind; + public class Button extends Component { - [Bindable] - public var label:String; - [Bindable] - public var selected:Boolean; + private var _icon:Object; + private var _label:String; + private var _selected:Boolean; + + [Bindable(event="iconChange")] + public function get icon():Object { return _icon; } + public function set icon(value:Object):void { + notify("icon", _icon, _icon = value); + } + + [Bindable(event="labelChange")] + [Inspectable(name="Label", type=String, defaultValue="Label")] + public function get label():String { return _label; } + public function set label(value:String):void { + notify("label", _label, _label = value); + } - public function Button() + [Bindable(event="selectedChange")] + [Inspectable(name="Selected", type=Boolean, defaultValue=false)] + public function get selected():Boolean {return _selected; } + public function set selected(value:Boolean):void { + notify("selected", _selected, _selected = value); + } + + public function Button(label:String = "") { super(); - behaviors.button = new ButtonBehavior(this); - behaviors.selectable = new SelectableBehavior(this); - behaviors.movieclip = new MovieClipSkinBehavior(this); - skin = new ButtonGraphic(); + this.label = label; + } + + override protected function initialize():void { + super.initialize(); + Bind.addBinding(this, "skin.iconDisplay.source", this, "icon", false); + Bind.addBinding(this, "skin.labelDisplay.text", this, "label", false); + Bind.addBinding(this, "skin.currentState", this, "currentState", false); } } diff --git a/src/reflex/components/CheckBox.as b/src/reflex/components/CheckBox.as new file mode 100644 index 00000000..4fb4ff0d --- /dev/null +++ b/src/reflex/components/CheckBox.as @@ -0,0 +1,31 @@ +package reflex.components +{ + import flash.events.Event; + + import reflex.behaviors.ButtonBehavior; + import reflex.behaviors.SelectBehavior; + import reflex.binding.Bind; + import reflex.skins.CheckBoxSkin; + + public class CheckBox extends Button + { + + public function CheckBox(label:String = "") + { + super(); + this.label = label; + } + + override protected function initialize():void { + super.initialize(); + skin = new CheckBoxSkin(); + behaviors.addItem(new ButtonBehavior(this)); + behaviors.addItem(new SelectBehavior(this)); + Bind.addBinding(this, "skin.labelDisplay.text", this, "label", false); + Bind.addBinding(this, "skin.currentState", this, "currentState", false); + //_measuredWidth = 210; + //_measuredHeight = 45; + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/Component.as b/src/reflex/components/Component.as index 5ab36972..ed142bd4 100644 --- a/src/reflex/components/Component.as +++ b/src/reflex/components/Component.as @@ -1,89 +1,96 @@ -package reflex.components +package reflex.components { import flash.display.DisplayObject; + import flash.events.Event; - import flight.events.PropertyEvent; - import flight.observers.PropertyChange; + import mx.collections.IList; + import mx.core.IStateClient2; + import mx.events.CollectionEvent; + import mx.events.CollectionEventKind; - import mx.utils.ObjectProxy; - - import reflex.behaviors.CompositeBehavior; import reflex.behaviors.IBehavior; - import reflex.behaviors.IBehavioral; - import reflex.display.Container; - import reflex.display.ReflexDisplay; - import reflex.events.InvalidationEvent; + import reflex.collections.SimpleCollection; + import reflex.containers.Container; + import reflex.display.MeasurableItem; + import reflex.display.PropertyDispatcher; + import reflex.display.StatefulItem; + import reflex.display.StyleableItem; + import reflex.framework.IMeasurable; + import reflex.framework.IMeasurablePercent; + import reflex.injection.HardCodedInjector; + import reflex.injection.IReflexInjector; + import reflex.invalidation.IReflexInvalidation; + import reflex.invalidation.LifeCycle; import reflex.measurement.resolveHeight; import reflex.measurement.resolveWidth; + import reflex.measurement.setSize; + import reflex.metadata.resolveCommitProperties; import reflex.skins.ISkin; - import reflex.skins.ISkinnable; - [Style(name="left")] - [Style(name="right")] - [Style(name="top")] - [Style(name="bottom")] - [Style(name="horizontalCenter")] - [Style(name="verticalCenter")] - [Style(name="dock")] - [Style(name="align")] + /** * @alpha */ - public class Component extends ReflexDisplay implements IBehavioral, ISkinnable + public class Component extends StatefulItem { - static public const MEASURE:String = "measure"; - InvalidationEvent.registerPhase(MEASURE, 0, true); - - private var _state:String; + private var _explicitWidth:Number; + private var _explicitHeight:Number; + /* + private var _percentWidth:Number; + private var _percentHeight:Number; + */ private var _skin:Object; - private var _behaviors:CompositeBehavior; + private var _behaviors:IList = new SimpleCollection(); // injection issues + //private var _queue:Array; - public function Component() - { - _behaviors = new CompositeBehavior(this); - PropertyChange.addObserver(this, "skin", this, setTarget); - } + private var _states:Array; + private var _currentState:String; - [Bindable] private var _style:ObjectProxy = new ObjectProxy(); - public function get style():Object { return _style; } - public function set style(value:*):void { - if(value is String) { - var token:String = value as String; - var assignments:Array = token.split(";"); - for each(var assignment:String in assignments) { - var split:Array = assignment.split(":"); - var property:String = split[0]; - var v:String = split[1]; - _style[property] = v; + private var _enabled:Boolean = true; + + private var _injector:IReflexInjector;// = new HardCodedInjector(); + + public function get injector():IReflexInjector { return _injector; } + public function set injector(value:IReflexInjector):void { + _injector = value; + if(_injector) { + var length:int = _behaviors ? _behaviors.length : 0; + for(var i:int = 0; i < length; i++) { + var item:IBehavior = _behaviors.getItemAt(i) as IBehavior; + _injector.injectInto(item); } } } - public function setStyle(property:String, value:*):void { - style[property] = value; + override public function set display(value:Object):void { + super.display = value; + if(_skin) { + _skin.display = value; + } } - [Bindable] - public function get currentState():String - { - return _state; - } - public function set currentState(value:String):void + public function Component() { - var change:PropertyChange = PropertyChange.begin(); - _state = change.add(this, "currentState", _state, value); - change.commit(); + super(); + _behaviors.addEventListener(CollectionEvent.COLLECTION_CHANGE, behaviorsCollectionChangeHandler, false, 0, true); + //this.addEventListener(LifeCycle.INITIALIZE, initialize); } + override protected function initialize():void { + super.initialize(); + if(_skin) { + injector.injectInto(_skin); + _skin.display = display; + } + } [ArrayElementType("reflex.behaviors.IBehavior")] + [Bindable(event="behaviorsChange")] + [Inspectable(name="Behaviors", type=Array)] /** - * A dynamic object or hash map of behavior objects. behaviors - * is effectively read-only, but setting either an IBehavior or array of - * IBehavior to this property will add those behaviors to the behaviors - * object/map. + * A collection of behavior objects. * * To set behaviors in MXML: * <Component...> @@ -93,48 +100,131 @@ package reflex.components * </behaviors> * </Component> */ - public function get behaviors():CompositeBehavior - { - return _behaviors; - } + public function get behaviors():IList { return _behaviors; } public function set behaviors(value:*):void { - var change:PropertyChange = PropertyChange.begin(); - value = change.add(this, "behaviors", _behaviors, value); - _behaviors.clear(); if (value is Array) { - _behaviors.add(value); + var valueArray:Array = value as Array; + var length:int = valueArray.length; + for(var i:int = 0; i < length; i++) { + var behavior:IBehavior = valueArray[i]; + _behaviors.addItem(behavior); + } + //_behaviors.source = value; } else if (value is IBehavior) { - _behaviors.add([value]); + _behaviors.addItem(value); + //_behaviors.source = [value]; + } else if(value is IList) { + (value as IList) + _behaviors = value; } - change.commit(); + dispatchEvent(new Event("behaviorsChange")); } - [Bindable] - public function get skin():Object - { - return _skin; - } + [Bindable(event="skinChange")] + [Inspectable(name="Skin", type=Class)] + public function get skin():Object { return _skin; } public function set skin(value:Object):void { - var change:PropertyChange = PropertyChange.begin(); - _skin = change.add(this, "skin", _skin, value); - //setSize(getWidth(_skin), getHeight(_skin)); - change.commit(); - } - - protected function setTarget(oldValue:*, newValue:*):void - { - if (oldValue != null) { - oldValue.target = null; + if (_skin == value) { + return; + } + + if (_skin is ISkin) { + (_skin as ISkin).target = null; + } + + var oldSkin:Object = _skin; + _skin = value; + + if (_skin is ISkin) { + (_skin as ISkin).target = this; + } + if(injector && _skin) { + injector.injectInto(_skin); } - if (newValue is ISkin) { - newValue.target = this; - } else if(newValue is DisplayObject) { - addChild(newValue as DisplayObject); + // invalidation in skin + //_skin.invalidate(LifeCycle.INVALIDATE); + //_skin.invalidate(LifeCycle.MEASURE); + //_skin.invalidate(LifeCycle.LAYOUT); + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + dispatchEvent(new Event("skinChange")); + } + + private function behaviorsCollectionChangeHandler(event:CollectionEvent):void { + switch(event.kind) { + case CollectionEventKind.ADD: + for each(var item:IBehavior in event.items) { + item.target = this; + if(injector) { injector.injectInto(item); } + } + break; + } } + + [PercentProxy("percentWidth")] + [Bindable(event="widthChange")] + override public function get width():Number { + if(!isNaN(_explicitWidth)) { return _explicitWidth; } + if(_skin) { return _skin.width; } + return 0; + } + override public function set width(value:Number):void { + _explicitWidth = value; + reflex.measurement.setSize(skin, value, height); + //skin.width = value; + } + + [PercentProxy("percentHeight")] + [Bindable(event="heightChange")] + override public function get height():Number { + if(!isNaN(_explicitHeight)) { return _explicitHeight; } + if(_skin) { return _skin.height; } + return 0; + } + override public function set height(value:Number):void { + notify("height", _explicitHeight, _explicitHeight = value); + reflex.measurement.setSize(skin, width, value); + //skin.height = value; + } + /* + public function get percentWidth():Number { return _percentWidth; } + public function set percentWidth(value:Number):void { + _percentWidth = value; + } + + public function get percentHeight():Number { return _percentHeight; } + public function set percentHeight(value:Number):void { + _percentHeight = value; + } + + public function get explicitWidth():Number { return _explicitWidth; } + public function get explicitHeight():Number { return _explicitHeight; } + + public function get measuredWidth():Number { return skin.width; } + public function get measuredHeight():Number { return skin.height; } + */ + override public function setSize(width:Number, height:Number):void { + super.setSize(width, height); + reflex.measurement.setSize(skin, width, height); + } + /* + override protected function onMeasure(event:Event):void { + if(skin) { + if (isNaN(explicitWidth)) { + var w:Number = reflex.measurement.resolveWidth(skin); + _measuredWidth = w; // explicit width of skin becomes measured width of component + } + if(isNaN(explicitHeight)) { + var h:Number = reflex.measurement.resolveHeight(skin); + _measuredHeight = h; // explicit height of skin becomes measured height of component + } + } + } + */ } } diff --git a/src/reflex/components/HScrollBar.as b/src/reflex/components/HScrollBar.as new file mode 100644 index 00000000..aab75643 --- /dev/null +++ b/src/reflex/components/HScrollBar.as @@ -0,0 +1,30 @@ +package reflex.components +{ + + import flash.events.Event; + + import reflex.behaviors.SlideBehavior; + import reflex.behaviors.StepBehavior; + import reflex.data.ScrollPosition; + import reflex.skins.HSliderSkin; + + public class HScrollBar extends SliderComponent + { + + public function HScrollBar() + { + super(); + } + + override protected function initialize():void { + super.initialize(); + position = new ScrollPosition(); + skin = new HSliderSkin(); + behaviors.addItem(new StepBehavior(this)); + behaviors.addItem(new SlideBehavior(this, SlideBehavior.HORIZONTAL, true)); + //measured.width = 170; + //measured.height = 20; + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/HSlider.as b/src/reflex/components/HSlider.as new file mode 100644 index 00000000..439a5ca9 --- /dev/null +++ b/src/reflex/components/HSlider.as @@ -0,0 +1,14 @@ +package reflex.components +{ + + import flash.events.Event; + + import reflex.behaviors.SlideBehavior; + import reflex.data.Position; + import reflex.skins.HSliderSkin; + + public class HSlider extends SliderComponent + { + + } +} \ No newline at end of file diff --git a/src/reflex/components/Knob.as b/src/reflex/components/Knob.as new file mode 100644 index 00000000..d93a1c39 --- /dev/null +++ b/src/reflex/components/Knob.as @@ -0,0 +1,21 @@ +package reflex.components +{ + + import flash.display.Sprite; + + import reflex.behaviors.SlideBehavior; + import reflex.data.Position; + + public class Knob extends SliderComponent + { + + public function Knob() + { + super(); + position = new Position(); + skin = new Sprite();//new KnobSkin(); + behaviors.addItem(new SlideBehavior(this)); + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/List.as b/src/reflex/components/List.as index 1125b482..f08045c0 100644 --- a/src/reflex/components/List.as +++ b/src/reflex/components/List.as @@ -1,44 +1,79 @@ package reflex.components { - import flight.binding.Bind; - import flight.list.ArrayList; - import flight.list.IList; - import flight.position.IPosition; + import flash.display.DisplayObject; + import flash.events.Event; - import mx.core.Container; + import mx.collections.IList; + import reflex.behaviors.ListSelectionBehavior; + import reflex.binding.Bind; + import reflex.data.IPosition; + import reflex.data.IRange; + import reflex.data.ISelection; + import reflex.data.Selection; + import reflex.invalidation.LifeCycle; + import reflex.layouts.HorizontalLayout; import reflex.layouts.ILayout; import reflex.layouts.VerticalLayout; - import reflex.skins.ListBoxSkin; import reflex.skins.ListSkin; - - /** - * @alpha - */ + public class List extends Component { - [Bindable] public var layout:ILayout; - [Bindable] public var position:IPosition; - [Bindable] public var template:Object; - [Bindable] public var dataProvider:IList; + private var _layout:ILayout; + private var _dataProvider:IList; + private var _template:Object; + private var _position:IPosition; + private var _selection:ISelection + + [Bindable(event="layoutChange")] + public function get layout():ILayout { return _layout; } + public function set layout(value:ILayout):void { + notify("layout", _layout, _layout = value); + } + + [Bindable(event="dataProviderChange")] + public function get dataProvider():IList { return _dataProvider; } + public function set dataProvider(value:IList):void { + notify("dataProvider", _dataProvider, _dataProvider = value); + } - public function List() - { - skin = new ListSkin(); - layout = new VerticalLayout(); - template = Button; + [Bindable(event="templateChange")] + public function get template():Object { return _template; } + public function set template(value:Object):void { + notify("template", _template, _template = value); } - /* - protected function init():void - { - var listBoxSkin:ListBoxSkin = new ListBoxSkin(); - skin = listBoxSkin; - position = listBoxSkin.position; - - Bind.addBinding(listBoxSkin, "template", this, "template", true); - Bind.addBinding(listBoxSkin, "dataProvider", this, "dataProvider", true); + + [Bindable(event="selectionChange")] + public function get selection():ISelection { return _selection; } + public function set selection(value:ISelection):void { + notify("selection", _selection, _selection = value); } - */ + + [Bindable(event="positionChange")] + public function get position():IPosition { return _position; } + public function set position(value:IPosition):void { + notify("position", _position, _position = value); + } + + override protected function initialize():void { + super.initialize(); + //selection = new Selection(); + //if(layout == null) { + // layout = new VerticalLayout(); + // (layout as VerticalLayout).gap = 10; + //} + //if(template == null) { template = ListItem; } + //if(skin == null) { skin = new ListSkin(); } + Bind.addBinding(this, "skin.container.content", this, "dataProvider"); + Bind.addBinding(this, "skin.container.template", this, "template"); + Bind.addBinding(this, "skin.container.layout", this, "layout"); + Bind.addBinding(this, "skin.container.horizontal", this, "position"); + Bind.addBinding(this, "skin.container.vertical", this, "position"); + //behaviors.addItem(new ListSelectionBehavior(this)); + //invalidate(LifeCycle.MEASURE); + //invalidate(LifeCycle.LAYOUT); + } + } } \ No newline at end of file diff --git a/src/reflex/components/ListItem.as b/src/reflex/components/ListItem.as index b642df42..ca4361b6 100644 --- a/src/reflex/components/ListItem.as +++ b/src/reflex/components/ListItem.as @@ -1,27 +1,86 @@ package reflex.components { + + import flash.events.Event; + import flash.utils.getDefinitionByName; + import flash.utils.getQualifiedClassName; + import mx.core.IDataRenderer; + import mx.core.IFactory; import reflex.behaviors.ButtonBehavior; + import reflex.behaviors.SelectBehavior; + import reflex.binding.Bind; + import reflex.skins.ISkin; + import reflex.skins.ListItemSkin; /** * @alpha */ - public class ListItem extends Component implements IDataRenderer + public class ListItem extends Button implements IDataRenderer, IFactory { - [Bindable] - public var label:String; - [Bindable] - public var selected:Boolean; + private var _data:Object; + + [Bindable(event="dataChange")] + public function get data():Object { return _data; } + public function set data(value:Object):void { + notify("data", _data, _data = value); + } - [Bindable] - public var data:Object; public function ListItem() { - behaviors = new ButtonBehavior(); - skin = new ButtonGraphic(); + super(); + } + + override protected function initialize():void { + super.initialize(); + //skin = new ListItemSkin(); + //behaviors.addItem(new ButtonBehavior(this)); + //behaviors.addItem(new SelectBehavior(this)); + Bind.addBinding(this, "skin.labelDisplay.text", this, "data.label"); + //Bind.addBinding(this, "skin.labelDisplay.text", this, "data.name"); // weird - only one targetPath + Bind.addBinding(this, "skin.currentState", this, "currentState", false); + //_measuredWidth = 210; + //_measuredHeight = 88; + percentWidth = 100; + } + + public function newInstance():* { + var n:String = flash.utils.getQualifiedClassName(this); + var C:Class = getDefinition( n ); + + var instance:ListItem = new C(); + + if (skin == null) + throw new Error("Trying to create a new instance of ListItemDefinition using a ListItemDefinition object that does not have a skin defined. ListItemDefinition.newInstance() requires that a skin is defined."); + + var sn:String = flash.utils.getQualifiedClassName(skin); + var sC:Class = getDefinition( sn ); + instance.skin = new sC() as ISkin; + return instance; + } + + private function getDefinition( name:String ):Class + { + var C:Class; + try{ + C = flash.utils.getDefinitionByName( name ) as Class + } catch ( error:Error ) + { + /*if( loaderInfo ) + { + C = loaderInfo.applicationDomain.getDefinition( name ) as Class; + }*/ + if( !C ) + { + trace( "ListItem: Unable to retrieve definition of Class '" + name + "'." ); + throw error; + } + } + return C; } + } } \ No newline at end of file diff --git a/src/reflex/components/ProgressBar.as b/src/reflex/components/ProgressBar.as new file mode 100644 index 00000000..deb3363a --- /dev/null +++ b/src/reflex/components/ProgressBar.as @@ -0,0 +1,14 @@ +package reflex.components +{ + + import flash.events.Event; + + import reflex.behaviors.SlideBehavior; + import reflex.components.SliderComponent; + import reflex.data.Position; + + public class ProgressBar extends SliderComponent + { + + } +} \ No newline at end of file diff --git a/src/reflex/components/RadioButton.as b/src/reflex/components/RadioButton.as new file mode 100644 index 00000000..19e5f1c2 --- /dev/null +++ b/src/reflex/components/RadioButton.as @@ -0,0 +1,31 @@ +package reflex.components +{ + import flash.events.Event; + + import reflex.behaviors.ButtonBehavior; + import reflex.behaviors.SelectBehavior; + import reflex.binding.Bind; + import reflex.skins.RadioButtonSkin; + + public class RadioButton extends Button + { + + public function RadioButton(label:String = "") + { + super(); + this.label = label; + } + + override protected function initialize():void { + super.initialize(); + skin = new RadioButtonSkin(); + behaviors.addItem(new ButtonBehavior(this)); + behaviors.addItem(new SelectBehavior(this)); + Bind.addBinding(this, "skin.labelDisplay.text", this, "label", false); + Bind.addBinding(this, "skin.currentState", this, "currentState", false); + //_measuredWidth = 210; + //_measuredHeight = 45; + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/ScrollBar.as b/src/reflex/components/ScrollBar.as deleted file mode 100644 index 38a1a122..00000000 --- a/src/reflex/components/ScrollBar.as +++ /dev/null @@ -1,33 +0,0 @@ -package reflex.components -{ - import flight.position.IPosition; - - import legato.components.ScrollBarGraphic; - - import reflex.behaviors.SlideBehavior; - import reflex.behaviors.StepBehavior; - import reflex.skins.ScrollBarSkin; - - public class ScrollBar extends Component - { - [Bindable] public var position:IPosition; - - public function ScrollBar() - { - //skin = new ScrollBarGraphic(); - behaviors.slide = new SlideBehavior(this); - } - - /*override */protected function init():void - { - //var scrollBarSkin:ScrollBarSkin = new ScrollBarSkin(); - skin = new ScrollBarGraphic(); - var slideBehavior:SlideBehavior = new SlideBehavior(this); - behaviors.slide = slideBehavior; - var stepBehavior:StepBehavior = new StepBehavior(this); - behaviors.step = stepBehavior; -// position = scrollBarSkin.position = slideBehavior.position = stepBehavior.position; - } - - } -} \ No newline at end of file diff --git a/src/reflex/components/ScrollPane.as b/src/reflex/components/ScrollPane.as deleted file mode 100644 index 60c6c60c..00000000 --- a/src/reflex/components/ScrollPane.as +++ /dev/null @@ -1,86 +0,0 @@ -package reflex.components -{ - import flash.display.InteractiveObject; - - import flight.binding.Bind; - import flight.position.IPosition; - - import mx.core.Container; - - import reflex.behaviors.SlideBehavior; - import reflex.display.ScrollContainer; - import reflex.layout.Align; - import reflex.layout.Block; - import reflex.layout.LayoutWrapper; - import reflex.measurement.resolveHeight; - import reflex.skins.GraphicSkin; - import reflex.skins.ScrollBarSkin; - - [DefaultProperty("container")] - public class ScrollPane extends Component - { - [Bindable] - public var horizontal:IPosition; - - [Bindable] - public var vertical:IPosition; - - private var _container:InteractiveObject; [Bindable] - public function get container():InteractiveObject { return _container; } - public function set container(value:InteractiveObject):void { - if(_container) { this.removeChild(_container); } - _container = value; - if(_container) { this.addChild(_container); } - } - - public function ScrollPane() - { - Bind.addListener(this, onVerticalScroll, this, "vertical.value"); - } - - private function onVerticalScroll(value:Object):void { - var height:Number = reflex.measurement.resolveHeight(this); - var containerHeight:Number = reflex.measurement.resolveHeight(container); - var potential:Number = containerHeight - height; - if(vertical) { - vertical.min = 0; - vertical.max = containerHeight; - container.y = potential * vertical.percent * -1; - } - } - - /* - override protected function constructChildren():void - { - container = new ScrollContainer(); - //container.dock = Align.FILL; - - var graphic:ScrollPaneGraphic = new ScrollPaneGraphic(); - graphic.addChild(container); - - var block:Block; - block = new Block(graphic.corner); - block.scale = true; - block.anchor.right = block.anchor.bottom = 0; - - var slideBehavior:SlideBehavior; - var scrollBarSkin:ScrollBarSkin; - - slideBehavior = new SlideBehavior(graphic.hScroll); - scrollBarSkin = new ScrollBarSkin(graphic.hScroll); - scrollBarSkin.target = graphic.hScroll; - hPosition = scrollBarSkin.position = slideBehavior.position = container.hPosition; - scrollBarSkin.graphicBlock.dock = Align.BOTTOM; - scrollBarSkin.graphicBlock.margin.right = graphic.corner.width; - - slideBehavior = new SlideBehavior(graphic.vScroll); - scrollBarSkin = new ScrollBarSkin(graphic.vScroll); - scrollBarSkin.target = graphic.vScroll; - vPosition = scrollBarSkin.position = slideBehavior.position = container.vPosition; - scrollBarSkin.graphicBlock.dock = Align.RIGHT; - - skin = new GraphicSkin(graphic); - } - */ - } -} \ No newline at end of file diff --git a/src/reflex/components/Scroller.as b/src/reflex/components/Scroller.as new file mode 100644 index 00000000..4291dcdd --- /dev/null +++ b/src/reflex/components/Scroller.as @@ -0,0 +1,75 @@ +package reflex.components +{ + + import flash.events.Event; + + import mx.collections.IList; + + import reflex.behaviors.ScrollerBehavior; + import reflex.binding.Bind; + import reflex.collections.SimpleCollection; + import reflex.collections.convertToIList; + import reflex.containers.IContainer; + import reflex.data.IPosition; + import reflex.data.IRange; + import reflex.data.Position; + import reflex.layouts.BasicLayout; + import reflex.layouts.ILayout; + import reflex.skins.ScrollerSkin; + + [DefaultProperty("content")] + public class Scroller extends Component implements IContainer + { + + + private var _horizontal:IPosition; + private var _vertical:IPosition; + private var _content:IList; + private var _layout:ILayout; + + [Bindable(event="horizontalPositionChange")] + public function get horizontalPosition():IPosition { return _horizontal; } + public function set horizontalPosition(value:IPosition):void { + notify("horizontalPosition", _horizontal, _horizontal = value); + } + + [Bindable(event="verticalPositionChange")] + public function get verticalPosition():IPosition { return _vertical; } + public function set verticalPosition(value:IPosition):void { + notify("verticalPosition", _vertical, _vertical = value); + } + + [Bindable(event="contentChange")] + public function get content():IList{ return _content; } + public function set content(value:*):void { + if(_content == value) { + return; + } + var oldContent:IList = _content; + _content = reflex.collections.convertToIList(value); + notify("content", oldContent, _content); + } + + [Bindable(event="layoutChange")] + public function get layout():ILayout { return _layout; } + public function set layout(value:ILayout):void { + notify("layout", _layout, _layout = value); + } + + override protected function initialize():void { + super.initialize(); + _content = new SimpleCollection(); + horizontalPosition = new Position(); + verticalPosition = new Position(); + layout = new BasicLayout(); + skin = new ScrollerSkin(); + + Bind.addBinding(this, "skin.container.content", this, "content"); + Bind.addBinding(this, "skin.container.layout", this, "layout"); + + //Bind.addBinding(this, "skin.container.mask", this, "skin.mask"); + behaviors.addItem(new ScrollerBehavior(this)); + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/SliderComponent.as b/src/reflex/components/SliderComponent.as new file mode 100644 index 00000000..f54d6d02 --- /dev/null +++ b/src/reflex/components/SliderComponent.as @@ -0,0 +1,34 @@ +package reflex.components +{ + + import flash.events.Event; + import flash.events.IEventDispatcher; + + import reflex.data.IPosition; + import reflex.events.DataChangeEvent; + + [Event(name="valueChange", type="reflex.events.DataChangeEvent")] + public class SliderComponent extends Component + { + + private var _position:IPosition; + + [Bindable(event="positionChange")] + public function get position():IPosition { return _position; } + public function set position(value:IPosition):void { + if(_position is IEventDispatcher) { + (_position as IEventDispatcher).removeEventListener("valueChange", redispatch, false); + } + notify("position", _position, _position = value); + if(_position is IEventDispatcher) { + (_position as IEventDispatcher).addEventListener("valueChange", redispatch, false, 0, true); + } + } + + private function redispatch(event:Event):void { + this.dispatchEvent(event); + } + + } + +} \ No newline at end of file diff --git a/src/reflex/components/StatusBar.as b/src/reflex/components/StatusBar.as new file mode 100644 index 00000000..de206ce4 --- /dev/null +++ b/src/reflex/components/StatusBar.as @@ -0,0 +1,28 @@ +package reflex.components +{ + + import flash.events.Event; + + import reflex.binding.Bind; + import reflex.components.Component; + import reflex.containers.HGroup; + + public class StatusBar extends Component + { + + public function StatusBar() + { + super(); + + } + + override protected function initialize():void { + super.initialize(); + //skin = new StatusBarSkin(); + //measured.width = 640; + //measured.height = 40; + Bind.addBinding(this, "skin.currentState", this, "currentState", false); + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/Stepper.as b/src/reflex/components/Stepper.as index b5adc5af..cdfe263f 100644 --- a/src/reflex/components/Stepper.as +++ b/src/reflex/components/Stepper.as @@ -1,28 +1,23 @@ package reflex.components { - import flight.position.IPosition; - import flight.position.Position; + import reflex.behaviors.SlideBehavior; import reflex.behaviors.StepBehavior; + import reflex.binding.Bind; + import reflex.data.Position; import reflex.skins.StepperSkin; - public class Stepper extends Component + public class Stepper extends SliderComponent { - [Bindable] - public var position:IPosition; public function Stepper() { + super(); position = new Position(); - position.value = 0; - position.min = 0; - position.max = 100; skin = new StepperSkin(); - behaviors = new StepBehavior(); + behaviors.addItem(new StepBehavior(this)); + Bind.addBinding(this, "skin.textField.text", this, "position.value", true); } - /*override */protected function init():void - { - } } } \ No newline at end of file diff --git a/src/reflex/components/TextArea.as b/src/reflex/components/TextArea.as new file mode 100644 index 00000000..c94a8b04 --- /dev/null +++ b/src/reflex/components/TextArea.as @@ -0,0 +1,40 @@ +package reflex.components +{ + import reflex.behaviors.TextAreaBehavior; + import reflex.binding.Bind; + import reflex.data.IPosition; + import reflex.skins.TextAreaSkin; + + [Style(name="multiline")] + public class TextArea extends Component + { + + private var _text:String; + private var _verticalPosition:IPosition; + + [Bindable(event="textChange")] + [Inspectable(name="Text", type=String, defaultValue="Text")] + public function get text():String { return _text; } + public function set text(value:String):void { + notify("text", _text, _text = value); + } + + [Bindable(event="verticalPositionChange")] + public function get verticalPosition():IPosition { return _verticalPosition; } + public function set verticalPosition(value:IPosition):void { + notify("verticalPosition", _verticalPosition, _verticalPosition = value); + } + + public function TextArea(text:String = "") + { + super(); + this.text = text; + skin = new TextAreaSkin(true); + behaviors = [new TextAreaBehavior(this)]; + Bind.addBinding(this, "skin.textField.text", this, "text", false); + Bind.addBinding(this, "verticalPosition", this, "skin.verticalScrollBar.position", false); + Bind.addBinding(this, "skin.currentState", this, "currentState", false); + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/TextInput.as b/src/reflex/components/TextInput.as new file mode 100644 index 00000000..3dfbc877 --- /dev/null +++ b/src/reflex/components/TextInput.as @@ -0,0 +1,28 @@ +package reflex.components +{ + import reflex.binding.Bind; + import reflex.skins.TextInputSkin; + + public class TextInput extends Component + { + + private var _text:String; + + [Bindable(event="textChange")] + [Inspectable(name="Text", type=String, defaultValue="Text")] + public function get text():String { return _text; } + public function set text(value:String):void { + notify("text", _text, _text = value); + } + + public function TextInput(text:String = "") + { + super(); + this.text = text; + skin = new TextInputSkin(); + Bind.addBinding(this, "skin.textField.text", this, "text", true); + Bind.addBinding(this, "skin.currentState", this, "currentState", false); + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/TitleBar.as b/src/reflex/components/TitleBar.as new file mode 100644 index 00000000..696cc74b --- /dev/null +++ b/src/reflex/components/TitleBar.as @@ -0,0 +1,35 @@ +package reflex.components +{ + + import flash.events.Event; + + import reflex.binding.Bind; + import reflex.components.Component; + import reflex.containers.HGroup; + + public class TitleBar extends Component + { + + private var _title:String; + + [Bindable(event="titleChange")] + public function get title():String { return _title; } + public function set title(value:String):void { + notify("title", _title, _title = value); + } + + public function TitleBar() + { + super(); + } + + override protected function initialize():void { + super.initialize(); + //skin = new TitleBarSkin(); + //measured.width = 640; + //measured.height = 88; + Bind.addBinding(this, "skin.currentState", this, "currentState", false); + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/VScrollBar.as b/src/reflex/components/VScrollBar.as new file mode 100644 index 00000000..7e566bd2 --- /dev/null +++ b/src/reflex/components/VScrollBar.as @@ -0,0 +1,30 @@ +package reflex.components +{ + + import flash.events.Event; + + import reflex.behaviors.SlideBehavior; + import reflex.behaviors.StepBehavior; + import reflex.data.ScrollPosition; + import reflex.skins.VSliderSkin; + + public class VScrollBar extends SliderComponent + { + + public function VScrollBar() + { + super(); + } + + override protected function initialize():void { + super.initialize(); + position = new ScrollPosition(); + skin = new VSliderSkin(); + behaviors.addItem(new StepBehavior(this)); + behaviors.addItem(new SlideBehavior(this, SlideBehavior.VERTICAL, true)); + //measured.width = 20; + //measured.height = 170; + } + + } +} \ No newline at end of file diff --git a/src/reflex/components/VSlider.as b/src/reflex/components/VSlider.as new file mode 100644 index 00000000..f0c66822 --- /dev/null +++ b/src/reflex/components/VSlider.as @@ -0,0 +1,15 @@ +package reflex.components +{ + + import flash.events.Event; + + import reflex.behaviors.SlideBehavior; + import reflex.data.IPosition; + import reflex.data.Position; + import reflex.skins.VSliderSkin; + + public class VSlider extends SliderComponent + { + + } +} \ No newline at end of file diff --git a/src/reflex/containers/Application.as b/src/reflex/containers/Application.as new file mode 100644 index 00000000..a5fec7a6 --- /dev/null +++ b/src/reflex/containers/Application.as @@ -0,0 +1,105 @@ +package reflex.containers +{ + + import flash.display.DisplayObject; + import flash.display.Sprite; + import flash.display.Stage; + import flash.display.StageAlign; + import flash.display.StageScaleMode; + import flash.events.ContextMenuEvent; + import flash.events.Event; + import flash.net.URLRequest; + import flash.net.navigateToURL; + import flash.ui.ContextMenu; + import flash.ui.ContextMenuItem; + + import mx.collections.IList; + import mx.core.IStateClient; + import mx.core.IStateClient2; + + import reflex.framework.IStateful; + import reflex.injection.IReflexInjector; + import reflex.invalidation.Interval; + import reflex.invalidation.Invalidation; + import reflex.invalidation.LifeCycle; + import reflex.layouts.BasicLayout; + + + //[Frame(factoryClass="reflex.tools.flashbuilder.ReflexApplicationLoader")] + [SWF(widthPercent="100%", heightPercent="100%", frameRate="30")] + + [DefaultProperty("content")] + /** + * @alpha + */ + public class Application extends Sprite implements IStateful + { + + include "../framework/PropertyDispatcherImplementation.as"; + include "../framework/StatefulImplementation.as"; + + public var injector:IReflexInjector; + + private var _container:Group; + public function get container():Group { return _container; } + + public function get content():IList { return _container ? _container.content : null; } + public function set content(value:*):void { + if(_container) { + _container.content = value; + } + } + + private var _backgroundColor:uint; + [Bindable(event="backgroundColorChange")] // the compiler knows to look for this, so we don't really draw anything for it + public function get backgroundColor():uint { return _backgroundColor; } + public function set backgroundColor(value:uint):void { + _backgroundColor = value; + //notify("backgroundColor", _backgroundColor, _backgroundColor = value); + } + + public var owner:Object = null; + + public function Application() + { + super(); + _container = new Group(); + _container.display = this; + preinitialize(); + } + + protected function preinitialize():void { + if(stage) { initialize(null); } + else { addEventListener(Event.ADDED_TO_STAGE, initialize); } + } + + public function initialize(event:Event):void { + // Application is the only Reflex thing not in a container + //super.initialize(event); + var contextMenu:ContextMenu = new ContextMenu(); + contextMenu.hideBuiltInItems(); + this.contextMenu = contextMenu; + + stage.scaleMode = StageScaleMode.NO_SCALE; + stage.align = StageAlign.TOP_LEFT; + stage.addEventListener(Event.RESIZE, onStageResize, false, 0, true); + removeEventListener(Event.ADDED_TO_STAGE, initialize); + + _container.owner = owner = this.stage; + //Invalidation.stage = this.stage; + //Invalidation.app = _container; + injector.initialize(stage, _container); + injector.injectInto(_container); + stage.addChild(this); + //this.addChild(_container.display as DisplayObject); + onStageResize(null); + } + + private function onStageResize(event:Event):void { + _container.width = stage.stageWidth; + _container.height = stage.stageHeight; + } + + + } +} \ No newline at end of file diff --git a/src/reflex/containers/Container.as b/src/reflex/containers/Container.as new file mode 100644 index 00000000..a8c9b6ee --- /dev/null +++ b/src/reflex/containers/Container.as @@ -0,0 +1,377 @@ +package reflex.containers +{ + import flash.display.DisplayObject; + import flash.display.Graphics; + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.geom.Point; + import flash.geom.Rectangle; + import flash.utils.Dictionary; + + import mx.collections.IList; + import mx.core.mx_internal; + import mx.events.CollectionEvent; + import mx.events.CollectionEventKind; + import mx.graphics.IFill; + import mx.states.IOverride; + import mx.states.State; + + import reflex.animation.AnimationToken; + import reflex.animation.AnimationType; + import reflex.animation.Animator; + import reflex.animation.IAnimator; + import reflex.collections.SimpleCollection; + import reflex.collections.convertToIList; + import reflex.components.Component; + import reflex.data.IPosition; + import reflex.display.FlashDisplayHelper; + import reflex.display.IDisplayHelper; + import reflex.display.MeasurableItem; + import reflex.display.StatefulItem; + import reflex.events.ContainerEvent; + import reflex.framework.IDataContainer; + import reflex.framework.IStateful; + import reflex.injection.IReflexInjector; + import reflex.invalidation.IReflexInvalidation; + import reflex.invalidation.LifeCycle; + import reflex.layouts.BasicLayout; + import reflex.layouts.ILayout; + import reflex.states.applyState; + import reflex.states.removeState; + import reflex.templating.getDataRenderer; + + [Style(name="left")] + [Style(name="right")] + [Style(name="top")] + [Style(name="bottom")] + [Style(name="horizontalCenter")] + [Style(name="verticalCenter")] + [Style(name="dock")] + //[Style(name="align")] + + [Event(name="initialize", type="flash.events.Event")] + [Event(name="create", type="flash.events.Event")] + + [DefaultProperty("content")] + + /** + * Container is a cornerstone class of Reflex. + * Anything that holds anything extends this (though it's never referenced concretely in the framework). + * It delegates item renderers, layout and dependency injection among other things. + * As such, it doesn't do too much internally besides provide coordination of the multiple systems. + * @alpha + */ + public class Container extends StatefulItem implements IDataContainer + { + + private var _layout:ILayout; + private var _template:Object; + private var templateChanged:Boolean; + + private var _content:IList; // a mix of data and/or Reflex Components + private var contentChanged:Boolean; + private var layoutChanged:Boolean; + + private var _fill:IFill; + private var fillChanged:Boolean; + + [Bindable] + public var renderers:IList = new SimpleCollection(); //:Array = []; // just reflex components + + private var itemRenderers:Dictionary = new Dictionary(true); + private var rendererItems:Dictionary = new Dictionary(true); + private var animationType:String = AnimationType.GENERIC; // generic, add, remove, reset, layout, resize, drag, scroll ... ? + + public function getItemForRenderer(renderer:Object):* { + return rendererItems[renderer]; + } + + public function getRendererForItem(item:*):Object { + return itemRenderers[item]; + } + + public function getRenderers():Array { return renderers.toArray()/*concat()*/; } + + private var _animator:IAnimator;//= new Animator(); + public function get animator():IAnimator { return _animator; } + public function set animator(value:IAnimator):void { + _animator = value; + } + + + private var _injector:IReflexInjector;// = new HardCodedInjector(); + public function get injector():IReflexInjector { return _injector; } + public function set injector(value:IReflexInjector):void { + _injector = value; + } + + private var _horizontal:IPosition; [Bindable(event="horizontalChange")] + public function get horizontal():IPosition { return _horizontal; } + public function set horizontal(value:IPosition):void { + if(_horizontal is IEventDispatcher) { + (_horizontal as IEventDispatcher).removeEventListener("valueChange", positionChangeHandler); + } + notify("horizontal", _horizontal, _horizontal = value); + if(_horizontal is IEventDispatcher) { + (_horizontal as IEventDispatcher).addEventListener("valueChange", positionChangeHandler); + } + } + + private var _vertical:IPosition; [Bindable(event="verticalChange")] + public function get vertical():IPosition { return _vertical; } + public function set vertical(value:IPosition):void { + if(_vertical is IEventDispatcher) { + (_vertical as IEventDispatcher).removeEventListener("valueChange", positionChangeHandler); + } + notify("vertical", _vertical, _vertical = value); + if(_vertical is IEventDispatcher) { + (_vertical as IEventDispatcher).addEventListener("valueChange", positionChangeHandler); + } + } + + private function positionChangeHandler(event:Event):void { + animationType = AnimationType.SCROLL; + onLayout(); // skip render event + } + + [Bindable(event="fillChange")] + public function get fill():IFill { return _fill; } + public function set fill(value:IFill):void { + fillChanged = true; + notify("fill", _fill, _fill = value); + invalidate(LifeCycle.COMMIT); + } + + /** + * @inheritDoc + */ + [ArrayElementType("Object")] + [Bindable(event="contentChange")] + public function get content():IList { return _content; } + public function set content(value:*):void + { + if (_content == value) { + return; + } + + var oldContent:IList = _content; + + if (_content) { + _content.removeEventListener(CollectionEvent.COLLECTION_CHANGE, onChildrenChange); + removeItems(_content.toArray(), 0); + } + _content = reflex.collections.convertToIList(value); + + contentChanged = true; + animationType = AnimationType.RESET; + invalidate(LifeCycle.COMMIT); + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("content", oldContent, _content); + this.validate(); // todo: ??? + } + + /** + * @inheritDoc + */ + [Bindable(event="layoutChange")] + public function get layout():ILayout { return _layout; } + public function set layout(value:ILayout):void { + if (_layout == value) { + return; + } + var oldLayout:ILayout = _layout; + if (_layout) { _layout.target = null; } + _layout = value; + if (_layout) { _layout.target = this; } + layoutChanged = true; + animationType = AnimationType.LAYOUT; + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("layout", oldLayout, _layout); + } + + [Bindable(event="templateChange")] + public function get template():Object { return _template; } + public function set template(value:Object):void { + if (_template == value) { + return; + } + var oldTemplate:Object = _template; + _template = value; + templateChanged = true; + animationType = AnimationType.RESET; + invalidate(LifeCycle.COMMIT); + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("template", oldTemplate, _template); + } + + override public function setSize(width:Number, height:Number):void { + super.setSize(width, height); + animationType = AnimationType.RESIZE; + invalidate(LifeCycle.LAYOUT); + } + + override protected function onCommit():void { + super.onCommit(); + if(contentChanged || templateChanged) { + if (_content) { + _content.addEventListener(CollectionEvent.COLLECTION_CHANGE, onChildrenChange); + } + addItems(_content ? _content.toArray() : [], 0);//reset(_content ? _content.toArray() : []); + contentChanged = false; + templateChanged = false; + } + if(fillChanged) { + drawBackground(); + fillChanged = false; + } + } + + override protected function onMeasure():void { + super.onMeasure(); + // the compiler gives us root styles like this. yay? + if(styleDeclaration.defaultFactory != null) { + var f:Function = styleDeclaration.defaultFactory; + var t:* = f.apply(style); + styleDeclaration.defaultFactory = null + } + if ((isNaN(explicitWidth) || isNaN(explicitHeight)) + //|| (isNaN(percentWidth) || isNaN(percentHeight)) + && layout) { + var point:Point = layout.measure(renderers.toArray()); + if (point.x != measuredWidth || point.y != measuredHeight) { + _measuredWidth = point.x; + _measuredHeight = point.y; + } + } + } + + override protected function onLayout():void { + super.onLayout(); + if (layout && renderers.length > 0) { + var rectangle:Rectangle = new Rectangle(0, 0, unscaledWidth, unscaledHeight); + var tokens:Array = layout.update(_content ? _content.toArray() : [], generateTokens(), rectangle); + animateToTokens(renderers.toArray(), tokens); + } + drawBackground(); + } + + private function drawBackground():void { + if(fill && display && display.graphics) { + var g:Graphics = display.graphics; + g.clear(); + fill.begin(g, new Rectangle(0, 0, unscaledWidth, unscaledHeight), new Point()); + g.drawRect(0, 0, unscaledWidth, unscaledHeight); + fill.end(g); + } + } + + private function generateTokens():Array { + // we'll want to pool these tokens later + var tokens:Array = []; + //if(templateChanged==true) { return tokens; } + var length:int = renderers ? renderers.length : 0; + for( var i:int = 0; i < length; i++) { + var renderer:Object = renderers.getItemAt(i); //renderers[i]; + var token:AnimationToken = animator.createAnimationToken(renderer); + tokens.push(token); + } + return tokens; + } + + private function animateToTokens(renderers:Array, tokens:Array):void { + //if(templateChanged==true) { return; } + animator.begin(); + var length:int = renderers ? renderers.length : 0; + for( var i:int = 0; i < length; i++) { + var renderer:Object = renderers[i]; + var token:AnimationToken = tokens[i]; + animator.moveItem(renderer, token, animationType); + } + animator.end(); + layoutChanged = false; + animationType = AnimationType.GENERIC; + } + + private function onChildrenChange(event:CollectionEvent):void + { + switch (event.kind) { + case CollectionEventKind.ADD : + animationType = AnimationType.ADD; + addItems(event.items, event.location); + break; + case CollectionEventKind.REMOVE : + animationType = AnimationType.REMOVE; + removeItems(event.items, event.oldLocation); + break; + case CollectionEventKind.REPLACE : + animationType = AnimationType.REPLACE; + helper.removeChild(display, event.items[1]); + //addChildAt(event.items[0], loc); + break; + case CollectionEventKind.RESET : + default: + animationType = AnimationType.RESET; + //reset(event.items); + removeItems(event.items, 0); + addItems(_content ? _content.toArray() : [], 0); + break; + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + } + /* + private function reset(items:Array):void { + removeItems(items, 0); + addItems(items, 0); + } + */ + private function addItems(items:Array, index:int):void { + var length:int = items ? items.length : 0; + for(var i:int = 0; i < length; i++) { + var item:Object = items[i]; + var renderer:Object = reflex.templating.getDataRenderer(display, item, _template); + + if(renderer is MeasurableItem) { // need to make this generic + (renderer as MeasurableItem).owner = this; + } + if(injector) { + injector.injectInto(renderer); + } + + // renderer is actually not a DisplayObject now + if(helper) { helper.addChild(display, renderer); } + //renderers.push(renderer); + //renderers.splice(index+i, 1, renderer); + renderers.addItem(renderer); + + itemRenderers[item] = renderer; + rendererItems[renderer] = item; + + //if(invalidation) { invalidation.add(renderer as IEventDispatcher); } + dispatchEvent(new ContainerEvent(ContainerEvent.ITEM_ADDED, item, renderer)); + } + } + + private function removeItems(items:Array, index:int):void { + for each (var item:Object in items) { + var renderer:Object = itemRenderers[item]; + //if(renderer == null) { renderer = item; } + if(renderer is MeasurableItem) { // need to make this generic + (renderer as MeasurableItem).owner = null; + } + if(helper.contains(display, renderer)) { + helper.removeChild(display, renderer); + } + if(invalidation) { + invalidation.validate(renderer as IEventDispatcher); + } + delete rendererItems[renderer]; + delete itemRenderers[item]; + } + } + + } +} \ No newline at end of file diff --git a/src/reflex/containers/Group.as b/src/reflex/containers/Group.as new file mode 100644 index 00000000..be6baa59 --- /dev/null +++ b/src/reflex/containers/Group.as @@ -0,0 +1,11 @@ +package reflex.containers +{ + import reflex.layouts.BasicLayout; + + // wow - after injection most component extensions are only semantic + public class Group extends Container + { + + } + +} \ No newline at end of file diff --git a/src/reflex/containers/HGroup.as b/src/reflex/containers/HGroup.as new file mode 100644 index 00000000..b08ae7b8 --- /dev/null +++ b/src/reflex/containers/HGroup.as @@ -0,0 +1,12 @@ +package reflex.containers +{ + import reflex.layouts.HorizontalLayout; + + [Style(name="gap", format="Length")] + [Style(name="horizontalAlign", format="String", enumeration="left,center,right")] + [Style(name="verticalAlign", format="String", enumeration="top,middle,bottom")] + public class HGroup extends Container + { + + } +} \ No newline at end of file diff --git a/src/reflex/containers/IContainer.as b/src/reflex/containers/IContainer.as new file mode 100644 index 00000000..3c949ae7 --- /dev/null +++ b/src/reflex/containers/IContainer.as @@ -0,0 +1,31 @@ +package reflex.containers +{ + import mx.collections.IList; + + import reflex.layouts.ILayout; + + /** + * Implemented by objects which can contain and layout children. + * + * @alpha + */ + public interface IContainer + { + + /** + * Holds the children to be measured and positioned by the container. + * Use children.addItem, children.removeItem, etc to add/remove items participating in measurement and layout. + * Note that you can still manipulate the DisplayList directly using the addChild, removeChildAt, etc. methods of DisplayObjectContainer, + * however children added manually will not be measured or positioned by the container. + */ + function get content():IList; + function set content(value:*):void; + + /** + * The layout used to measure and position this container's children. + */ + function get layout():ILayout; + function set layout(value:ILayout):void; + + } +} \ No newline at end of file diff --git a/src/reflex/containers/VGroup.as b/src/reflex/containers/VGroup.as new file mode 100644 index 00000000..cfea1312 --- /dev/null +++ b/src/reflex/containers/VGroup.as @@ -0,0 +1,11 @@ +package reflex.containers +{ + import reflex.layouts.VerticalLayout; + + [Style(name="gap", format="Length")] + [Style(name="horizontalAlign", format="String", enumeration="left,center,right")] + public class VGroup extends Container + { + + } +} \ No newline at end of file diff --git a/src/reflex/cursors/Cursor.as b/src/reflex/cursors/Cursor.as deleted file mode 100644 index 416ab378..00000000 --- a/src/reflex/cursors/Cursor.as +++ /dev/null @@ -1,358 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2009 Jacob Wright -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -//////////////////////////////////////////////////////////////////////////////// -package reflex.cursors -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.display.InteractiveObject; - import flash.events.Event; - import flash.events.IEventDispatcher; - import flash.events.MouseEvent; - import flash.ui.Mouse; - import flash.utils.Dictionary; - import flash.utils.getQualifiedClassName; - - import reflex.events.ButtonEvent; - - /** - * Cursor is a helper class to use custom cursors registered with it in your flash application. - * @experimental - */ - public class Cursor - { - - public static const AUTO:String = "auto"; - public static const ARROW:String = "arrow"; - public static const BUTTON:String = "button"; - public static const HAND:String = "hand"; - public static const HELP:String = "help"; - public static const CROSS_HAIR:String = "crossHair"; - public static const MOVE:String = "move"; - public static const WEST:String = "west"; - public static const EAST:String = "east"; - public static const NORTH:String = "north"; - public static const SOUTH:String = "south"; - public static const NORTH_WEST:String = "northWest"; - public static const NORTH_EAST:String = "northEast"; - public static const SOUTH_WEST:String = "southWest"; - public static const SOUTH_EAST:String = "southEast"; - public static const NORTH_SOUTH:String = "northSouth"; - public static const EAST_WEST:String = "eastWest"; - public static const NORTH_WEST_SOUTH_EAST:String = "northWestSouthEast"; - public static const NORTH_EAST_SOUTH_WEST:String = "northEastSouthWest"; - public static const COLUMN:String = "column"; - public static const ROW:String = "row"; - public static const VERTICAL_TEXT:String = "verticalText"; - public static const CONTEXT_MENU:String = "contextMenu"; - public static const NO_DROP:String = "noDrop"; - public static const NOT_ALLOWED:String = "notAllowed"; - public static const PROGRESS:String = "progress"; - public static const WAIT:String = "wait"; - public static const ALIAS:String = "alias"; - public static const CELL:String = "cell"; - public static const COPY:String = "copy"; - public static const ZOOM_IN:String = "zoomIn"; - public static const ZOOM_OUT:String = "zoomOut"; - public static const NONE:String = "none"; - - private static var instance:Cursor = new Cursor(); - private var flashHandled:Array = ["auto", "arrow", "button", "hand"]; - private var objects:Dictionary = new Dictionary(true); - private var cursors:Object = {}; - private var stack:Array = []; - private var currentCursor:DisplayObject; - - - public function Cursor() - { - if (instance) throw new Error("Singleton"); - init(); - } - - - protected function init():void - { - trace(ALIAS); - /* - registerCursor(ALIAS, DefaultCursors.ALIAS); - registerCursor(CELL, DefaultCursors.CELL); - registerCursor(COLUMN, DefaultCursors.COLUMN); - registerCursor(CONTEXT_MENU, DefaultCursors.CONTEXT_MENU); - registerCursor(COPY, DefaultCursors.COPY); - registerCursor(CROSS_HAIR, DefaultCursors.CROSS_HAIR); - registerCursor(EAST, DefaultCursors.EAST); - registerCursor(EAST_WEST, DefaultCursors.EAST_WEST); - registerCursor(HELP, DefaultCursors.HELP); - registerCursor(MOVE, DefaultCursors.MOVE); - registerCursor(NO_DROP, DefaultCursors.NO_DROP); - registerCursor(NONE, DefaultCursors.NONE); - registerCursor(NORTH, DefaultCursors.NORTH); - registerCursor(NORTH_EAST, DefaultCursors.NORTH_EAST); - registerCursor(NORTH_EAST_SOUTH_WEST, DefaultCursors.NORTH_EAST_SOUTH_WEST); - registerCursor(NORTH_SOUTH, DefaultCursors.NORTH_SOUTH); - registerCursor(NORTH_WEST, DefaultCursors.NORTH_WEST); - registerCursor(NORTH_WEST_SOUTH_EAST, DefaultCursors.NORTH_WEST_SOUTH_EAST); - registerCursor(NORTH_SOUTH, DefaultCursors.NOT_ALLOWED); - registerCursor(PROGRESS, DefaultCursors.PROGRESS); - registerCursor(ROW, DefaultCursors.ROW); - registerCursor(SOUTH, DefaultCursors.SOUTH); - registerCursor(SOUTH_EAST, DefaultCursors.SOUTH_EAST); - registerCursor(SOUTH_WEST, DefaultCursors.SOUTH_WEST); - registerCursor(VERTICAL_TEXT, DefaultCursors.VERTICAL_TEXT); - registerCursor(WAIT, DefaultCursors.WAIT); - registerCursor(WEST, DefaultCursors.WEST); - registerCursor(ZOOM_IN, DefaultCursors.ZOOM_IN); - registerCursor(ZOOM_OUT, DefaultCursors.ZOOM_OUT); - */ - } - - - public static function getInstance():Cursor - { - return instance; - } - - - public static function useCursor(interactiveObject:IEventDispatcher, cursor:Object):void - { - Cursor.getInstance().useCursor(interactiveObject, cursor); - } - - - public function registerCursor(name:String, cursor:Object):void - { - if (cursor is Class) { - cursor = new cursor(); - } - - if ( !(cursor is DisplayObject) ) { - throw new ArgumentError("Cursor registration failed. Cursors must be display objects."); - } - - if (cursor is InteractiveObject) { - InteractiveObject(cursor).cacheAsBitmap = true; - InteractiveObject(cursor).mouseEnabled = false; - if (cursor is DisplayObjectContainer) { - DisplayObjectContainer(cursor).mouseChildren = false; - } - } - - cursors[name] = cursor; - } - - - public function useCursor(interactiveObject:IEventDispatcher, cursor:Object):void - { - if (cursor == AUTO) { - delete objects[interactiveObject]; - interactiveObject.removeEventListener(MouseEvent.ROLL_OVER, onRollOver); - interactiveObject.removeEventListener(MouseEvent.ROLL_OUT, onRollOut); - } else { - objects[interactiveObject] = cursor; - interactiveObject.addEventListener(MouseEvent.ROLL_OVER, onRollOver); - interactiveObject.addEventListener(MouseEvent.ROLL_OUT, onRollOut); - interactiveObject.addEventListener(ButtonEvent.RELEASE_OUTSIDE, onRollOut); - } - } - - /** - * Handle the rollOver event. - */ - private function onRollOver(event:MouseEvent):void - { - showObjectCursor(event.target as InteractiveObject); - - // update the custom cursor right away if any - if (currentCursor) { - onMouseMove(event); - } - } - - /** - * Handle the rollOut event. - */ - private function onRollOut(event:MouseEvent):void - { - hideObjectCursor(event.target as InteractiveObject); - - // update the next custom cursor right away if any - if (currentCursor) { - onMouseMove(event); - } - } - - /** - * Handle a mouseMove event when there is a custom cursor on the stage. - */ - private function onMouseMove(event:MouseEvent):void - { - currentCursor.x = event.stageX; - currentCursor.y = event.stageY; - event.updateAfterEvent(); - } - - /** - * Handle a mouseLeave event. The mouse does a rollOut of all the items on the stage. - */ - private function onMouseLeave(event:Event):void - { - hideObjectCursor(null); - } - - /** - * Process and show an object's cursor. - */ - private function showObjectCursor(interactiveObject:InteractiveObject):void - { - if (stack.length) { - removeCursor(stack[stack.length - 1].target); - } - stack.push(TargetDepth.get(interactiveObject)); - stack.sortOn("depth"); - addCursor(stack[stack.length - 1].target); - } - - /** - * Remove or hide an object's cursor. - */ - private function hideObjectCursor(interactiveObject:InteractiveObject):void - { - while (stack.length && stack.pop().target != interactiveObject){} - - if (interactiveObject) { - removeCursor(interactiveObject); - } - - if (stack.length) { - addCursor(stack[stack.length - 1].target); - } - } - - /** - * Place the cursor on the stage if custom, or set the Mouse.cursor property if not. - */ - private function addCursor(interactiveObject:InteractiveObject):void - { - var cursor:Object = objects[interactiveObject]; - - if (cursor == AUTO) { - Mouse.show(); - if ("cursor" in Mouse) { - Mouse["cursor"] = AUTO; - } - return; - } - - if (cursor in cursors) { - cursor = cursors[cursor]; - } else if (cursor is Class || cursor is DisplayObject) { - var name:String = getQualifiedClassName(cursor); - if (name in cursors) { - cursor = cursors[name]; - } else { - if (cursor is Class) { - cursor = new cursor(); - } - cursors[name] = cursor; - } - } - - if (cursor is DisplayObject) { - Mouse.hide(); - interactiveObject.stage.addChild(cursor as DisplayObject); - currentCursor = cursor as DisplayObject; - interactiveObject.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); - interactiveObject.stage.addEventListener(Event.MOUSE_LEAVE, onMouseLeave); - } else if ("cursor" in Mouse) { - try { - Mouse["cursor"] = cursor; - } catch (e:ArgumentError) { - Mouse["cursor"] = AUTO; - } - } - } - - /** - * Remove the cursor on the stage if custom, or set AUTO if not. - */ - private function removeCursor(interactiveObject:InteractiveObject):void - { - var cursor:Object = objects[interactiveObject]; - - if (cursor is Class || cursor is DisplayObject) { - cursor = getQualifiedClassName(cursor); - } - - // custom cursor type - if (cursor in cursors) { - Mouse.show(); - var display:DisplayObject = cursors[cursor]; - interactiveObject.stage.removeChild(display); - currentCursor = null; - interactiveObject.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); - interactiveObject.stage.removeEventListener(Event.MOUSE_LEAVE, onMouseLeave); - // built-in cursor type - } else if ("cursor" in Mouse) { - Mouse["cursor"] = AUTO; // handle flash 9 nicely - } - } - - } -} -import flash.display.DisplayObject; -import flash.display.InteractiveObject; - -internal final class TargetDepth -{ - public var target:InteractiveObject; - public var depth:uint; - - private var next:TargetDepth; - - private static var pool:TargetDepth; - - public static function get(target:InteractiveObject):TargetDepth - { - var td:TargetDepth = pool; - if (td) { - pool = td.next; - } else { - td = new TargetDepth(); - } - var depth:int = 0; - var p:DisplayObject = target; - while (p = p.parent) depth++; - td.target = target; - td.depth = depth; - return td; - } - - public static function put(td:TargetDepth):void - { - td.target = null; - td.depth = 0; - td.next = pool; - pool = td; - } -} \ No newline at end of file diff --git a/src/reflex/cursors/DefaultCursors.as b/src/reflex/cursors/DefaultCursors.as deleted file mode 100644 index 28fc59b6..00000000 --- a/src/reflex/cursors/DefaultCursors.as +++ /dev/null @@ -1,117 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2009 Jacob Wright -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -//////////////////////////////////////////////////////////////////////////////// -package reflex.cursors -{ - public class DefaultCursors - { - /* - [Embed(source="assets/cursors.swf", symbol="Help")] - public static const HELP:Class; - - [Embed(source="assets/cursors.swf", symbol="CrossHair")] - public static const CROSS_HAIR:Class; - - [Embed(source="assets/cursors.swf", symbol="Move")] - public static const MOVE:Class; - - [Embed(source="assets/cursors.swf", symbol="WestResize")] - public static const WEST:Class; - - [Embed(source="assets/cursors.swf", symbol="EastResize")] - public static const EAST:Class; - - [Embed(source="assets/cursors.swf", symbol="NorthResize")] - public static const NORTH:Class; - - [Embed(source="assets/cursors.swf", symbol="SouthResize")] - public static const SOUTH:Class; - - [Embed(source="assets/cursors.swf", symbol="NorthWestResize")] - public static const NORTH_WEST:Class; - - [Embed(source="assets/cursors.swf", symbol="NorthEastResize")] - public static const NORTH_EAST:Class; - - [Embed(source="assets/cursors.swf", symbol="SouthWestResize")] - public static const SOUTH_WEST:Class; - - [Embed(source="assets/cursors.swf", symbol="SouthEastResize")] - public static const SOUTH_EAST:Class; - - [Embed(source="assets/cursors.swf", symbol="NorthSouthResize")] - public static const NORTH_SOUTH:Class; - - [Embed(source="assets/cursors.swf", symbol="EastWestResize")] - public static const EAST_WEST:Class; - - [Embed(source="assets/cursors.swf", symbol="NorthWestSouthEastResize")] - public static const NORTH_WEST_SOUTH_EAST:Class; - - [Embed(source="assets/cursors.swf", symbol="NorthEastSouthWestResize")] - public static const NORTH_EAST_SOUTH_WEST:Class; - - [Embed(source="assets/cursors.swf", symbol="ColumnResize")] - public static const COLUMN:Class; - - [Embed(source="assets/cursors.swf", symbol="RowResize")] - public static const ROW:Class; - - [Embed(source="assets/cursors.swf", symbol="VerticalText")] - public static const VERTICAL_TEXT:Class; - - [Embed(source="assets/cursors.swf", symbol="ContextMenu")] - public static const CONTEXT_MENU:Class; - - [Embed(source="assets/cursors.swf", symbol="NoDrop")] - public static const NO_DROP:Class; - - [Embed(source="assets/cursors.swf", symbol="NotAllowed")] - public static const NOT_ALLOWED:Class; - - [Embed(source="assets/cursors.swf", symbol="Progress")] - public static const PROGRESS:Class; - - [Embed(source="assets/cursors.swf", symbol="Wait")] - public static const WAIT:Class; - - [Embed(source="assets/cursors.swf", symbol="Alias")] - public static const ALIAS:Class; - - [Embed(source="assets/cursors.swf", symbol="Cell")] - public static const CELL:Class; - - [Embed(source="assets/cursors.swf", symbol="Copy")] - public static const COPY:Class; - - [Embed(source="assets/cursors.swf", symbol="ZoomIn")] - public static const ZOOM_IN:Class; - - [Embed(source="assets/cursors.swf", symbol="ZoomOut")] - public static const ZOOM_OUT:Class; - - [Embed(source="assets/cursors.swf", symbol="None")] - public static const NONE:Class; - */ - } -} \ No newline at end of file diff --git a/src/reflex/data/IPagingPosition.as b/src/reflex/data/IPagingPosition.as new file mode 100644 index 00000000..98d49a2b --- /dev/null +++ b/src/reflex/data/IPagingPosition.as @@ -0,0 +1,11 @@ +package reflex.data +{ + public interface IPagingPosition extends IPosition + { + + [Bindable(event="pageSizeChange")] + function get pageSize():Number; + function set pageSize(value:Number):void; + + } +} \ No newline at end of file diff --git a/src/reflex/data/IPosition.as b/src/reflex/data/IPosition.as new file mode 100644 index 00000000..b1185cec --- /dev/null +++ b/src/reflex/data/IPosition.as @@ -0,0 +1,11 @@ +package reflex.data +{ + public interface IPosition extends IRange + { + + [Bindable(event="valueChange")] + function get value():Number; + function set value(value:Number):void; + + } +} \ No newline at end of file diff --git a/src/reflex/data/IRange.as b/src/reflex/data/IRange.as new file mode 100644 index 00000000..d95c2a86 --- /dev/null +++ b/src/reflex/data/IRange.as @@ -0,0 +1,15 @@ +package reflex.data +{ + public interface IRange + { + + [Bindable(event="minimumChange")] + function get minimum():Number; + function set minimum(value:Number):void; + + [Bindable(event="maximumChange")] + function get maximum():Number; + function set maximum(value:Number):void; + + } +} \ No newline at end of file diff --git a/src/reflex/data/ISelection.as b/src/reflex/data/ISelection.as new file mode 100644 index 00000000..c3c0661d --- /dev/null +++ b/src/reflex/data/ISelection.as @@ -0,0 +1,17 @@ +package reflex.data +{ + import mx.collections.IList; + + public interface ISelection + { + + [Bindable(event="selectedItemChange")] + function get selectedItem():Object; + function set selectedItem(value:Object):void; + + [Bindable(event="selectedItemsChange")] + function get selectedItems():IList; + //function set selectedItems(value:IList):void; + + } +} \ No newline at end of file diff --git a/src/reflex/data/ISteppingPosition.as b/src/reflex/data/ISteppingPosition.as new file mode 100644 index 00000000..ce87eb66 --- /dev/null +++ b/src/reflex/data/ISteppingPosition.as @@ -0,0 +1,11 @@ +package reflex.data +{ + public interface ISteppingPosition extends IPosition + { + + [Bindable(event="stepSizeChange")] + function get stepSize():Number; + function set stepSize(value:Number):void; + + } +} \ No newline at end of file diff --git a/src/reflex/data/Position.as b/src/reflex/data/Position.as new file mode 100644 index 00000000..27d5b333 --- /dev/null +++ b/src/reflex/data/Position.as @@ -0,0 +1,32 @@ +package reflex.data +{ + + public class Position extends Range implements IPosition, ISteppingPosition + { + + private var _value:Number = 0; + private var _stepSize:Number = 0; + + [Bindable(event="valueChange")] + public function get value():Number { return _value; } + public function set value(value:Number):void { + notify("value", _value, _value = value); + } + + [Bindable(event="stepSizeChange")] + public function get stepSize():Number { return _stepSize; } + public function set stepSize(value:Number):void { + notify("stepSize", _stepSize, _stepSize = value); + } + + public function Position(minimum:Number = 0, maximum:Number = 100, value:Number = 0, stepSize:Number = 1) + { + super(minimum, maximum); + _value = value; + _stepSize = stepSize; + } + + + + } +} \ No newline at end of file diff --git a/src/reflex/data/Range.as b/src/reflex/data/Range.as new file mode 100644 index 00000000..66529789 --- /dev/null +++ b/src/reflex/data/Range.as @@ -0,0 +1,33 @@ +package reflex.data +{ + import flash.events.IEventDispatcher; + + import reflex.events.DataChangeEvent; + import reflex.display.PropertyDispatcher; + + public class Range extends PropertyDispatcher implements IRange + { + + private var _minimum:Number = 0; + private var _maximum:Number = 0; + + [Bindable(event="minimumChange", noEvent)] + public function get minimum():Number { return _minimum; } + public function set minimum(value:Number):void { + notify("minimum", _minimum, _minimum = value); + } + + [Bindable(event="maximumChange", noEvent)] + public function get maximum():Number { return _maximum;} + public function set maximum(value:Number):void { + notify("maximum", _maximum, _maximum = value); + } + + public function Range(minimum:Number = 0, maximum:Number = 100) + { + _minimum = minimum; + _maximum = maximum; + } + + } +} diff --git a/src/reflex/data/ScrollPosition.as b/src/reflex/data/ScrollPosition.as new file mode 100644 index 00000000..82d347c1 --- /dev/null +++ b/src/reflex/data/ScrollPosition.as @@ -0,0 +1,22 @@ +package reflex.data +{ + + public class ScrollPosition extends Position implements IPagingPosition + { + + private var _pageSize:Number = 10; + + [Bindable(event="pageSizeChange", noEvent)] + public function get pageSize():Number { return _pageSize; } + public function set pageSize(value:Number):void { + notify("pageSize", _pageSize, _pageSize = value); + } + + public function ScrollPosition(minimum:Number = 0, maximum:Number = 100, value:Number = 0, stepSize:Number = 1, pageSize:Number = 10) + { + super(minimum, maximum, value, stepSize); + _pageSize = pageSize; + } + + } +} \ No newline at end of file diff --git a/src/reflex/data/Selection.as b/src/reflex/data/Selection.as new file mode 100644 index 00000000..cf37ba82 --- /dev/null +++ b/src/reflex/data/Selection.as @@ -0,0 +1,77 @@ +package reflex.data +{ + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + + import mx.collections.IList; + import mx.events.CollectionEvent; + import mx.events.CollectionEventKind; + + import reflex.collections.SimpleCollection; + import reflex.events.DataChangeEvent; + import reflex.display.PropertyDispatcher; + + [Event(name="selectedItemChange", type="reflex.events.DataChangeEvent")] + + /** + * Holds the selected data for list components. + */ + public class Selection extends PropertyDispatcher implements ISelection + { + + private var _selectedItem:Object; + private var _selectedItems:IList; + + public function Selection() + { + _selectedItems = new SimpleCollection(); + _selectedItems.addEventListener(CollectionEvent.COLLECTION_CHANGE, onCollectionChange, false, 0, true); + } + + [Bindable(event="selectedItemChange")] + /** + * The selected item related to this selection. + * If multiple items are selected this property will hold the most recently selected item. + */ + public function get selectedItem():Object { return _selectedItem; } + public function set selectedItem(value:Object):void { + if (_selectedItem == value) return; + + _selectedItems.removeAll(); + if(value != null) { + _selectedItems.addItem(value); + } + notify("selectedItem", _selectedItem, _selectedItem = value); + + //dispatcheEvent(new Event("selectionChanged")); + } + + /** + * A collection of selected items. + * Add new items to the selection by calling selectedItems.addItem. + */ + public function get selectedItems():IList { return _selectedItems; } + + private function onCollectionChange(event:CollectionEvent):void { + switch(event.kind) { + case CollectionEventKind.ADD: + case CollectionEventKind.MOVE: + case CollectionEventKind.REPLACE: + notify("selectedItem", _selectedItem, _selectedItem = event.items[0]); + break; + case CollectionEventKind.REFRESH: + case CollectionEventKind.REMOVE: + case CollectionEventKind.RESET: + var length:int = _selectedItems.length; + if(length > 0) { + notify("selectedItem", _selectedItem, _selectedItem = _selectedItems.getItemAt(length-1)); + } else { + notify("selectedItem", _selectedItem, _selectedItem = null); + } + break; + } + } + + } +} \ No newline at end of file diff --git a/src/reflex/display/Container.as b/src/reflex/display/Container.as deleted file mode 100644 index 2cb1b1da..00000000 --- a/src/reflex/display/Container.as +++ /dev/null @@ -1,169 +0,0 @@ -package reflex.display -{ - import flash.display.DisplayObject; - import flash.display.MovieClip; - import flash.events.Event; - import flash.geom.Point; - import flash.geom.Rectangle; - - import flight.binding.Bind; - import flight.events.ListEvent; - import flight.events.ListEventKind; - import flight.events.PropertyEvent; - import flight.list.ArrayList; - import flight.list.IList; - - import reflex.events.InvalidationEvent; - import reflex.graphics.IDrawable; - import reflex.layout.Block; - import reflex.layout.Bounds; - import reflex.layout.Box; - import reflex.layout.LayoutWrapper; - import reflex.layouts.ILayout; - import reflex.measurement.resolveHeight; - import reflex.measurement.resolveWidth; - - [Event(name="initialize", type="reflex.events.InvalidationEvent")] - - [DefaultProperty("children")] - - /** - * @alpha - */ - public class Container extends ReflexDisplay implements IContainer - { - - static public const CREATE:String = "create"; - static public const INITIALIZE:String = "initialize"; - static public const MEASURE:String = "measure"; - static public const LAYOUT:String = "layout"; - - InvalidationEvent.registerPhase(CREATE, 0, true); - InvalidationEvent.registerPhase(INITIALIZE, 1, true); - InvalidationEvent.registerPhase(MEASURE, 2, true); - InvalidationEvent.registerPhase(LAYOUT, 3, true); - - private var _layout:ILayout; - private var _template:Object = new ReflexDataTemplate(); - private var _children:IList; - private var renderers:Array; - - public function Container() - { - addEventListener(Event.ADDED, onAdded, false, 0, true); - addEventListener(MEASURE, onMeasure, false, 0, true); - addEventListener(LAYOUT, onLayout, false, 0, true); - } - - [ArrayElementType("Object")] - public function get children():IList { return _children; } - public function set children(value:*):void - { - if(_children == value) { - return; - } - - if(_children) { - _children.removeEventListener(ListEvent.LIST_CHANGE, onChildrenChange); - } - - if(value == null) { - _children = null; - } else if(value is IList) { - _children = value as IList; - } else if(value is Array || value is Vector) { - _children = new ArrayList(value); - } else { - _children = new ArrayList([value]); - } - - if(_children) { - _children.addEventListener(ListEvent.LIST_CHANGE, onChildrenChange); - var items:Array = []; - for (var i:int = 0; i < _children.length; i++) { - items.push(_children.getItemAt(i)); - } - reset(items); - } - } - - [Bindable(event="layoutChange")] - public function get layout():ILayout { return _layout; } - public function set layout(value:ILayout):void { - if(_layout) { _layout.target = null; } - _layout = value; - if(_layout) { _layout.target = this; } - InvalidationEvent.invalidate(this, MEASURE); - InvalidationEvent.invalidate(this, LAYOUT); - } - - [Bindable] - public function get template():Object { return _template; } - public function set template(value:Object):void { - _template = value; - } - - private function onAdded(event:Event):void { - removeEventListener(Event.ADDED, onAdded, false); - InvalidationEvent.invalidate(this, CREATE); - InvalidationEvent.invalidate(this, INITIALIZE); - } - - private function onMeasure(event:InvalidationEvent):void { - if(isNaN(measurements.expliciteWidth) || isNaN(measurements.expliciteHeight)) { - var point:Point = layout.measure(renderers); - measurements.measuredWidth = point.x; - measurements.measuredHeight = point.y; - InvalidationEvent.invalidate(this, LAYOUT); - } - } - - private function onLayout(event:InvalidationEvent):void { - if(layout) { - var width:Number = reflex.measurement.resolveWidth(this); - var height:Number = reflex.measurement.resolveHeight(this); - var rectangle:Rectangle = new Rectangle(0, 0, width, height); - layout.update(renderers, rectangle); - } - } - - private function onChildrenChange(event:ListEvent):void - { - var child:DisplayObject; - var loc:int = event.location1; - switch (event.kind) { - case ListEventKind.ADD : - add(event.items, loc); - break; - case ListEventKind.REMOVE : - for each (child in event.items) { - removeChild(child); - } - break; - case ListEventKind.REPLACE : - removeChild(event.items[1]); - //addChildAt(event.items[0], loc); - break; - case ListEventKind.RESET : - reset(event.items); - break; - } - InvalidationEvent.invalidate(this, LAYOUT); - } - - private function add(items:Array, index:int):void { - var children:Array = reflex.display.addItemsAt(this, items, index, _template); - renderers.concat(children); // todo: correct ordering - } - - private function reset(items:Array):void { - while (numChildren) { - removeChildAt(numChildren-1); - } - renderers = reflex.display.addItemsAt(this, items, 0, _template); // todo: correct ordering - InvalidationEvent.invalidate(this, LAYOUT); - } - - - } -} \ No newline at end of file diff --git a/src/reflex/display/FlashDisplayHelper.as b/src/reflex/display/FlashDisplayHelper.as new file mode 100644 index 00000000..8d20b3c8 --- /dev/null +++ b/src/reflex/display/FlashDisplayHelper.as @@ -0,0 +1,72 @@ +package reflex.display +{ + import flash.display.DisplayObject; + import flash.display.Graphics; + import flash.display.Sprite; + import flash.events.Event; + + public class FlashDisplayHelper implements IDisplayHelper + { + + //public function getDisplayItem():Object { return sprite; } + + public function getGraphics(instance:Object):Graphics { + return instance.graphics as Graphics; + } + + public function getNumChildren(instance:Object):int { + if(instance) { + return instance.numChildren; + } else { + return 0; + } + } + + public function contains(instance:Object, child:Object):Boolean { + if(instance == null || child == null) { return false; } + if(child is StyleableItem) { child = child.display; } + if(child != null) { + return instance.contains(child); + } else { + return false; + } + } + + public function addChild(instance:Object, child:Object):Object { + if(instance == null || child == null) { return null; } + if(child is StyleableItem) { child = child.display; } + if(child is DisplayObject) { + return instance.addChild(child); + } else { + return null; + } + } + + public function addChildAt(instance:Object, child:Object, index:int):Object { + if(instance) { + return instance.addChildAt(child, index); + } else { + return null; + } + } + + public function removeChild(instance:Object, child:Object):Object { + if(instance == null || child == null) { return null; } + if(child is StyleableItem) { child = child.display; } + if(child != null) { + return instance.removeChild(child); + } else { + return null; + } + } + + public function removeChildAt(instance:Object, index:int):Object { + return instance.removeChildAt(index); + } + + public function removeChildren(instance:Object):void { + instance.removeChildren(); + } + + } +} \ No newline at end of file diff --git a/src/reflex/display/IContainer.as b/src/reflex/display/IContainer.as deleted file mode 100644 index 7ea77bb4..00000000 --- a/src/reflex/display/IContainer.as +++ /dev/null @@ -1,19 +0,0 @@ -package reflex.display -{ - import flight.list.IList; - - import reflex.layouts.ILayout; - - /** - * @alpha - */ - public interface IContainer - { - function get children():IList; - - function get layout():ILayout; - function set layout(value:ILayout):void; - - //function setSize(width:Number, height:Number):void; - } -} \ No newline at end of file diff --git a/src/reflex/display/IDisplayHelper.as b/src/reflex/display/IDisplayHelper.as new file mode 100644 index 00000000..f3f5e098 --- /dev/null +++ b/src/reflex/display/IDisplayHelper.as @@ -0,0 +1,22 @@ +package reflex.display +{ + import flash.display.Graphics; + + public interface IDisplayHelper + { + + //function getDisplayItem():Object; + + function getGraphics(instance:Object):Graphics; + + function getNumChildren(instance:Object):int; + + function contains(instance:Object, child:Object):Boolean; + function addChild(instance:Object, child:Object):Object; + function addChildAt(instance:Object, child:Object, index:int):Object; + function removeChild(instance:Object, child:Object):Object; + function removeChildAt(instance:Object, index:int):Object; + function removeChildren(instance:Object):void; + + } +} \ No newline at end of file diff --git a/src/reflex/display/ImageDisplay.as b/src/reflex/display/ImageDisplay.as deleted file mode 100644 index d422ef4b..00000000 --- a/src/reflex/display/ImageDisplay.as +++ /dev/null @@ -1,52 +0,0 @@ -package reflex.display -{ - import flash.display.Loader; - import flash.events.Event; - import flash.net.URLRequest; - - import reflex.events.InvalidationEvent; - - public class ImageDisplay extends ReflexDisplay - { - - public static const MEASURE:String = "measure"; - public static const SOURCE_CHANGED:String = "sourceChanged"; - - InvalidationEvent.registerPhase(SOURCE_CHANGED, 0, true); - - private var loader:Loader; - - private var _source:Object; [Bindable] - public function get source():Object { return _source; } - public function set source(value:Object):void { - _source = value; - InvalidationEvent.invalidate(this, SOURCE_CHANGED); - } - - public function ImageDisplay() - { - super(); - addEventListener(SOURCE_CHANGED, onSourceChanged, false, 0, true); - addEventListener(MEASURE, onMeasure, false, 0, true); - } - - private function onSourceChanged(event:InvalidationEvent):void { - var request:URLRequest = new URLRequest(source as String); - loader = new Loader(); - loader.load(request); - loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true); - } - - private function onComplete(event:Event):void { - measurements.measuredWidth = loader.content.width; - measurements.measuredHeight = loader.content.height; - setSize(measurements.measuredWidth, measurements.measuredHeight); - addChild(loader); - } - - private function onMeasure(event:InvalidationEvent):void { - - } - - } -} \ No newline at end of file diff --git a/src/reflex/display/MeasurableItem.as b/src/reflex/display/MeasurableItem.as new file mode 100644 index 00000000..5395e79b --- /dev/null +++ b/src/reflex/display/MeasurableItem.as @@ -0,0 +1,116 @@ +package reflex.display +{ + import flash.display.Sprite; + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + + import reflex.events.DataChangeEvent; + import reflex.framework.IMeasurable; + import reflex.framework.IMeasurablePercent; + import reflex.framework.IStyleable; + import reflex.injection.IReflexInjector; + import reflex.invalidation.IReflexInvalidation; + import reflex.invalidation.LifeCycle; + import reflex.styles.Style; + + [Event(name="mouseOver", type="flash.events.MouseEvent")] + [Event(name="mouseOut", type="flash.events.MouseEvent")] + [Event(name="mouseDown", type="flash.events.MouseEvent")] + [Event(name="mouseUp", type="flash.events.MouseEvent")] + [Event(name="click", type="flash.events.MouseEvent")] + + [Style(name="left")] + [Style(name="right")] + [Style(name="top")] + [Style(name="bottom")] + [Style(name="horizontalCenter")] + [Style(name="verticalCenter")] + //[Style(name="dock")] + [Style(name="align")] + + /** + * Provides explicit, implicit, and percent based measurement properties as well as a light-weight styling system. + * Although most display classes in Reflex extend Display, it's not referenced directly by the framework itself. + * In other words, you are not required to extend this class to use Reflex! + * + * @alpha + */ + public class MeasurableItem extends StyleableItem implements IMeasurable, IMeasurablePercent + { + + include "../framework/MeasurableImplementation.as"; // measurement + include "../framework/DisplayProxyImplementation.as"; // display proxy + + + // invalidation & lifecycle + + private var _invalidation:IReflexInvalidation; + + //[Bindable] + public var owner:Object; // Reflex Container + + private var _initialized:Boolean; + public function get initialized():Boolean { return _initialized; } + + [Bindable(event="invalidationChange")] + public function get invalidation():IReflexInvalidation { return _invalidation; } + public function set invalidation(value:IReflexInvalidation):void { + _invalidation = value; + } + + public function MeasurableItem() { + super(); + addEventListener(LifeCycle.INITIALIZE, initializeHandler); + addEventListener(LifeCycle.COMMIT, commitHandler, false, 0, true); + addEventListener(LifeCycle.MEASURE, measureHandler, false, 0, true); + addEventListener(LifeCycle.LAYOUT, layoutHandler, false, 0, true); + } + + + public function invalidate(phase:String):void { + if(_invalidation && owner != null) { _invalidation.invalidate(this, phase); } + } + + private function initializeHandler(event:Event):void { + if(_initialized) { return; } + _initialized = true; + initialize(); + } + + private function commitHandler(event:Event):void { + onCommit(); + } + + private function measureHandler(event:Event):void { + onMeasure(); + } + + private function layoutHandler(event:Event):void { + onLayout(); + } + + protected function initialize():void { + _invalidation.invalidate(this, LifeCycle.COMMIT); + _invalidation.invalidate(this, LifeCycle.MEASURE); + _invalidation.invalidate(this, LifeCycle.LAYOUT); + } + + protected function onCommit():void { + + } + + protected function onMeasure():void { + + } + + protected function onLayout():void { + + } + + public function validate():void { + if(_invalidation) { _invalidation.validate(this); } + } + + } +} \ No newline at end of file diff --git a/src/reflex/display/PropertyDispatcher.as b/src/reflex/display/PropertyDispatcher.as new file mode 100644 index 00000000..2ae2e775 --- /dev/null +++ b/src/reflex/display/PropertyDispatcher.as @@ -0,0 +1,19 @@ +package reflex.display +{ + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + + import reflex.events.DataChangeEvent; + + public class PropertyDispatcher extends EventDispatcher + { + + include "../framework/PropertyDispatcherImplementation.as"; + + public function PropertyDispatcher(target:IEventDispatcher=null) + { + super(target); + } + + } +} \ No newline at end of file diff --git a/src/reflex/display/ReflexDataTemplate.as b/src/reflex/display/ReflexDataTemplate.as deleted file mode 100644 index 4bf921b5..00000000 --- a/src/reflex/display/ReflexDataTemplate.as +++ /dev/null @@ -1,27 +0,0 @@ -package reflex.display -{ - import flash.display.Shape; - import flash.display.Sprite; - - import reflex.graphics.IDrawable; - - public class ReflexDataTemplate implements IDataTemplate - { - - public function createDisplayObject(data:*):Object - { - if(data is IDrawable) { - var shape:Shape = new Shape(); - (data as IDrawable).target = shape; - //(data as IDrawable).render(); - //shape.graphics.beginFill(0x000000, 1); - //shape.graphics.drawRect(0, 0, 100, 100); - //shape.graphics.endFill(); - return shape; - } else { - return data; - } - } - - } -} \ No newline at end of file diff --git a/src/reflex/display/ReflexDisplay.as b/src/reflex/display/ReflexDisplay.as deleted file mode 100644 index 68ece845..00000000 --- a/src/reflex/display/ReflexDisplay.as +++ /dev/null @@ -1,90 +0,0 @@ -package reflex.display -{ - import flash.display.Sprite; - import flash.events.Event; - - import reflex.events.InvalidationEvent; - import reflex.measurement.IMeasurable; - import reflex.measurement.IMeasurements; - import reflex.measurement.Measurements; - - /** - * Modifies common DisplayObject properties for improved usability. - * For instance width and height properties will not be be affected by graphics API updates. - * Better naming options are welcome for this class. - * @alpha - */ - public class ReflexDisplay extends Sprite implements IMeasurable - { - - // we might consider splitting measurement into - // a MeasuredDisplay class later. - - private var unscaledWidth:Number; - private var unscaledHeight:Number; - private var _measurements:IMeasurements = new Measurements(); - - [Bindable(event="xChange")] - override public function get x():Number { return super.x; } - override public function set x(value:Number):void { - if (super.x == value) { - return; - } - super.x = value; - dispatchEvent( new Event("xChange") ); - } - - [Bindable(event="yChange")] - override public function get y():Number { return super.y; } - override public function set y(value:Number):void { - if (super.y == value) { - return; - } - super.y = value; - dispatchEvent( new Event("yChange") ); - } - - // these width/height setters need review in regards to scaling. - // I think I would perfer following Flex's lead here. - - [Bindable(event="widthChange")] - override public function get width():Number { - return unscaledWidth * scaleX; - } - override public function set width(value:Number):void { - unscaledWidth = value / scaleX; - _measurements.expliciteWidth = unscaledWidth; - dispatchEvent( new Event("widthChange") ); - } - - - [Bindable(event="heightChange")] - override public function get height():Number { - return unscaledHeight * scaleY; - } - override public function set height(value:Number):void { - unscaledHeight = value / scaleY; - _measurements.expliciteHeight = unscaledHeight; - dispatchEvent( new Event("heightChange") ); - } - - public function get measurements():IMeasurements { return _measurements; } - public function set measurements(value:IMeasurements):void { - if(value != null) { // must not be null - _measurements = value; - } - } - - /** - * Sets width and height properties without effecting measurement. - * Use cases include layout and animation/tweening among other things. - */ - public function setSize(width:Number, height:Number):void { - unscaledWidth = width / scaleX; - unscaledHeight = height / scaleY; - dispatchEvent( new Event("widthChange") ); - dispatchEvent( new Event("heightChange") ); - } - - } -} \ No newline at end of file diff --git a/src/reflex/display/Replicator.as b/src/reflex/display/Replicator.as deleted file mode 100644 index 0ca59649..00000000 --- a/src/reflex/display/Replicator.as +++ /dev/null @@ -1,242 +0,0 @@ -package reflex.display -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.events.EventDispatcher; - - import flight.binding.Bind; - import flight.events.ListEvent; - import flight.events.ListEventKind; - import flight.events.PropertyEvent; - import flight.list.ArrayList; - import flight.list.IList; - import flight.position.IPosition; - import flight.position.Position; - - import reflex.components.Button; - import reflex.layout.LayoutWrapper; - - /** - * A replicator can create any inanimate matter, as long as the desired - * molecular structure is on file, but it cannot create antimatter, - * dilithium, latinum, or a living organism of any kind. - */ - public class Replicator extends EventDispatcher - { - [Bindable] - public var position:IPosition = new Position(); - - [Bindable] - public var dataProvider:IList = new ArrayList(); - - public var children:IList = new ArrayList(); - - // TODO: replicating subject - // 1) single template defined on component - // 2) template tied to data type - // 3) template retrieved through data inspection - - [Bindable] - public var template:Class = Button; - - [Bindable] - public var coverageSize:Number = 0; // size of coverage area (viewport) - - [Bindable] - public var itemSize:Number = 20; // assumed size of all items // TOOD: implement begin/end padding and in-between pad - - private var cap:int = 0; - private var shift:int = 0; - private var _target:DisplayObjectContainer; - - public function Replicator(target:DisplayObjectContainer = null) - { -// Bind.addListener(this, onPositionChange, this, "position.percent"); - this.target = target; - - Bind.bindEventListener(ListEvent.LIST_CHANGE, onChildrenChange, this, "dataProvider"); - Bind.addListener(this, onDataProviderChange, this, "dataProvider"); - - position.stepSize = 10; - position.skipSize = 100; - } - - [Bindable(event="targetChange")] - public function get target():DisplayObjectContainer - { - return _target; - } - public function set target(value:DisplayObjectContainer):void - { - if (_target == value) { - return; - } - - if (_target != null) { - } - - var oldValue:Object = _target; - _target = value; - - if (_target != null) { - } - - PropertyEvent.dispatchChange(this, "target", oldValue, _target); - } - - private function onPositionChange(percent:Number):void - { - var layout:LayoutWrapper = LayoutWrapper.getLayout(_target); - if (layout == null) { - return; - } - - layout.shift = position.value % (itemSize*4); - layout.invalidate(true); - var oldShift:int = shift; - shift = Math.floor(position.value / itemSize / 4)*4; - - if (shift == oldShift) { - return; - } - - // TODO: get rid of the hard-coded reference to button - // TODO: reduce loops though something called logic - I'll do it later - var i:int, button:Button; - var reshuffle:int = shift - oldShift; - if (Math.abs(reshuffle) < _target.numChildren) { - if (reshuffle < 0) { - for (i = 0; i > reshuffle; i--) { - button = _target.getChildAt(_target.numChildren-1) as Button; - _target.addChildAt(button, 0); - button.label = String( dataProvider.getItemAt(shift-1 - reshuffle + i) ); - } - } else { - for (i = 0; i < reshuffle; i++) { - button = _target.getChildAt(0) as Button; - _target.addChildAt(button, _target.numChildren); - button.label = String( dataProvider.getItemAt(shift + _target.numChildren - reshuffle + i) ); - } - } - - } else { - for (i = 0; i < _target.numChildren; i++) { - button = _target.getChildAt(i) as Button; - button.label = String( dataProvider.getItemAt(shift + i) ); - } - } - } - - private function onDataProviderChange(dp:IList):void - { - var data:Object; - var child:DisplayObject; - while (_target.numChildren > dataProvider.length) { - _target.removeChildAt(_target.numChildren-1); - } - for (var i:int = 0; i < dataProvider.length; i++) { - if (children.length < cap) { - child = i < _target.numChildren ? _target.getChildAt(i) : new template() as DisplayObject; - // TODO: assign data to child - child["label"] = String( dataProvider.getItemAt(shift) ); - children.addItemAt(child, i); - _target.addChildAt(child, i); - } - } - } - - private function onVirtualChange(event:ListEvent):void - { - if (_target == null) { - return; - } - - // measure - cap = Math.ceil(coverageSize / itemSize) + 10; - position.size = itemSize * dataProvider.length; - position.space = coverageSize; - - var data:Object; - var child:DisplayObject; - var loc:int = event.location1; - switch (event.kind) { - case ListEventKind.ADD : - for each (data in event.items) { - if (children.length < cap) { - child = new template() as DisplayObject; - // TODO: assign data to child - child["label"] = String( dataProvider.getItemAt(loc + shift) ); - children.addItemAt(child, loc); - _target.addChildAt(child, loc++); - } - } - break; - case ListEventKind.REMOVE : - for each (data in event.items) { - _target.removeChildAt(loc); - } - break; - case ListEventKind.REPLACE : - child = _target.getChildAt(loc); - // TODO: assign data to child - break; - case ListEventKind.RESET : - while (_target.numChildren > dataProvider.length) { - _target.removeChildAt(_target.numChildren-1); - } - for (var i:int = 0; i < dataProvider.length; i++) { - if (children.length < cap) { - child = i < _target.numChildren ? _target.getChildAt(i) : new template() as DisplayObject; - // TODO: assign data to child - child["label"] = String( dataProvider.getItemAt(shift) ); - children.addItemAt(child, i); - _target.addChildAt(child, i); - } - } - break; - } - } - - private function onChildrenChange(event:ListEvent):void - { - if (_target == null) { - return; - } - - var data:Object; - var child:DisplayObject; - var loc:int = event.location1; - switch (event.kind) { - case ListEventKind.ADD : - for each (data in event.items) { - child = new template() as DisplayObject; - // TODO: assign data to child - child["data"] = data; - _target.addChildAt(child, loc++); - } - break; - case ListEventKind.REMOVE : - for each (data in event.items) { - _target.removeChildAt(loc); - } - break; - case ListEventKind.REPLACE : - child = _target.getChildAt(loc); - // TODO: assign data to child - break; - case ListEventKind.RESET : - while (_target.numChildren > dataProvider.length) { - _target.removeChildAt(_target.numChildren-1); - } - for (var i:int = 0; i < dataProvider.length; i++) { - child = i < _target.numChildren ? _target.getChildAt(i) : new template() as DisplayObject; - // TODO: assign data to child - child["data"] = data; - _target.addChildAt(child, i); - } - break; - } - } - - } -} \ No newline at end of file diff --git a/src/reflex/display/ScrollContainer.as b/src/reflex/display/ScrollContainer.as deleted file mode 100644 index f5c78289..00000000 --- a/src/reflex/display/ScrollContainer.as +++ /dev/null @@ -1,85 +0,0 @@ -package reflex.display -{ - import flash.events.Event; - - import flight.binding.Bind; - import flight.position.IPosition; - - import reflex.layout.ScrollBlock; - - public class ScrollContainer extends Container - { - [Bindable] - public var horizontal:IPosition; - - [Bindable] - public var vertical:IPosition; - - /* - override public function set background(value:Number):void - { - super.background = value; - opaqueBackground = background; - cacheAsBitmap = !isNaN(background); - } - */ - /* - override public function get width():Number - { - return block.width; - } - override public function set width(value:Number):void - { - block.width = value; - } - - override public function get height():Number - { - return block.height; - } - override public function set height(value:Number):void - { - block.height = value; - } - */ - /*override *//*protected function initLayout():void - { - var scrollBlock:ScrollBlock = new ScrollBlock(); - hPosition = scrollBlock.hPosition; - vPosition = scrollBlock.vPosition; - Bind.addBinding(scrollBlock, "hPosition", this, "hPosition"); - Bind.addBinding(scrollBlock, "vPosition", this, "vPosition"); - Bind.addBinding(scrollBlock, "freeform", this, "freeform", true); - scrollBlock.addEventListener("xChange", forwardEvent); - scrollBlock.addEventListener("yChange", forwardEvent); - scrollBlock.addEventListener("displayWidthChange", forwardEvent); - scrollBlock.addEventListener("displayWidthChange", onWidthChange); - scrollBlock.addEventListener("displayHeightChange", forwardEvent); - scrollBlock.addEventListener("displayHeightChange", onHeightChange); - scrollBlock.addEventListener("snapToPixelChange", forwardEvent); - scrollBlock.addEventListener("layoutChange", forwardEvent); - scrollBlock.addEventListener("boundsChange", forwardEvent); - scrollBlock.addEventListener("marginChange", forwardEvent); - scrollBlock.addEventListener("paddingChange", forwardEvent); - scrollBlock.addEventListener("dockChange", forwardEvent); - scrollBlock.addEventListener("alignChange", forwardEvent); - - //block = scrollBlock; - } - - private function forwardEvent(event:Event):void - { - dispatchEvent(event); - } - - private function onWidthChange(event:Event):void - { - dispatchEvent( new Event("widthChange") ); - } - - private function onHeightChange(event:Event):void - { - dispatchEvent( new Event("heightChange") ); - }*/ - } -} \ No newline at end of file diff --git a/src/reflex/display/StatefulItem.as b/src/reflex/display/StatefulItem.as new file mode 100644 index 00000000..58aa399f --- /dev/null +++ b/src/reflex/display/StatefulItem.as @@ -0,0 +1,15 @@ +package reflex.display +{ + + import reflex.framework.IStateful; + import reflex.display.MeasurableItem; + import reflex.states.applyState; + import reflex.states.removeState; + + public class StatefulItem extends MeasurableItem implements IStateful + { + + include "../framework/StatefulImplementation.as"; + + } +} \ No newline at end of file diff --git a/src/reflex/display/StyleableItem.as b/src/reflex/display/StyleableItem.as new file mode 100644 index 00000000..fd5db49e --- /dev/null +++ b/src/reflex/display/StyleableItem.as @@ -0,0 +1,19 @@ +package reflex.display +{ + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + + import mx.core.IMXMLObject; + + import reflex.framework.IStyleable; + import reflex.styles.Style; + + + public class StyleableItem extends PropertyDispatcher implements IStyleable + { + + include "../framework/StyleableImplementation.as" + + } +} \ No newline at end of file diff --git a/src/reflex/display/addItem.as b/src/reflex/display/addItem.as deleted file mode 100644 index eb90ef08..00000000 --- a/src/reflex/display/addItem.as +++ /dev/null @@ -1,16 +0,0 @@ -package reflex.display -{ - import flash.display.DisplayObject; - - import reflex.graphics.IDrawable; - - // the generic objects here are suspect, but I'm leaving them in for now. - // Think DisplayObject3D from PaperVision, etc. - public function addItem(container:Object, child:Object, template:Object = null):Object - { - var renderer:Object = getDataRenderer(child, template); - container.addChild(renderer as DisplayObject); - return renderer; - } - -} \ No newline at end of file diff --git a/src/reflex/display/addItemAt.as b/src/reflex/display/addItemAt.as deleted file mode 100644 index bb355172..00000000 --- a/src/reflex/display/addItemAt.as +++ /dev/null @@ -1,36 +0,0 @@ -package reflex.display -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.display.Shape; - - import reflex.graphics.IDrawable; - - // the generic objects here are suspect, but I'm leaving them in for now. - // Think DisplayObject3D from PaperVision, etc. - public function addItemAt(container:Object, child:Object, index:int = 0, template:Object = null):Object - { - var renderer:Object = getDataRenderer(child, template); - container.addChildAt(renderer as DisplayObject, index); - return renderer; - /* - if(child is DisplayObject) { - return container.addChildAt(child as DisplayObject, index); - } else if(child is IDrawable) { - var shape:Shape = new Shape(); - (child as IDrawable).target = shape; - container.addChildAt(shape, index); - return shape; - } else { - //return container.addChildAt(child, index); - var shape:Shape = new Shape(); - shape.graphics.beginFill(0x000000, 1); - shape.graphics.drawRect(0, 0, 100, 100); - shape.graphics.endFill(); - container.addChildAt(shape, index); - return; - } - */ - } - -} \ No newline at end of file diff --git a/src/reflex/display/addItemsAt.as b/src/reflex/display/addItemsAt.as deleted file mode 100644 index c540cf6c..00000000 --- a/src/reflex/display/addItemsAt.as +++ /dev/null @@ -1,28 +0,0 @@ -package reflex.display -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - - import reflex.graphics.IDrawable; - - // the generic objects here are suspect, but I'm leaving them in for now. - // Think DisplayObject3D from PaperVision, etc. - public function addItemsAt(container:Object, children:Array, index:int = 0, template:Object = null):Array - { - var output:Array = []; - var length:int = children.length; - for(var i:int = 0; i < length; i++) { - var child:Object = children[i]; - var renderer:Object = addItemAt(container, child, index, template); - output.push(renderer); - index++; - // I think this is why Flex4 skins have a seperate graphics declaration. - // we'll have to account for this better later. - /*if(child is DisplayObject) { - index++; - }*/ - } - return output; - } - -} \ No newline at end of file diff --git a/src/reflex/display/getDataRenderer.as b/src/reflex/display/getDataRenderer.as deleted file mode 100644 index c3c3eb54..00000000 --- a/src/reflex/display/getDataRenderer.as +++ /dev/null @@ -1,28 +0,0 @@ -package reflex.display -{ - import flash.display.DisplayObject; - - import mx.core.IDataRenderer; - import mx.core.IFactory; - - public function getDataRenderer(data:*, template:Object):Object - { - var instance:Object; - if(template is IDataTemplate) { - instance = (template as IDataTemplate).createDisplayObject(data); - } else if(template is IFactory) { - instance = (template as IFactory).newInstance(); - } else if(template is Class) { - instance = new (template as Class); - } else if(template is Function) { - instance = (template as Function)(data); - } else if(data is DisplayObject) { - instance = data as DisplayObject; - } - if (instance is IDataRenderer) { - (instance as IDataRenderer).data = data; - } - return instance; - } - -} \ No newline at end of file diff --git a/src/reflex/events/ButtonEvent.as b/src/reflex/events/ButtonEvent.as deleted file mode 100644 index 8986d0f8..00000000 --- a/src/reflex/events/ButtonEvent.as +++ /dev/null @@ -1,346 +0,0 @@ -package reflex.events -{ - import flash.display.InteractiveObject; - import flash.display.Stage; - import flash.events.Event; - import flash.events.IEventDispatcher; - import flash.events.MouseEvent; - import flash.utils.Dictionary; - import flash.utils.clearTimeout; - import flash.utils.setTimeout; - - /** - * The ButtonEvent transforms InteractiveObjects into buttons by adding the common - * mouse-related events that make up a buttons behavior. The ButtonEvent class is a - * unique Event subclass because it is never instantiated and dispatched. Instances of - * the ButtonEvent are unused in favor of the standard MouseEvent type in order to - * maintain consistency with the native "rollOver" and "rollOut" events. All Button - * events are non-bubbling. - */ - public class ButtonEvent extends MouseEvent - { - /** - * Defines the value of the type property of a press event object. The press - * event is dispatched when the primary mouse button is pressed. - */ - public static const PRESS:String = "press"; - - /** - */ - public static const HOLD:String = "hold"; - - /** - * Defines the value of the type property of a drag event object. The drag - * event is dispatched anytime the mouse moves while it is being pressed. - */ - public static const DRAG:String = "drag"; - - /** - * Defines the value of the type property of a dragOver event object. The dragOver - * event is dispatched when the mouse moves over the target while it is being pressed. - */ - public static const DRAG_OVER:String = "dragOver"; - - /** - * Defines the value of the type property of a dragOut event object. The dragOut event - * is dispatched when the mouse moves off of the target while it is being pressed. - */ - public static const DRAG_OUT:String = "dragOut"; - - /** - * Defines the value of the type property of a release event object. The release - * event is dispatched when the primary mouse button is released over the target. - */ - public static const RELEASE:String = "release"; - - /** - * Defines the value of the type property of a releaseOutside event object. The releaseOutside - * event is dispatched when the primary mouse button is released off of the target. - */ - public static const RELEASE_OUTSIDE:String = "releaseOutside"; - - /** - * Defines the value of the type property of a releaseOutside event object. The releaseOutside - * event is dispatched when the primary mouse button is released off of the target. - */ - public static const STATE_UP:String = "stateUp"; - - /** - * Defines the value of the type property of a releaseOutside event object. The releaseOutside - * event is dispatched when the primary mouse button is released off of the target. - */ - public static const STATE_OVER:String = "stateOver"; - - /** - * Defines the value of the type property of a releaseOutside event object. The releaseOutside - * event is dispatched when the primary mouse button is released off of the target. - */ - public static const STATE_DOWN:String = "stateDown"; - - /** - * An index of all button's that are currently effected by a mouse press. - * Index values track whether the mouse is over the indexed Button's at any given moment. - */ - private static var pressedIndex:Dictionary = new Dictionary(true); - private static var pressedX:Dictionary = new Dictionary(true); - private static var pressedY:Dictionary = new Dictionary(true); - - private static var DELAY_INTERVAL:int = 300; - private static var HOLD_INTERVAL:int = 30; - - /** - * The makeButton static method is the primary method of the ButtonEvent, transforming a - * target InteractiveObject into a Button. By adding the ButtonEvent event types any - * InteractiveObject may become a Button, including Sprites, TextFields, even the Stage. - * - * @param object This InteractiveObject will be modified to respond to the - * mouse with the appropriate Button events. - * @param includeCallbacks When set to true, any callback methods that are defined will - * be triggered automatically. A callback method is a method - * defined on Button with a magic name, the event name, prepended - * with the prefix "on" (for example "onPress()", "onRelease()", - * "onStateUp()"). Callbacks have no parameters. - * @return Returns the object that was converted for convenience. - */ - public static function initialize(button:IEventDispatcher, includeCallbacks:Boolean = false):IEventDispatcher - { - button.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 0xFF); - button.addEventListener(MouseEvent.ROLL_OVER, onRollOver, false, 0xFF); - button.addEventListener(MouseEvent.ROLL_OUT, onRollOut, false, 0xFF); - button.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 0xFF); - - if (includeCallbacks) { - - button.addEventListener(MouseEvent.CLICK, onCallbackEvent); - button.addEventListener(MouseEvent.DOUBLE_CLICK, onCallbackEvent); - button.addEventListener(MouseEvent.ROLL_OVER, onCallbackEvent); - button.addEventListener(MouseEvent.ROLL_OUT, onCallbackEvent); - button.addEventListener(PRESS, onCallbackEvent); - button.addEventListener(DRAG, onCallbackEvent); - button.addEventListener(DRAG_OVER, onCallbackEvent); - button.addEventListener(DRAG_OUT, onCallbackEvent); - button.addEventListener(RELEASE, onCallbackEvent); - button.addEventListener(RELEASE_OUTSIDE, onCallbackEvent); - button.addEventListener(HOLD, onCallbackEvent); - button.addEventListener(STATE_UP, onCallbackEvent); - button.addEventListener(STATE_OVER, onCallbackEvent); - button.addEventListener(STATE_DOWN, onCallbackEvent); - } - - return button; - } - - public static function deinitialize(button:InteractiveObject):InteractiveObject - { - button.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); - button.removeEventListener(MouseEvent.ROLL_OVER, onRollOver); - button.removeEventListener(MouseEvent.ROLL_OUT, onRollOut); - button.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); - - button.removeEventListener(MouseEvent.CLICK, onCallbackEvent); - button.removeEventListener(MouseEvent.DOUBLE_CLICK, onCallbackEvent); - button.removeEventListener(MouseEvent.ROLL_OVER, onCallbackEvent); - button.removeEventListener(MouseEvent.ROLL_OUT, onCallbackEvent); - button.removeEventListener(PRESS, onCallbackEvent); - button.removeEventListener(DRAG, onCallbackEvent); - button.removeEventListener(DRAG_OVER, onCallbackEvent); - button.removeEventListener(DRAG_OUT, onCallbackEvent); - button.removeEventListener(RELEASE, onCallbackEvent); - button.removeEventListener(RELEASE_OUTSIDE, onCallbackEvent); - button.removeEventListener(HOLD, onCallbackEvent); - button.removeEventListener(STATE_UP, onCallbackEvent); - button.removeEventListener(STATE_OVER, onCallbackEvent); - button.removeEventListener(STATE_DOWN, onCallbackEvent); - - return button; - } - - public var deltaX:Number = 0; - public var deltaY:Number = 0; - - /** - * Though the ButtonEvent constructor is defined, ButtonEvents are never instantiated. - * All ButtonEvent types are dispatched as MouseEvents. - */ - public function ButtonEvent(type:String, bubbles:Boolean = true, cancelable:Boolean = false, - localX:Number = NaN, localY:Number = NaN, relatedObject:InteractiveObject = null, - ctrlKey:Boolean = false, altKey:Boolean = false, shiftKey:Boolean = false, - buttonDown:Boolean = false, delta:int = 0, deltaX:Number = 0, deltaY:Number = 0) - { - super(type, bubbles, cancelable, localX, localY, relatedObject, ctrlKey, altKey, shiftKey, buttonDown, delta); - this.deltaX = deltaX; - this.deltaY = deltaY; - } - - /** - * The dispatch process for all ButtonEvent events. - */ - private static function dispatchButtonEvent(button:InteractiveObject, type:String, event:MouseEvent = null, mouseEventType:Boolean = false):void - { - // performance improvement when dispatch is aborted while there are no listeners - // note: there are always listeners when including callbacks - if ( !button.hasEventListener(type) ) { - return; - } - - var classType:Class = mouseEventType ? MouseEvent : ButtonEvent; - if (event == null) { - event = new classType(type, false, false, button.mouseX, button.mouseY, null, false, false, false, pressedIndex[button] != null); - } else { - event = new classType(type, false, false, button.mouseX, button.mouseY, event.relatedObject, - event.ctrlKey, event.altKey, event.shiftKey, event.buttonDown, event.delta); - } - - if (!mouseEventType && pressedIndex[button] != null) { - ButtonEvent(event).deltaX = button.mouseX - pressedX[button]; - ButtonEvent(event).deltaY = button.mouseY - pressedY[button]; - } - - button.dispatchEvent(event); - } - - /** - * mouseDown event listener. Triggers press and stateDown events and - * sets up stage listeners for mouse move and release. - */ - private static function onMouseDown(event:MouseEvent):void - { - var button:InteractiveObject = event.currentTarget as InteractiveObject; - button.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); - button.stage.addEventListener(MouseEvent.MOUSE_UP, onRelease); - button.stage.addEventListener(Event.MOUSE_LEAVE, onRelease); - - pressedIndex[button] = setTimeout(onHold, DELAY_INTERVAL, button); - pressedX[button] = button.mouseX; - pressedY[button] = button.mouseY; - dispatchButtonEvent(button, PRESS, event); - dispatchButtonEvent(button, STATE_DOWN, event); - } - - /** - * mouseMove event listener. Triggers drag event for all pressed buttons - */ - private static function onMouseMove(event:MouseEvent):void - { - for (var i:* in pressedIndex) { - var button:InteractiveObject = i as InteractiveObject; - dispatchButtonEvent(button, DRAG, event); - } - } - - /** - * rollOver event listener. Triggers dragOver and stateDown events for - * pressed buttons and a stateOver event if the mouse isn't pressed. - */ - private static function onRollOver(event:MouseEvent):void - { - if (event is ButtonEvent) { - return; - } - var button:InteractiveObject = event.currentTarget as InteractiveObject; - - if (pressedIndex[button] != null) { - pressedIndex[button] = setTimeout(onHold, HOLD_INTERVAL, button); - dispatchButtonEvent(button, DRAG_OVER, event); - dispatchButtonEvent(button, STATE_DOWN, event); - } else if (!event.buttonDown) { - dispatchButtonEvent(button, STATE_OVER, event); - } - } - - /** - * rollOut event listener. Triggers dragOut and stateOver events for - * pressed buttons and a stateUp event if the mouse isn't pressed. - */ - private static function onRollOut(event:MouseEvent):void - { - var button:InteractiveObject = event.currentTarget as InteractiveObject; - - if (pressedIndex[button] != null) { - clearTimeout(pressedIndex[button]); - pressedIndex[button] = -1; - dispatchButtonEvent(button, DRAG_OUT, event); - dispatchButtonEvent(button, STATE_OVER, event); - } else if (!event.buttonDown) { - dispatchButtonEvent(button, STATE_UP, event); - } - } - - /** - * mouseUp event listener. Triggers rollOver and stateOver events for - * buttons that were not previously being pressed. - */ - private static function onMouseUp(event:MouseEvent):void - { - var button:InteractiveObject = event.currentTarget as InteractiveObject; - if (pressedIndex[button] == null) { - dispatchButtonEvent(button, MouseEvent.ROLL_OVER, event, true); - dispatchButtonEvent(button, STATE_OVER, event); - } - } - - /** - */ - private static function onHold(button:InteractiveObject):void - { - dispatchButtonEvent(button, HOLD); - pressedIndex[button] = setTimeout(onHold, HOLD_INTERVAL, button); - } - - /** - * mouseUp event listener. Triggers release and stateOver events for - * buttons that were just pressed and are still under the mouse, and - * releaseOutside and stateUp events for buttons that were just pressed - * and are no longer under the mouse. Also removes stage listeners for - * mouse move and release. - */ - private static function onRelease(event:Event):void - { - var stage:Stage = event.currentTarget as Stage; - stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); - stage.removeEventListener(MouseEvent.MOUSE_UP, onRelease); - stage.removeEventListener(Event.MOUSE_LEAVE, onRelease); - - for (var i:* in pressedIndex) { - var button:InteractiveObject = i as InteractiveObject; - - if (pressedIndex[button] != -1) { - dispatchButtonEvent(button, RELEASE, event as MouseEvent); - dispatchButtonEvent(button, STATE_OVER, event as MouseEvent); - } else { - dispatchButtonEvent(button, RELEASE_OUTSIDE, event as MouseEvent); - dispatchButtonEvent(button, STATE_UP, event as MouseEvent); - } - - clearTimeout(pressedIndex[button]); - delete pressedIndex[button]; - delete pressedX[button]; - delete pressedY[button]; - } - } - - /** - * Single listener for all callback events. This method is set up to listen - * to each ButtonEvent, evaluate if a callback of the appropriate name is - * available, and execute the callback. It also causes a screen refresh via - * updateAfterEvent after callback execution - this increases the perceived - * responsiveness of the button to a user. - */ - private static function onCallbackEvent(event:MouseEvent):void - { - var button:InteractiveObject = event.currentTarget as InteractiveObject; - var callback:String = event.type; - callback = "on" + callback.substr(0, 1).toUpperCase() + callback.substr(1); - - if (callback in button) { - if (button[callback].length == 1) { - button[callback](event); - } else { - button[callback](); - } - event.updateAfterEvent(); - } - } - - } -} \ No newline at end of file diff --git a/src/reflex/events/ContainerEvent.as b/src/reflex/events/ContainerEvent.as new file mode 100644 index 00000000..ca6bae07 --- /dev/null +++ b/src/reflex/events/ContainerEvent.as @@ -0,0 +1,21 @@ +package reflex.events +{ + import flash.events.Event; + + public class ContainerEvent extends Event + { + + static public var ITEM_ADDED:String = "itemAdded"; + static public var ITEM_REMOVED:String = "itemRemoved"; + + public var item:*; + public var renderer:Object; + + public function ContainerEvent(type:String, item:*, renderer:Object) + { + this.item = item; + this.renderer = renderer + super(type, false, false); + } + } +} \ No newline at end of file diff --git a/src/reflex/events/DataChangeEvent.as b/src/reflex/events/DataChangeEvent.as new file mode 100644 index 00000000..5d38bfc0 --- /dev/null +++ b/src/reflex/events/DataChangeEvent.as @@ -0,0 +1,24 @@ +package reflex.events +{ + import flash.events.Event; + + public class DataChangeEvent extends Event + { + + public var oldValue:*; + public var newValue:*; + + public function DataChangeEvent(type:String, oldValue:*, newValue:*) + { + super(type, false, false); + this.oldValue = oldValue; + this.newValue = newValue; + } + + override public function clone():Event { + var event:DataChangeEvent = new DataChangeEvent(type, oldValue, newValue); + return event; + } + + } +} \ No newline at end of file diff --git a/src/reflex/events/DragEvent.as b/src/reflex/events/DragEvent.as new file mode 100644 index 00000000..a6c124fa --- /dev/null +++ b/src/reflex/events/DragEvent.as @@ -0,0 +1,23 @@ +package reflex.events +{ + import flash.events.Event; + + public class DragEvent extends Event + { + + static public const DRAG_ENTER:String = "dragEnter"; + static public const DRAG_EXIT:String = "dragExit"; + static public const DRAG_DROP:String = "dragDrop"; + + public var item:*; + public var initiator:Object; + + public function DragEvent(type:String, initiator:Object, item:*) + { + super(type, false, false); + this.initiator = initiator; + this.item = item; + } + + } +} \ No newline at end of file diff --git a/src/reflex/events/InvalidationEvent.as b/src/reflex/events/InvalidationEvent.as deleted file mode 100644 index 72a01d7f..00000000 --- a/src/reflex/events/InvalidationEvent.as +++ /dev/null @@ -1,239 +0,0 @@ -package reflex.events -{ - import flash.display.DisplayObject; - import flash.display.Stage; - import flash.events.Event; - import flash.events.IEventDispatcher; - import flash.utils.Dictionary; - import flash.utils.setTimeout; - - /** - * @alpha - **/ - public class InvalidationEvent extends Event - { - public function InvalidationEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false) - { - super(type, bubbles, cancelable); - } - - override public function clone():Event - { - return new InvalidationEvent(type, bubbles, cancelable); - } - - private static var rendering:Boolean = false; - private static var phaseList:Array = []; - private static var phaseIndex:Object = {}; - private static var displayDepths:Dictionary = new Dictionary(true); - private static var invalidStages:Dictionary = new Dictionary(true); - - public static function registerPhase(type:String, priority:int = 0, ascending:Boolean = true):Boolean - { - var phase:RenderPhase; - if (phaseIndex[type] != null) { - phase = phaseIndex[type]; - phase.priority = priority; - phase.ascending = ascending; - } else { - phase = new RenderPhase(type, priority, ascending); - phaseIndex[type] = phase; - phaseList.push(phase); - } - - phaseList.sortOn("priority"); - return true; - } - - public static function invalidate(display:DisplayObject, type:String):void - { - if (phaseIndex[type] == null) { - throw new Error("DisplayObject cannot be invalidated in unknown phase '" + type + "'."); - } - - var phase:RenderPhase = phaseIndex[type]; - if (phase.hasDisplay(display)) { - return; - } - - display.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 0xF, true); - display.addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage, false, 0xF, true); - - if (display.stage == null) { - phase.addDisplay(display, -1); - return; - } - - var depth:int = displayDepths[display] != null ? - displayDepths[display] : - displayDepths[display] = getDepth(display); - - phase.addDisplay(display, depth); - - if (!rendering) { - invalidateStage(display.stage); - } else if ( (phase.ascending && depth <= phase.renderingDepth) || - (!phase.ascending && depth >= phase.renderingDepth) ) { - setTimeout(invalidateStage, 0, display.stage); - } - } - - public static function render():void - { - rendering = true; - validateStages(); - for each (var phase:RenderPhase in phaseList) { - phase.render(); - } - rendering = false; - } - - private static function getDepth(display:DisplayObject):int - { - var depth:int = 0; - while ( (display = display.parent) != null) { - depth++; - // if a parent already has depth defined, take the shortcut - if (displayDepths[display] != null) { - depth += displayDepths[display]; - break; - } - } - return depth; - } - - private static function invalidateStage(stage:Stage):void - { - invalidStages[stage] = true; - stage.invalidate(); - stage.addEventListener(Event.RENDER, onRender, false, 0xF, true); - stage.addEventListener(Event.RESIZE, onRender, false, 0xF, true); - } - - private static function validateStages():void - { - for (var i:* in invalidStages) { - var stage:Stage = i; - stage.removeEventListener(Event.RENDER, onRender); - stage.removeEventListener(Event.RESIZE, onRender); - } - } - - private static function onRender(event:Event):void - { - render(); - } - - private static function onAddedToStage(event:Event):void - { - var display:DisplayObject = DisplayObject(event.target); - displayDepths[display] = getDepth(display); - - for each (var phase:RenderPhase in phaseList) { - if (phase.hasDisplay(display)) { - phase.removeDisplay(display); - invalidate(display, phase.type); - } - } - } - - private static function onRemovedFromStage(event:Event):void - { - var display:DisplayObject = DisplayObject(event.target); - delete displayDepths[display]; - - for each (var phase:RenderPhase in phaseList) { - if (phase.hasDisplay(display)) { - phase.removeDisplay(display); - phase.addDisplay(display, -1); - } - } - } - - } -} - - -import flash.display.DisplayObject; -import flash.events.IEventDispatcher; -import flash.utils.Dictionary; - -import reflex.events.InvalidationEvent; - -class RenderPhase -{ - public var ascending:Boolean = true; - public var priority:int = 0; - - private var _type:String; - private var depths:Array = []; - private var pos:int = -1; - private var current:Dictionary = new Dictionary(true); - private var invalidated:Dictionary = new Dictionary(true); - - public function RenderPhase(type:String, priority:int = 0, ascending:Boolean = true) - { - _type = type; - this.ascending = ascending; - this.priority = priority; - } - - public function get type():String - { - return _type; - } - - public function get renderingDepth():int - { - return pos; - } - - public function render():void - { - if (depths.length == 0) { - return; - } - - var beg:int = ascending ? -1 : depths.length; - var end:int = ascending ? depths.length : 0; - var vel:int = ascending ? 1 : -1; - var pre:Dictionary; - - for (pos = beg; pos != end; pos += vel) { - if (depths[pos] == null) { - continue; - } - - // replace current dictionary with a clean one before new cycle - pre = current; - current = depths[pos]; - depths[pos] = pre; - - for (var i:* in current) { - var display:DisplayObject = i; - delete current[i]; - delete invalidated[display]; - display.dispatchEvent( new InvalidationEvent(type) ); - } - } - pos = -1; - } - - public function addDisplay(display:DisplayObject, depth:int):void - { - if (depths[depth] == null) { - depths[depth] = new Dictionary(true); - } - depths[depth][display] = true; - invalidated[display] = depth; - } - public function removeDisplay(display:DisplayObject):void - { - delete depths[ invalidated[display] ][display]; - delete invalidated[display]; - } - public function hasDisplay(display:IEventDispatcher):Boolean - { - return invalidated[display] != null; - } -} diff --git a/src/reflex/features/DragManager.as b/src/reflex/features/DragManager.as new file mode 100644 index 00000000..d7a5d5ec --- /dev/null +++ b/src/reflex/features/DragManager.as @@ -0,0 +1,75 @@ +package reflex.features +{ + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + + import reflex.events.DragEvent; + + public class DragManager implements IDragManager + { + + private var item:*; + private var initiator:Object; + private var target:IEventDispatcher; + + private var targets:Array = []; + + public function addTarget(instance:IEventDispatcher):void { + targets.push(instance); + instance.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver, false, 0, true); + } + + public function removeTarget(instance:IEventDispatcher):void { + // + instance.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver, false); + } + + public function doDrag(initiator:Object, item:*):void { + this.item = item; + this.initiator = initiator; + } + + public function acceptDragDrop(target:IEventDispatcher):void { + this.target = target; + } + + public function endDrag():void { + if(target) { + target.dispatchEvent(new DragEvent(DragEvent.DRAG_DROP, initiator, item)); + } + initiator = null; + item = null; + target = null; + } + + public function isDragging():Boolean { + return initiator != null; + } + + // handlers + + private function onMouseOver(event:MouseEvent):void { + if(isDragging()) { + var instance:IEventDispatcher = event.currentTarget as IEventDispatcher; + instance.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut, false, 0, true); + instance.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 0, true); + instance.dispatchEvent(new DragEvent(DragEvent.DRAG_ENTER, initiator, item)); + } + } + + private function onMouseOut(event:MouseEvent):void { + var instance:IEventDispatcher = event.currentTarget as IEventDispatcher; + instance.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOut, false); + instance.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp, false); + instance.dispatchEvent(new DragEvent(DragEvent.DRAG_EXIT, initiator, item)); + } + + private function onMouseUp(event:MouseEvent):void { + var instance:IEventDispatcher = event.currentTarget as IEventDispatcher; + instance.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOut, false); + instance.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp, false); + instance.dispatchEvent(new DragEvent(DragEvent.DRAG_DROP, initiator, item)); + } + + } +} \ No newline at end of file diff --git a/src/reflex/features/IDragManager.as b/src/reflex/features/IDragManager.as new file mode 100644 index 00000000..a56e9d7b --- /dev/null +++ b/src/reflex/features/IDragManager.as @@ -0,0 +1,20 @@ +package reflex.features +{ + import flash.events.IEventDispatcher; + + public interface IDragManager + { + + function addTarget(instance:IEventDispatcher):void; + function removeTarget(instance:IEventDispatcher):void; + + function isDragging():Boolean; + + function doDrag(initiator:Object, item:*):void; + function acceptDragDrop(target:IEventDispatcher):void; + //function showFeedback(feedback:String):void; + //function getFeedback():String; + function endDrag():void; + + } +} \ No newline at end of file diff --git a/src/reflex/framework/DisplayProxyImplementation.as b/src/reflex/framework/DisplayProxyImplementation.as new file mode 100644 index 00000000..aaa8ca5a --- /dev/null +++ b/src/reflex/framework/DisplayProxyImplementation.as @@ -0,0 +1,153 @@ +import flash.display.DisplayObject; +import flash.events.IEventDispatcher; +import flash.events.MouseEvent; + +import reflex.display.IDisplayHelper; + +private var _x:Number = 0; +private var _y:Number = 0; +private var _scaleX:Number = 1; +private var _scaleY:Number = 1; +private var _rotationX:Number = 0; +private var _rotationY:Number = 0; +private var _rotationZ:Number = 0; +//private var _alpha:Number = 1.0; + +public var mask:Object; // todo: ? +public var filters:Object; // todo: ? + +private var _blendMode:String = "normal"; +public function get blendMode():String { return _blendMode; } +public function set blendMode(value:String):void { + _blendMode = value; + if(display) { display.blendMode = value; } +} + +private var _visible:Boolean = true; +private var _enabled:Boolean = true; + + +[Bindable(event="enabledChange")] +public function get enabled():Boolean { return _enabled; } +public function set enabled(value:Boolean):void { + if(display) { display.mouseEnabled = value; } + //if(display) { display.alpha = value ? 1 : 0.5; } + notify("enabled", _enabled, _enabled = value); +} + +[Bindable(event="xChange")] +public function get x():Number { return _x; } // needed for display == null, but doesn't respect transform/matrix changes ??? +public function set x(value:Number):void { + if(display) { display.x = value; } + notify("x", _x, _x = value); +} + +[Bindable(event="yChange")] +public function get y():Number { return _y; } +public function set y(value:Number):void { + if(display) { display.y = value; } + notify("y", _y, _y = value); +} + +[Bindable(event="scaleXChange")] +public function get scaleX():Number { return _scaleX; } +public function set scaleX(value:Number):void { + if(display) { display.scaleX = value; } + notify("scaleX", _scaleX, _scaleX = value); +} + +[Bindable(event="scaleYChange")] +public function get scaleY():Number { return _scaleY; } +public function set scaleY(value:Number):void { + if(display) { display.scaleY = value; } + notify("scaleY", _scaleY, _scaleY = value); +} + +[Bindable(event="rotationXChange")] +public function get rotationX():Number { return _rotationX; } +public function set rotationX(value:Number):void { + if(display) { display.rotationX = value; } + notify("rotationX", _rotationX, _rotationX = value); +} + +[Bindable(event="rotationYChange")] +public function get rotationY():Number { return _rotationY; } +public function set rotationY(value:Number):void { + if(display) { display.rotationY = value; } + notify("rotationY", _rotationY, _rotationY = value); +} + +[Bindable(event="rotationZChange")] +public function get rotationZ():Number { return _rotationZ; } +public function set rotationZ(value:Number):void { + if(display) { display.rotationZ = value; } + notify("rotationZ", _rotationZ, _rotationZ = value); +} + +/* +[Bindable(event="alphaChange")] +public function get alpha():Number { return _alpha; } +public function set alpha(value:Number):void { + if(display is DisplayObject) { display.alpha = value; } + notify("alpha", _alpha, _alpha = value); +} +*/ +[Bindable(event="visibleChange")] +public function get visible():Boolean { return _visible; } +public function set visible(value:Boolean):void { + if(display) { display.visible = value; } + notify("visible", _visible, _visible = value); +} + + +private var _display:Object;// = new Sprite(); +public function get display():Object { return _display; } +public function set display(value:Object):void { + if(_display is IEventDispatcher) { + (_display as IEventDispatcher).removeEventListener(MouseEvent.MOUSE_OVER, eventRepeater, false); + (_display as IEventDispatcher).removeEventListener(MouseEvent.MOUSE_OUT, eventRepeater, false); + (_display as IEventDispatcher).removeEventListener(MouseEvent.MOUSE_UP, eventRepeater, false); + (_display as IEventDispatcher).removeEventListener(MouseEvent.MOUSE_DOWN, eventRepeater, false); + (_display as IEventDispatcher).removeEventListener(MouseEvent.CLICK, eventRepeater, false); + //(_display as IEventDispatcher).removeEventListener(LifeCycle.INITIALIZE, eventRepeater, false); + //(_display as IEventDispatcher).removeEventListener(LifeCycle.INVALIDATE, eventRepeater, false); + //(_display as IEventDispatcher).removeEventListener(LifeCycle.MEASURE, eventRepeater, false); + //(_display as IEventDispatcher).removeEventListener(LifeCycle.LAYOUT, eventRepeater, false); + } + _display = value; + if(_display is IEventDispatcher) { + (_display as IEventDispatcher).addEventListener(MouseEvent.MOUSE_OVER, eventRepeater, false, 0, true); + (_display as IEventDispatcher).addEventListener(MouseEvent.MOUSE_OUT, eventRepeater, false, 0, true); + (_display as IEventDispatcher).addEventListener(MouseEvent.MOUSE_UP, eventRepeater, false, 0, true); + (_display as IEventDispatcher).addEventListener(MouseEvent.MOUSE_DOWN, eventRepeater, false, 0, true); + (_display as IEventDispatcher).addEventListener(MouseEvent.CLICK, eventRepeater, false, 0, true); + //(_display as IEventDispatcher).addEventListener(LifeCycle.INITIALIZE, eventRepeater, false, 0, true); + //(_display as IEventDispatcher).addEventListener(LifeCycle.INVALIDATE, eventRepeater, false, 0, true); + //(_display as IEventDispatcher).addEventListener(LifeCycle.MEASURE, eventRepeater, false, 0, true); + //(_display as IEventDispatcher).addEventListener(LifeCycle.LAYOUT, eventRepeater, false, 0, true); + } + if(_display) { + _display.x = _x; + _display.y = _y; + _display.scaleX = _scaleX; + _display.scaleY = _scaleY; + _display.visible = _visible; + //_display.rotationX = _rotationX; + //_display.rotationY = _rotationY; + //_display.rotationZ = _rotationZ; + //if(_display is DisplayObject) { _display.alpha = _alpha; }//_enabled ? 1 : 0.5; + } + if(_display is DisplayObject) { // need a better workaround + _display.blendMode = _blendMode; + } +} + +private var _helper:IDisplayHelper;// = new FlashDisplayHelper(); +public function get helper():IDisplayHelper { return _helper; } +public function set helper(value:IDisplayHelper):void { + _helper = value; +} + +private function eventRepeater(event:Event):void { + this.dispatchEvent(event); +} \ No newline at end of file diff --git a/src/reflex/framework/IDataContainer.as b/src/reflex/framework/IDataContainer.as new file mode 100644 index 00000000..5e7f36e0 --- /dev/null +++ b/src/reflex/framework/IDataContainer.as @@ -0,0 +1,16 @@ +package reflex.framework +{ + import reflex.containers.IContainer; + + public interface IDataContainer extends IContainer + { + + function get template():Object; + function set template(value:Object):void; + + function getRendererForItem(item:*):Object; + function getItemForRenderer(renderer:Object):*; + function getRenderers():Array; + + } +} \ No newline at end of file diff --git a/src/reflex/framework/IMeasurable.as b/src/reflex/framework/IMeasurable.as new file mode 100644 index 00000000..a9ff8ff8 --- /dev/null +++ b/src/reflex/framework/IMeasurable.as @@ -0,0 +1,53 @@ +package reflex.framework +{ + + /** + * Implemented by objects that want to have implicit measurements. + * In this system objects calculate implicit measurement preferences, but still allow explicit measurements to be set in AS3 or MXML. + * This interface is NOT required for items to be added to containers or to participate in Reflex's layout system. + */ + public interface IMeasurable + { + + function get width():Number; + function set width(value:Number):void; + + function get height():Number; + function set height(value:Number):void; + /* + function get minWidth():Number; + function set minWidth(value:Number):void; + + function get minHeight():Number; + function set minHeight(value:Number):void; + + function get maxWidth():Number; + function set maxWidth(value:Number):void; + + function get maxHeight():Number; + function set maxHeight(value:Number):void; + */ + + /** + * Holds explicit width/height values which have been assigned directly in AS3 or MXML. + */ + //function get explicit():IMeasurements; + + function get explicitWidth():Number; + function get explicitHeight():Number; + + /** + * Holds implicit width/height values which have been calculated internally. + */ + //function get measured():IMeasurements; + function get measuredWidth():Number; + function get measuredHeight():Number; + + /** + * Sets width and height properties without effecting measurement. + * Use cases include layout and animation/tweening. + */ + function setSize(width:Number, height:Number):void; + + } +} \ No newline at end of file diff --git a/src/reflex/framework/IMeasurablePercent.as b/src/reflex/framework/IMeasurablePercent.as new file mode 100644 index 00000000..afe31b5f --- /dev/null +++ b/src/reflex/framework/IMeasurablePercent.as @@ -0,0 +1,23 @@ +package reflex.framework +{ + + /** + * Implemented by objects that want to participate in percetage-based layouts. + */ + public interface IMeasurablePercent + { + + /** + * Holds an object's preference for width as a percentage (0-100). + */ + function get percentWidth():Number; + function set percentWidth(value:Number):void; + + /** + * Holds an object's preference for height as a percentage (0-100). + */ + function get percentHeight():Number; + function set percentHeight(value:Number):void; + + } +} \ No newline at end of file diff --git a/src/reflex/components/IStateful.as b/src/reflex/framework/IStateful.as similarity index 61% rename from src/reflex/components/IStateful.as rename to src/reflex/framework/IStateful.as index adefcee1..92125e9c 100644 --- a/src/reflex/components/IStateful.as +++ b/src/reflex/framework/IStateful.as @@ -1,11 +1,16 @@ -package reflex.components +package reflex.framework { + import mx.core.IStateClient2; /** * @alpha */ - public interface IStateful + public interface IStateful extends IStateClient2 { + + // these are all expected by the MXMLC compiler + // even transitions, yeah I know + /* function get states():Array; function set states(value:Array):void; @@ -16,6 +21,6 @@ package reflex.components function set currentState(value:String):void; function hasState(state:String):Boolean; - + */ } } \ No newline at end of file diff --git a/src/reflex/framework/IStyleable.as b/src/reflex/framework/IStyleable.as new file mode 100644 index 00000000..90c8f876 --- /dev/null +++ b/src/reflex/framework/IStyleable.as @@ -0,0 +1,22 @@ +package reflex.framework +{ + + public interface IStyleable + { + + function get id():String; + function set id(value:String):void; + + function get styleName():String; + function set styleName(value:String):void; + /* + function get style():Object; + function set style(value:Object):void; + */ + + function getStyle(property:String):*; + function setStyle(property:String, value:*):void; // the mxmlc compiler looks or this method specifically (no interface is required) + + } + +} diff --git a/src/reflex/framework/MeasurableImplementation.as b/src/reflex/framework/MeasurableImplementation.as new file mode 100644 index 00000000..7efbbdd3 --- /dev/null +++ b/src/reflex/framework/MeasurableImplementation.as @@ -0,0 +1,83 @@ +// ActionScript file + +private var _explicitWidth:Number; +private var _explicitHeight:Number; +protected var _measuredWidth:Number; +protected var _measuredHeight:Number; + +private var _percentWidth:Number; +private var _percentHeight:Number; + +protected var unscaledWidth:Number = 0; // 160 +protected var unscaledHeight:Number = 0; // 22 + +// IMeasurable implementation + +// these width/height setters need review in regards to scaling. +// I think I would perfer following Flex's lead here. + +/** + * @inheritDoc + */ + +[PercentProxy("percentWidth")] +[Bindable(event="widthChange")] +public function get width():Number { return unscaledWidth; } +public function set width(value:Number):void { + //unscaledWidth = _explicitWidth = value; + _explicitWidth = value; + setSize(_explicitWidth, height); +} + +/** + * @inheritDoc + */ + +[PercentProxy("percentHeight")] +[Bindable(event="heightChange")] +public function get height():Number { return unscaledHeight; } +public function set height(value:Number):void { + //unscaledHeight = _explicitHeight = value; + _explicitHeight = value; + setSize(width, _explicitHeight); +} + +/** + * @inheritDoc + */ +//[Bindable(event="explicitChange", noEvent)] +public function get explicitWidth():Number { return _explicitWidth; } +public function get explicitHeight():Number { return _explicitHeight; } + +/** + * @inheritDoc + */ +//[Bindable(event="measuredChange", noEvent)] +public function get measuredWidth():Number { return _measuredWidth; } +public function get measuredHeight():Number { return _measuredHeight; } + +/** + * @inheritDoc + */ +[Bindable(event="percentWidthChange")] +public function get percentWidth():Number { return _percentWidth; } +public function set percentWidth(value:Number):void { + notify("percentWidth", _percentWidth, _percentWidth = value); +} + +/** + * @inheritDoc + */ +[Bindable(event="percentHeightChange")] +public function get percentHeight():Number { return _percentHeight; } +public function set percentHeight(value:Number):void { + notify("percentHeight", _percentHeight, _percentHeight = value); +} + +/** + * @inheritDoc + */ +public function setSize(width:Number, height:Number):void { + if (unscaledWidth != width) { notify("width", unscaledWidth, unscaledWidth = width); } + if (unscaledHeight != height) { notify("height", unscaledHeight, unscaledHeight = height); } +} \ No newline at end of file diff --git a/src/reflex/framework/Preloader.as b/src/reflex/framework/Preloader.as new file mode 100644 index 00000000..d4f32982 --- /dev/null +++ b/src/reflex/framework/Preloader.as @@ -0,0 +1,110 @@ +package reflex.framework +{ + import flash.desktop.NativeApplication; + import flash.display.Bitmap; + import flash.display.DisplayObject; + import flash.display.MovieClip; + import flash.display.Sprite; + import flash.display.StageAlign; + import flash.display.StageScaleMode; + import flash.events.Event; + import flash.events.IOErrorEvent; + import flash.events.ProgressEvent; + import flash.utils.getDefinitionByName; + + public class Preloader extends MovieClip + { + + //[Embed(source="Default.png")] + //private var splashScreenImage:Class; + //private var splashScreen:Bitmap; + + public function Preloader() + { + stop(); + stage.scaleMode = StageScaleMode.NO_SCALE; + stage.align = StageAlign.TOP_LEFT; + + addEventListener(Event.ENTER_FRAME, onEnterFrame); + //loaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress, false, 0, true); + //loaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true); + //loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioError); + + // Show Splash Screen + //stage.addEventListener(Event.DEACTIVATE, deactivate); + //splashScreen = new splashScreenImage() as Bitmap; + //addChild( splashScreen ); + + //Resize Splash screen to fit the screen (comment this out if you don't want the graphic resized) + /*var wScale:Number = stage.stageWidth / splashScreen.width; + var hScale:Number = stage.stageHeight / splashScreen.height; + if (wScale < hScale) { + splashScreen.scaleX = wScale; + splashScreen.scaleY = wScale; + }else { + splashScreen.scaleX = hScale; + splashScreen.scaleY = hScale; + } + + //Position splash screen + splashScreen.x = (stage.stageWidth - splashScreen.width) / 2; + splashScreen.y = (stage.stageHeight - splashScreen.height) / 2; + */ + } + /* + private function ioError(e:IOErrorEvent):void + { + trace(e.text); + } + */ + + + + private function onEnterFrame(event:Event):void + { + // TODO update loader + graphics.clear(); + if(framesLoaded == totalFrames) { + this.removeEventListener(Event.ENTER_FRAME, onEnterFrame, false); + try{ + this.nextFrame(); // flex throws some weird mojo in here + } catch(e:Error) {} + + var name:String = this.currentLabels[1].name; + var APP:Class = getDefinitionByName(name) as Class; + var instance:Object = new APP(); + stage.addChild(instance as DisplayObject); + instance.initialize(null); + } else { + var percent:Number = Math.round(this.loaderInfo.bytesLoaded / this.loaderInfo.bytesTotal); + graphics.beginFill(0, 1.0); + graphics.drawRect(0, stage.stageHeight/2-10, stage.stageWidth*percent, 20); + graphics.endFill(); + } + } + /* + private function onComplete(event:Event):void + { + //removeEventListener(Event.ENTER_FRAME, enterFrame); + //loaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress); + //loaderInfo.removeEventListener(Event.COMPLETE, onComplete, false); + //loaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioError); + + // Hide SplashScreen + //removeChild( splashScreen ); + //splashScreen.bitmapData.dispose(); + //splashScreen = null; + //splashScreenImage = null; + + startup(); + } + */ + /* + private function deactivate(e:Event):void + { + // auto-close + NativeApplication.nativeApplication.exit(); + } + */ + } +} \ No newline at end of file diff --git a/src/reflex/framework/PropertyDispatcherImplementation.as b/src/reflex/framework/PropertyDispatcherImplementation.as new file mode 100644 index 00000000..9b6bca61 --- /dev/null +++ b/src/reflex/framework/PropertyDispatcherImplementation.as @@ -0,0 +1,15 @@ +import flash.events.IEventDispatcher; + +import reflex.events.DataChangeEvent; + +protected function notify(property:String, oldValue:*, newValue:*):void { + var force:Boolean = false; + var instance:IEventDispatcher = this; + if(oldValue != newValue || force) { + var eventType:String = property + "Change"; + if(instance is IEventDispatcher && (instance as IEventDispatcher).hasEventListener(eventType)) { + var event:DataChangeEvent = new DataChangeEvent(eventType, oldValue, newValue); + (instance as IEventDispatcher).dispatchEvent(event); + } + } +} \ No newline at end of file diff --git a/src/reflex/framework/StatefulImplementation.as b/src/reflex/framework/StatefulImplementation.as new file mode 100644 index 00000000..02b32a6d --- /dev/null +++ b/src/reflex/framework/StatefulImplementation.as @@ -0,0 +1,42 @@ +import reflex.states.removeState; +import reflex.states.applyState; + + +private var _states:Array; +private var _transitions:Array; +private var _currentState:String; + +// IStateful implementation + +[Bindable(event="statesChange")] +public function get states():Array { return _states; } +public function set states(value:Array):void { + notify("states", _states, _states = value); +} + +[Bindable(event="transitionsChange")] +public function get transitions():Array { return _transitions; } +public function set transitions(value:Array):void { + notify("transitions", _transitions, _transitions = value); +} + +[Bindable(event="currentStateChange")] +public function get currentState():String { return _currentState; } +public function set currentState(value:String):void { + if (_currentState == value) { + return; + } + // might need to add invalidation for this later + reflex.states.removeState(this, _currentState, states); + notify("currentState", _currentState, _currentState = value); + reflex.states.applyState(this, _currentState, states); +} + +public function hasState(state:String):Boolean { + for each(var s:Object in states) { + if(s.name == state) { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/src/reflex/framework/StyleableImplementation.as b/src/reflex/framework/StyleableImplementation.as new file mode 100644 index 00000000..6b7ad254 --- /dev/null +++ b/src/reflex/framework/StyleableImplementation.as @@ -0,0 +1,51 @@ +//import reflex.styles.Style; + +// IStyleable implementation + +private var _id:String; +private var _styleName:String; +private var _style:Style = new Style(); // might need to make object props bindable - something like ObjectProxy but lighter? + +private var _styleDeclaration:* = {}; +private var _styleManager:* = {}; + +[Bindable(event="idChange", noEvent)] +public function get id():String { return _id; } +public function set id(value:String):void { + notify("id", _id, _id = value); +} + +[Bindable(event="styleNameChange", noEvent)] +public function get styleName():String { return _styleName;} +public function set styleName(value:String):void { + notify("styleName", _styleName, _styleName= value); +} + +[Bindable(event="styleChange", noEvent)] +public function get style():Style { return _style; } +public function set style(value:*):void { // this needs expanding in the future + if (value is String) { + var token:String = value as String; + reflex.styles.parseStyles(_style, token); + } else { + throw new Error("BitmapDisplay.set style() does not currently accept a parameter of type: " + value); + } +} + +public function getStyle(property:String):* { + return style[property]; +} + +public function setStyle(property:String, value:*):void { + notify(property, style[property], style[property] = value); +} + +// the compiler goes looking for styleDeclaration and styleManager properties when setting styles on root elements +// ... but they don't really have to be anything specific :) + +public function get styleDeclaration():* { return _styleDeclaration; } +public function set styleDeclaration(value:*):void { + _styleDeclaration = value; +} + +public function get styleManager():* { return _styleManager; } \ No newline at end of file diff --git a/src/reflex/graphics/BitmapImage.as b/src/reflex/graphics/BitmapImage.as new file mode 100644 index 00000000..0ff54f26 --- /dev/null +++ b/src/reflex/graphics/BitmapImage.as @@ -0,0 +1,215 @@ +package reflex.graphics +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.IBitmapDrawable; + import flash.display.Loader; + import flash.events.Event; + import flash.geom.Matrix; + import flash.net.URLRequest; + import flash.system.LoaderContext; + + import reflex.display.MeasurableItem; + import reflex.invalidation.Invalidation; + import reflex.invalidation.LifeCycle; + import reflex.metadata.resolveCommitProperties; + + + public class BitmapImage extends MeasurableItem + { + + static public const BEST_FIT:String = "bestFit"; + static public const BEST_FILL:String = "bestFill"; + static public const HORIZONTAL_FIT:String = "horizontalFit"; + static public const VERTICAL_FIT:String = "verticalFit"; + static public const SKEW:String = "skew"; + + private var loader:Loader; + private var original:BitmapData; + private var bitmap:Bitmap; + + private var _source:Object; + private var _scaling:String = BEST_FILL; + private var _backgroundColor:uint = 0xFFFFFF; + private var _backgroundAlpha:Number = 0; + + private var sourceChanged:Boolean; + + override public function set enabled(value:Boolean):void { + super.enabled = value; + if(display) { display.alpha = value ? 1 : 0.5; } + } + + [Bindable(event="sourceChange")] + public function get source():Object { return _source; } + public function set source(value:Object):void { + sourceChanged = true; + notify("source", _source, _source = value); + invalidate(LifeCycle.COMMIT); + } + + [Bindable(event="scalingChanged")] + public function get scaling():String { return _scaling; } + public function set scaling(value:String):void { + notify("scaling", _scaling, _scaling = value); + } + + [Bindable(event="backgroundColorChanged")] + public function get backgroundColor():uint { return _backgroundColor; } + public function set backgroundColor(value:uint):void { + notify("backgroundColor", _backgroundColor, _backgroundColor = value); + } + + [Bindable(event="backgroundAlphaChanged")] + public function get backgroundAlpha():Number { return _backgroundAlpha; } + public function set backgroundAlpha(value:Number):void { + notify("backgroundAlpha", _backgroundAlpha, _backgroundAlpha = value); + } + + public function BitmapImage() + { + super(); + //reflex.metadata.resolveCommitProperties(this, resolve); + //this.addEventListener(LifeCycle.COMMIT, onInvalidate); + } + + override protected function initialize():void { + super.initialize(); + display.alpha = enabled ? 1 : 0.5; + } + + override protected function onCommit():void { + super.onCommit(); + if(sourceChanged) { + updateSource(); + sourceChanged = false; + } + } + + override protected function onLayout():void { + super.onLayout(); + draw(); + } + + /** + * @private + */ + //[Commit(properties="source")] + public function updateSource():void { + if (source is String) { + var request:URLRequest = new URLRequest(source as String); + loader = new Loader(); + loader.load(request, new LoaderContext(true)); + loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true); + } else if (source is Class) { + var display:Object = new (source as Class)(); + if(display is Bitmap) { + //var display:Bitmap = new (source as Class)(); + _measuredWidth = display.width; + _measuredHeight = display.height; + this.setSize(_measuredWidth, _measuredHeight); + original = display.bitmapData; + } else if(display is IBitmapDrawable) { + var bitmap:BitmapData = new BitmapData(display.width, display.height, true, 0); + bitmap.draw(display as IBitmapDrawable); + _measuredWidth = bitmap.width; + _measuredHeight = bitmap.height; + this.setSize(_measuredWidth, _measuredHeight); + original = bitmap; + } + draw(); + } else if (source is BitmapData) { + var bitmapdata:BitmapData = source as BitmapData; + _measuredWidth = bitmapdata.width; + _measuredHeight = bitmapdata.height; + this.setSize(_measuredWidth, _measuredHeight); + original = bitmapdata; + draw(); + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + } else { + original = null; + draw(); + } + } + + private function onComplete(event:Event):void { + _measuredWidth = loader.content.width; + _measuredHeight = loader.content.height; + original = (loader.content as Bitmap).bitmapData; + draw(); + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + } + + /** + * @private + */ + //[Commit(properties="width, height, scaling, backgroundColor, backgroundAlpha")] + public function onSizeChange(event:Event):void { + var color:uint = (_backgroundAlpha*255) << 24 | _backgroundColor + //this.bitmapData = new BitmapData(unscaledWidth, unscaledHeight, true, color); + //this.smoothing = true; + draw(); + } + + private function draw():void { + if(display == null) { return; } + if(bitmap == null) { + bitmap = new Bitmap(); + display.addChild(bitmap); + } + if(bitmap.bitmapData == null) { + bitmap.bitmapData = new BitmapData(width, height, true, 0); + } + + var bitmapData:BitmapData = bitmap.bitmapData; + bitmapData.fillRect(bitmapData.rect, 0x00000000); + if (original) { + + var mode:String = _scaling; + var matrix:Matrix; + + var originalRatio:Number = original.width/original.height; + var bitmapRatio:Number = unscaledWidth/unscaledHeight; + + if (_scaling == BEST_FIT) { + if (originalRatio > bitmapRatio) { + mode = HORIZONTAL_FIT; + } else { + mode = VERTICAL_FIT; + } + } else if (_scaling == BEST_FILL) { + if (originalRatio > bitmapRatio) { + mode = VERTICAL_FIT; + } else { + mode = HORIZONTAL_FIT; + } + } + + if (mode == HORIZONTAL_FIT) { + var hs:Number = unscaledWidth/original.width; + matrix = new Matrix(hs, 0, 0, hs, 0, (original.height*hs - unscaledHeight)/2 * -1); + } else if (mode == VERTICAL_FIT) { + var vs:Number = unscaledHeight/original.height; + matrix = new Matrix(vs, 0, 0, vs, (original.width*vs - unscaledWidth)/2 * -1, 0); + } else if (mode == SKEW) { + matrix = new Matrix(unscaledWidth/original.width, 0, 0, unscaledHeight/original.height, 0, 0); + } + + //bitmapData.floodFill(1000, 1000, 0x00000000); + //bitmapData.fillRect(bitmapData.rect, 0x00000000); + bitmapData.draw(original, matrix, null, null, null, true); + } else { + //bitmapData.floodFill(0, 0, 0); + //bitmapData.fillRect(bitmapData.rect, 0x00000000); + } + } + + private function resolve(m:String):* { + var t:* = this[m]; + return t; + } + + } +} \ No newline at end of file diff --git a/src/reflex/graphics/Ellipse.as b/src/reflex/graphics/Ellipse.as new file mode 100644 index 00000000..8160f01f --- /dev/null +++ b/src/reflex/graphics/Ellipse.as @@ -0,0 +1,70 @@ +package reflex.graphics +{ + + import flash.display.DisplayObject; + import flash.display.Graphics; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + import flash.geom.Point; + import flash.geom.Rectangle; + + import mx.events.PropertyChangeEvent; + import mx.graphics.IFill; + import mx.graphics.IStroke; + + import reflex.display.MeasurableItem; + import reflex.framework.IMeasurablePercent; + import reflex.framework.IStyleable; + import reflex.invalidation.Invalidation; + import reflex.invalidation.LifeCycle; + import reflex.metadata.resolveCommitProperties; + + + public class Ellipse extends GraphicItem implements IGraphicItem //extends GraphicBase implements IDrawable + { + + //public var owner:Object; + + //static public const RENDER:String = "render"; + //Invalidation.registerPhase(RENDER, Event, 0); + + + override protected function onLayout():void { + super.onLayout(); + var g:Graphics = this.display.graphics; + //var graphics:Vector. = reflex.graphics.resolveGraphics(target); + //for each(var g:Graphics in graphics) { + g.clear(); + drawTo(g); + //} + } + + private function drawTo(graphics:Graphics):void { + if(width > 0 && height > 0) { + var rectangle:Rectangle = new Rectangle(0, 0, width, height); + if(stroke != null) { + stroke.apply(graphics, rectangle, new Point()); + } else { + graphics.lineStyle(0, 0, 0); + } + if(fill != null) { + fill.begin(graphics, rectangle, new Point()); + } else { + graphics.beginFill(0, 0); + } + if(width == height) { + graphics.drawCircle(width/2, width/2, width/2); + } else { + graphics.drawEllipse(0, 0, width, height); + } + if(fill != null) { + fill.end(graphics); + } else { + graphics.endFill(); + } + } + } + + } +} \ No newline at end of file diff --git a/src/reflex/graphics/GraphicItem.as b/src/reflex/graphics/GraphicItem.as new file mode 100644 index 00000000..70755ff1 --- /dev/null +++ b/src/reflex/graphics/GraphicItem.as @@ -0,0 +1,64 @@ +package reflex.graphics +{ + import flash.events.Event; + import flash.events.IEventDispatcher; + + import mx.events.PropertyChangeEvent; + import mx.graphics.IFill; + import mx.graphics.IStroke; + + import reflex.display.MeasurableItem; + import reflex.invalidation.LifeCycle; + + public class GraphicItem extends MeasurableItem + { + + private var _fill:IFill; + private var _stroke:IStroke; + + [Bindable(event="fillChange")] + public function get fill():IFill { return _fill; } + public function set fill(value:IFill):void { + var fillDispatcher:IEventDispatcher = _fill as IEventDispatcher; + if(fillDispatcher) { + fillDispatcher.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, fillChangeHandler, false); + } + notify("fill", _fill, _fill = value); + fillDispatcher = _fill as IEventDispatcher; + if(fillDispatcher) { + fillDispatcher.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, fillChangeHandler, false, 0, true); + } + invalidate(LifeCycle.LAYOUT); + } + + [Bindable(event="strokeChange")] + public function get stroke():IStroke { return _stroke; } + public function set stroke(value:IStroke):void { + var strokeDispatcher:IEventDispatcher = _stroke as IEventDispatcher; + if(strokeDispatcher) { + strokeDispatcher.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, strokeChangeHandler, false); + } + notify("stroke", _stroke, _stroke = value); + strokeDispatcher = _stroke as IEventDispatcher; + if(strokeDispatcher) { + strokeDispatcher.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, strokeChangeHandler, false, 0, true); + } + invalidate(LifeCycle.LAYOUT); + } + + override public function setSize(width:Number, height:Number):void { + super.setSize(width, height); + //animationType = AnimationType.RESIZE; + invalidate(LifeCycle.LAYOUT); + } + + private function fillChangeHandler(event:Event):void { + invalidate(LifeCycle.LAYOUT); + } + + private function strokeChangeHandler(event:Event):void { + invalidate(LifeCycle.LAYOUT); + } + + } +} \ No newline at end of file diff --git a/src/reflex/graphics/IDrawable.as b/src/reflex/graphics/IDrawable.as deleted file mode 100644 index 02af4961..00000000 --- a/src/reflex/graphics/IDrawable.as +++ /dev/null @@ -1,12 +0,0 @@ -package reflex.graphics -{ - import flash.display.Graphics; - - public interface IDrawable - { - - function set target(value:Object):void; - function render():void; - - } -} \ No newline at end of file diff --git a/src/reflex/graphics/IGraphicItem.as b/src/reflex/graphics/IGraphicItem.as new file mode 100644 index 00000000..4d28b3d0 --- /dev/null +++ b/src/reflex/graphics/IGraphicItem.as @@ -0,0 +1,19 @@ +package reflex.graphics +{ + import mx.graphics.IFill; + import mx.graphics.IStroke; + + public interface IGraphicItem + { + + function get display():Object; + function set display(value:Object):void; + + //public function get fill():IFill; + //public function set fill(value:IFill):void; + + //public function get stroke():IStroke; + //public function set stroke(value:IStroke):void; + + } +} \ No newline at end of file diff --git a/src/reflex/graphics/Line.as b/src/reflex/graphics/Line.as new file mode 100644 index 00000000..b1199acd --- /dev/null +++ b/src/reflex/graphics/Line.as @@ -0,0 +1,105 @@ +package reflex.graphics +{ + + import flash.display.Graphics; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + import flash.geom.Point; + import flash.geom.Rectangle; + + import mx.events.PropertyChangeEvent; + import mx.graphics.IStroke; + + import reflex.display.MeasurableItem; + import reflex.framework.IMeasurablePercent; + import reflex.framework.IStyleable; + import reflex.invalidation.LifeCycle; + import reflex.metadata.resolveCommitProperties; + + + public class Line extends MeasurableItem implements IGraphicItem //GraphicBase implements IDrawable + { + + //public var owner:Object; + + private var _xFrom:Number; + private var _yFrom:Number; + + private var _xTo:Number; + private var _yTo:Number; + + private var _stroke:IStroke; + + [Bindable(event="xFromChange")] + public function get xFrom():Number { return isNaN(_xFrom) ? 0 : _xFrom; } + public function set xFrom(value:Number):void { + notify("xFrom", _xFrom, _xFrom = value); + } + + [Bindable(event="yFromChange")] + public function get yFrom():Number { return isNaN(_yFrom) ? 0 : _yFrom } + public function set yFrom(value:Number):void { + notify("yFrom", _yFrom, _yFrom = value); + } + + [Bindable(event="xToChange")] + public function get xTo():Number { return isNaN(_xTo) ? unscaledWidth : _xTo; } + public function set xTo(value:Number):void { + notify("xTo", _xTo, _xTo = value); + } + + [Bindable(event="yToChange")] + public function get yTo():Number { return isNaN(_yTo) ? unscaledHeight : _yTo; } + public function set yTo(value:Number):void { + notify("yTo", _yTo, _yTo = value); + } + + + [Bindable(event="strokeChange")] + public function get stroke():IStroke { return _stroke; } + public function set stroke(value:IStroke):void { + var strokeDispatcher:IEventDispatcher = _stroke as IEventDispatcher; + if(strokeDispatcher) { + strokeDispatcher.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, strokeChangeHandler, false); + } + notify("stroke", _stroke, _stroke = value); + strokeDispatcher = _stroke as IEventDispatcher; + if(strokeDispatcher) { + strokeDispatcher.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, strokeChangeHandler, false, 0, true); + } + invalidate(LifeCycle.LAYOUT); + } + + private function strokeChangeHandler(event:Event):void { + invalidate(LifeCycle.LAYOUT); + } + + override protected function onLayout():void { + super.onLayout(); + var g:Graphics = display.graphics; + //var graphics:Vector. = reflex.graphics.resolveGraphics(target); + //for each(var g:Graphics in graphics) { + g.clear(); // fix later + drawTo(g); + //} + } + + private function drawTo(graphics:Graphics):void { + // these should be precalculated later + var xf:Number = xFrom; // isNaN(xFrom) ? x : xFrom; + var yf:Number = yFrom; // isNaN(yFrom) ? y : yFrom; + var xt:Number = xTo; // isNaN(xTo) ? x+width : xTo; + var yt:Number = yTo; // isNaN(yTo) ? y+height : yTo; + var rectangle:Rectangle = new Rectangle(0, 0, Math.max(xt-xf, 1), Math.max(yt-yf, 1)); + if(stroke != null) { + stroke.apply(graphics, rectangle, new Point(xf, yf)); + } else { + graphics.lineStyle(0, 0, 0); + } + graphics.moveTo(xf, yf); + graphics.lineTo(xt, yt); + } + + } +} \ No newline at end of file diff --git a/src/reflex/graphics/Path.as b/src/reflex/graphics/Path.as new file mode 100644 index 00000000..748273de --- /dev/null +++ b/src/reflex/graphics/Path.as @@ -0,0 +1,65 @@ +package reflex.graphics +{ + + import flash.display.Graphics; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + import flash.geom.Point; + import flash.geom.Rectangle; + + import mx.events.PropertyChangeEvent; + + import reflex.framework.IMeasurablePercent; + import reflex.framework.IStyleable; + import reflex.metadata.resolveCommitProperties; + + public class Path extends GraphicItem implements IGraphicItem + { + + private var _data:String; + + + [Bindable(event="dataChange")] + public function get data():String { return _data; } + public function set data(value:String):void { + notify("data", _data, _data = value); + } + + + override protected function onLayout():void { + super.onLayout(); + var g:Graphics = display.graphics; + //var graphics:Vector. = reflex.graphics.resolveGraphics(target); + //for each(var g:Graphics in graphics) { + g.clear(); + drawTo(g); + //} + } + + private function drawTo(graphics:Graphics):void { + if(width > 0 && height > 0) { + var rectangle:Rectangle = new Rectangle(0, 0, width, height); + if(stroke != null) { + stroke.apply(graphics, rectangle, new Point()); + } else { + graphics.lineStyle(0, 0, 0); + } + if(fill != null) { + fill.begin(graphics, rectangle, new Point()); + } else { + graphics.beginFill(0, 0); + } + + //graphics.drawPath(); + + if(fill != null) { + fill.end(graphics); + } else { + graphics.endFill(); + } + } + } + + } +} \ No newline at end of file diff --git a/src/reflex/graphics/Rect.as b/src/reflex/graphics/Rect.as index a9f79fa0..db727ef9 100644 --- a/src/reflex/graphics/Rect.as +++ b/src/reflex/graphics/Rect.as @@ -1,106 +1,96 @@ package reflex.graphics { - import __AS3__.vec.Vector; import flash.display.DisplayObject; import flash.display.Graphics; + import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; import flash.geom.Point; import flash.geom.Rectangle; import mx.events.PropertyChangeEvent; + import mx.graphics.IFill; + import mx.graphics.IStroke; - import reflex.events.InvalidationEvent; + import reflex.display.MeasurableItem; + import reflex.framework.IMeasurablePercent; + import reflex.framework.IStyleable; + import reflex.invalidation.Invalidation; + import reflex.invalidation.LifeCycle; + import reflex.metadata.resolveCommitProperties; - [Style(name="left")] - [Style(name="right")] - [Style(name="top")] - [Style(name="bottom")] - [Style(name="horizontalCenter")] - [Style(name="verticalCenter")] - [Style(name="dock")] - [Style(name="align")] - public class Rect extends EventDispatcher implements IDrawable + public class Rect extends GraphicItem //extends GraphicBase implements IDrawable { - [Bindable] public var x:Number = 0; - [Bindable] public var y:Number = 0; + //public var owner:Object; - [Bindable] public var style:Object = new Object(); + //static public const RENDER:String = "render"; + //Invalidation.registerPhase(RENDER, Event, 0); - public function setStyle(property:String, value:*):void { - style[property] = value; - } + private var _radiusX:Number = 0; + private var _radiusY:Number = 0; - private var _width:Number = 0; [Bindable] - public function get width():Number { return _width; } - public function set width(value:Number):void { - _width = value; - render(); // add invalidation later - } - private var _height:Number = 0; [Bindable] - public function get height():Number { return _height; } - public function set height(value:Number):void { - _height = value; - render(); - } - public function setSize(width:Number, height:Number):void { - _width = width; - _height = height; - render(); + [Bindable(event="radiusXChange")] + public function get radiusX():Number { return _radiusX; } + public function set radiusX(value:Number):void { + notify("radiusX", _radiusX, _radiusX = value); } - private var _fill:*; - public function get fill():* { return _fill; } - public function set fill(value:*):void { - _fill = value; - // update this to use binding correctly - (_fill as IEventDispatcher).addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChangeHandler); - render(); + [Bindable(event="radiusYChange")] + public function get radiusY():Number { return _radiusY; } + public function set radiusY(value:Number):void { + notify("radiusY", _radiusY, _radiusY = value); } - private var _stroke:*; - public function get stroke():* { return _stroke; } - public function set stroke(value:*):void { - _stroke = value; - render(); - } + // topLeftRadiusX + // topLeftRadiusY + // topRightRadiusX + // topRightRadiusY + // bottomRightRadiusX + // botomRightRadiusY + // bottomLeftRadiusX + // bottomLeftRadiusY - private var _target:Object; - public function get target():Object { return _target; } - public function set target(value:Object):void { - _target = value; - render(); - } - public function Rect(target:Object = null) - { - this.target = target; - } - public function render():void { - var graphics:Vector. = reflex.graphics.resolveGraphics(target); - for each(var g:Graphics in graphics) { + override protected function onLayout():void { + super.onLayout(); + var g:Graphics = this.display.graphics; + //var graphics:Vector. = reflex.graphics.resolveGraphics(target); + //for each(var g:Graphics in graphics) { + g.clear(); // will fix this later drawTo(g); - } + //} } private function drawTo(graphics:Graphics):void { if(width > 0 && height > 0) { var rectangle:Rectangle = new Rectangle(0, 0, width, height); - if(stroke != null) { stroke.apply(graphics, rectangle, new Point()); } - if(fill != null) { fill.begin(graphics, rectangle, new Point()); } - graphics.drawRect(x, y, width, height); - if(fill != null) { fill.end(graphics); } + if(stroke != null) { + stroke.apply(graphics, rectangle, new Point()); + } else { + graphics.lineStyle(0, 0, 0); + } + if(fill != null) { + fill.begin(graphics, rectangle, new Point()); + } else { + graphics.beginFill(0, 0); + } + if(_radiusX > 0 || _radiusY > 0) { + graphics.drawRoundRect(0, 0, width, height, _radiusX*2, _radiusY*2); + } else { + graphics.drawRect(0, 0, width, height); + } + if(fill != null) { + fill.end(graphics); + } else { + graphics.endFill(); + } } } - private function propertyChangeHandler(event:PropertyChangeEvent):void { - render(); - } - } } \ No newline at end of file diff --git a/src/reflex/graphics/Spacer.as b/src/reflex/graphics/Spacer.as new file mode 100644 index 00000000..7104cd44 --- /dev/null +++ b/src/reflex/graphics/Spacer.as @@ -0,0 +1,9 @@ +package reflex.graphics +{ + import reflex.display.MeasurableItem; + + public class Spacer extends MeasurableItem + { + + } +} \ No newline at end of file diff --git a/src/reflex/graphics/attributes/Angle.as b/src/reflex/graphics/attributes/Angle.as deleted file mode 100644 index 3663cca9..00000000 --- a/src/reflex/graphics/attributes/Angle.as +++ /dev/null @@ -1,61 +0,0 @@ -package reflex.graphics.attributes -{ - /** - * @private - * Utility class that helps transform between degrees and radians. The - * default angle is radians. - */ - public class Angle - { - /** - * Takes a string value of an angle (e.g. deg(90)) and converts it into - * radians. - */ - public static function fromString(angle:String):Number - { - var parts:Array = angle.match(/(\d*.?\d+)(\w*)/); - var value:Number = parseFloat(parts[1]); - if (isNaN(value)) { - return 0; - } - var unit:String = parts[2] || "deg"; - if (unit == "deg") { - value = fromDegrees(value); - } else if (unit == "grad") { - value = fromGrads(value); - } else if (unit != "rad") { - return 0; // syntax error - } - - return value; - } - - - /** - * Converts grads to radians, for use with Transform rotations. - */ - public static function fromGrads(grads:Number):Number - { - return grads * Math.PI/200; - } - - - /** - * Converts degrees to radians, for use with Transform rotations. - */ - public static function fromDegrees(degrees:Number):Number - { - return degrees * Math.PI/180; - } - - /** - * Converts radians to degrees, for use with Transform rotations. - */ - public static function toDegree(radians:Number):Number - { - return radians * 180/Math.PI; - } - - - } -} \ No newline at end of file diff --git a/src/reflex/graphics/attributes/Color.as b/src/reflex/graphics/attributes/Color.as deleted file mode 100644 index bbabb962..00000000 --- a/src/reflex/graphics/attributes/Color.as +++ /dev/null @@ -1,226 +0,0 @@ -package reflex.graphics.attributes -{ - /** - * @private - * Converts string representations of a color to the uint value. - */ - public class Color - { - - private static var regex1:RegExp = /^#([a-zA-Z0-9]{6}|[a-zA-Z0-9]{3})$/; - private static var regex2:RegExp = /^rgb\((\d{1,3})(%?),(\d{1,3})\2,(\d{1,3})\2\)$/; - - /** - * Converts a string representation of a color into an int that can be - * use in fills, strokes, etc. - * - * @param The color string. - * @return A valid color uint - */ - public static function fromString(color:String):uint - { - color = color.replace(/\s/g, ""); - var result:uint; - var matches:Array; - - // traditional #FFFFFF representation of the color - if ( (matches = color.match(regex1)) ) { - color = matches[1]; - - // turn an rgb value to an rrggbb value - if (color.length == 3) { - color = color.charAt(0) + color.charAt(0) + color.charAt(1) + color.charAt(1) + color.charAt(2) + color.charAt(2); - } - - return parseInt(color, 16); - } else if ( (matches = color.match(regex2)) ) { - var isPercent:Boolean = matches[2] == "%"; - - // remove the % group in the array for simplicity - matches.splice(2, 1); - - result = 0; - - for (var i:uint = 1; i <= 3; i++) { - var part:uint = isPercent ? Number(matches[i])/100*255 : Number(matches[i]); - for (var j:uint = i; j < 3; j++) { - part <<= 8; - } - - result = result | part; - } - - return result; - } else if (color.toLowerCase() in colorNames) { - return colorNames[color.toLowerCase()]; - } else if ( !isNaN(result = parseInt(color)) ) { - return result; - } - - return 0; - } - - - public static function toString(value:uint):String - { - var clr:String = value.toString(16); - while (clr.length < 6) { - clr = "0" + clr; - } - return "#" + clr; - } - - - - public static const colorNames:Object = { - aliceblue: 0xf0f8ff, - antiquewhite: 0xfaebd7, - aqua: 0x00ffff, - aquamarine: 0x7fffd4, - azure: 0xf0ffff, - beige: 0xf5f5dc, - bisque: 0xffe4c4, - black: 0x000000, - blanchedalmond: 0xffebcd, - blue: 0x0000ff, - blueviolet: 0x8a2be2, - brown: 0xa52a2a, - burlywood: 0xdeb887, - cadetblue: 0x5f9ea0, - chartreuse: 0x7fff00, - chocolate: 0xd2691e, - coral: 0xff7f50, - cornflowerblue: 0x6495ed, - cornsilk: 0xfff8dc, - crimson: 0xdc143c, - cyan: 0x00ffff, - darkblue: 0x00008b, - darkcyan: 0x008b8b, - darkgoldenrod: 0xb8860b, - darkgray: 0xa9a9a9, - darkgreen: 0x006400, - darkgrey: 0xa9a9a9, - darkkhaki: 0xbdb76b, - darkmagenta: 0x8b008b, - darkolivegreen: 0x556b2f, - darkorange: 0xff8c00, - darkorchid: 0x9932cc, - darkred: 0x8b0000, - darksalmon: 0xe9967a, - darkseagreen: 0x8fbc8f, - darkslateblue: 0x483d8b, - darkslategray: 0x2f4f4f, - darkslategrey: 0x2f4f4f, - darkturquoise: 0x00ced1, - darkviolet: 0x9400d3, - deeppink: 0xff1493, - deepskyblue: 0x00bfff, - dimgray: 0x696969, - dimgrey: 0x696969, - dodgerblue: 0x1e90ff, - firebrick: 0xb22222, - floralwhite: 0xfffaf0, - forestgreen: 0x228b22, - fuchsia: 0xff00ff, - gainsboro: 0xdcdcdc, - ghostwhite: 0xf8f8ff, - gold: 0xffd700, - goldenrod: 0xdaa520, - gray: 0x808080, - grey: 0x808080, - green: 0x008000, - greenyellow: 0xadff2f, - honeydew: 0xf0fff0, - hotpink: 0xff69b4, - indianred: 0xcd5c5c, - indigo: 0x4b0082, - ivory: 0xfffff0, - khaki: 0xf0e68c, - lavender: 0xe6e6fa, - lavenderblush: 0xfff0f5, - lawngreen: 0x7cfc00, - lemonchiffon: 0xfffacd, - lightblue: 0xadd8e6, - lightcoral: 0xf08080, - lightcyan: 0xe0ffff, - lightgoldenrodyellow: 0xfafad2, - lightgray: 0xd3d3d3, - lightgreen: 0x90ee90, - lightgrey: 0xd3d3d3, - lightpink: 0xffb6c1, - lightsalmon: 0xffa07a, - lightseagreen: 0x20b2aa, - lightskyblue: 0x87cefa, - lightslategray: 0x778899, - lightslategrey: 0x778899, - lightsteelblue: 0xb0c4de, - lightyellow: 0xffffe0, - lime: 0x00ff00, - limegreen: 0x32cd32, - linen: 0xfaf0e6, - magenta: 0xff00ff, - maroon: 0x800000, - mediumaquamarine: 0x66cdaa, - mediumblue: 0x0000cd, - mediumorchid: 0xba55d3, - mediumpurple: 0x9370db, - mediumseagreen: 0x3cb371, - mediumslateblue: 0x7b68ee, - mediumspringgreen: 0x00fa9a, - mediumturquoise: 0x48d1cc, - mediumvioletred: 0xc71585, - midnightblue: 0x191970, - mintcream: 0xf5fffa, - mistyrose: 0xffe4e1, - moccasin: 0xffe4b5, - navajowhite: 0xffdead, - navy: 0x000080, - oldlace: 0xfdf5e6, - olive: 0x808000, - olivedrab: 0x6b8e23, - orange: 0xffa500, - orangered: 0xff4500, - orchid: 0xda70d6, - palegoldenrod: 0xeee8aa, - palegreen: 0x98fb98, - paleturquoise: 0xafeeee, - palevioletred: 0xdb7093, - papayawhip: 0xffefd5, - peachpuff: 0xffdab9, - peru: 0xcd853f, - pink: 0xffc0cb, - plum: 0xdda0dd, - powderblue: 0xb0e0e6, - purple: 0x800080, - red: 0xff0000, - rosybrown: 0xbc8f8f, - royalblue: 0x4169e1, - saddlebrown: 0x8b4513, - salmon: 0xfa8072, - sandybrown: 0xf4a460, - seagreen: 0x2e8b57, - seashell: 0xfff5ee, - sienna: 0xa0522d, - silver: 0xc0c0c0, - skyblue: 0x87ceeb, - slateblue: 0x6a5acd, - slategray: 0x708090, - slategrey: 0x708090, - snow: 0xfffafa, - springgreen: 0x00ff7f, - steelblue: 0x4682b4, - tan: 0xd2b48c, - teal: 0x008080, - thistle: 0xd8bfd8, - tomato: 0xff6347, - turquoise: 0x40e0d0, - violet: 0xee82ee, - wheat: 0xf5deb3, - white: 0xffffff, - whitesmoke: 0xf5f5f5, - yellow: 0xffff00, - yellowgreen: 0x9acd32 - } - - } -} \ No newline at end of file diff --git a/src/reflex/graphics/attributes/CornerRadius.as b/src/reflex/graphics/attributes/CornerRadius.as deleted file mode 100644 index efbae1a4..00000000 --- a/src/reflex/graphics/attributes/CornerRadius.as +++ /dev/null @@ -1,99 +0,0 @@ -package reflex.graphics.attributes -{ - - /** - * @private - **/ - public final class CornerRadius - { - public var topLeft:Number; - public var topRight:Number; - public var bottomLeft:Number; - public var bottomRight:Number; - - private static var pool:CornerRadius; - private var next:CornerRadius; - - public function CornerRadius(topLeft:Number = 0, topRight:Number = 0, bottomLeft:Number = 0, bottomRight:Number = 0) - { - this.topLeft = topLeft; - this.topRight = topRight; - this.bottomLeft = bottomLeft; - this.bottomRight = bottomRight; - } - - public function inset(delta:Number):void - { - topLeft = Math.max(0, topLeft - delta); - topRight = Math.max(0, topRight - delta); - bottomLeft = Math.max(0, bottomLeft - delta); - bottomRight = Math.max(0, bottomRight - delta); - } - - public function allZero():Boolean - { - return topLeft == 0 && topRight == 0 && bottomLeft == 0 && bottomRight == 0; - } - - public function equals(toCompare:CornerRadius):Boolean - { - return (toCompare.topLeft == topLeft && - toCompare.topRight == topRight && - toCompare.bottomLeft == bottomLeft && - toCompare.bottomRight == bottomRight); - } - - public function clone():CornerRadius - { - return new CornerRadius(topLeft, topRight, bottomLeft, bottomRight); - } - - public function dispose():void - { - topLeft = 0; - topRight = 0; - bottomLeft = 0; - bottomRight = 0; - next = pool; - pool = this; - } - - - private static var numberRegex:RegExp = new RegExp("\\s+", "g"); - - public static function fromString(value:String):CornerRadius - { - var corners:CornerRadius = pool || new CornerRadius(); - pool = corners.next; - - if (!value) return corners; - - var numbers:Array = value.split(numberRegex); - var n:int = numbers.length; - for (var i:int = 0; i < n; i++) { - numbers[i] = Length.fromString(numbers[i]); - } - - if (n == 1) { - corners.topLeft = corners.topRight = corners.bottomLeft = corners.bottomRight = numbers[0]; - } else if (n == 2) { - corners.topLeft = corners.topRight = numbers[0]; - corners.bottomLeft = corners.bottomRight = numbers[1]; - } else if (n == 4) { - corners.topLeft = numbers[0]; - corners.topRight = numbers[1]; - corners.bottomLeft = numbers[2]; - corners.bottomRight = numbers[3]; - } else { - throw new ArgumentError("Corners require 1, 2, or 4 Length parameters"); - } - - return corners; - } - - public function toString():String - { - return '[Edges(topLeft="' + topLeft + ', topRight="' + topRight + '", bottomLeft="' + bottomLeft + '", bottomRight="' + bottomRight + '")]'; - } - } -} \ No newline at end of file diff --git a/src/reflex/graphics/attributes/Length.as b/src/reflex/graphics/attributes/Length.as deleted file mode 100644 index fd6718cd..00000000 --- a/src/reflex/graphics/attributes/Length.as +++ /dev/null @@ -1,61 +0,0 @@ -package reflex.graphics.attributes -{ - - /** - * @private - **/ - public class Length - { - - private static var regex1:RegExp = /^(-?[\d.]+)(px)?$/; - private static var regex2:RegExp = /^(-?[\d.]+)(in|cm|mm|pt)$/; - private static var dpi:Number = 72 - - - /** - * Takes a length value (e.g. x, y, width, height) and returns the pixel - * value. Does NOT support % or "em" lengths - */ - public static function fromString(value:String):Number - { - // strip any whitespace - value = value.replace(/\s/g, ""); - - if (value == '') { - return 0; - } - - var matches:Array; - - if ( (matches = value.match(regex1)) ) { - return Number(matches[1]); - } else if ( (matches = value.match(regex2)) ) { - if (matches[2] == "in") { - return Number(matches[1])*dpi; // 1 inch in dpi pixels - } else if (matches[2] == "cm") { - return Number(matches[1])*dpi/2.54; // 2.54 cm in 1 inch - } else if (matches[2] == "mm") { - return Number(matches[1])*dpi/2.54/10; // 10 mm in 1 cm or 0.254 mm in 1 inch - } else if (matches[2] == "pt") { - return Number(matches[1]); // 72 points in 1 inch - } else if (matches[2] == "pc") { - return Number(matches[1])*12; // 1 pica in 12 points - } - } - - return 0; - } - - - /** - * Converts a number into a length string. - * - * @return The length string. - */ - public static function toString(value:Number):String - { - return value + "px"; - } - - } -} \ No newline at end of file diff --git a/src/reflex/injection/HardCodedInjector.as b/src/reflex/injection/HardCodedInjector.as new file mode 100644 index 00000000..e2602906 --- /dev/null +++ b/src/reflex/injection/HardCodedInjector.as @@ -0,0 +1,151 @@ +package reflex.injection +{ + + import flash.display.Bitmap; + import flash.display.Sprite; + import flash.display.Stage; + import flash.events.Event; + import flash.events.IEventDispatcher; + + import reflex.animation.Animator; + import reflex.animation.IAnimator; + import reflex.behaviors.ButtonBehavior; + import reflex.behaviors.SelectBehavior; + import reflex.binding.Bind; + import reflex.collections.SimpleCollection; + import reflex.components.Button; + import reflex.components.Component; + import reflex.components.List; + import reflex.components.ListItem; + import reflex.components.Scroller; + import reflex.containers.Application; + import reflex.containers.Container; + import reflex.containers.Group; + import reflex.containers.HGroup; + import reflex.containers.IContainer; + import reflex.containers.VGroup; + import reflex.data.Position; + import reflex.display.FlashDisplayHelper; + import reflex.display.IDisplayHelper; + import reflex.display.MeasurableItem; + import reflex.display.StyleableItem; + import reflex.graphics.BitmapImage; + import reflex.invalidation.IReflexInvalidation; + import reflex.invalidation.Invalidation; + import reflex.invalidation.LifeCycle; + import reflex.layouts.BasicLayout; + import reflex.layouts.HorizontalLayout; + import reflex.layouts.VerticalLayout; + import reflex.skins.ButtonSkin; + import reflex.skins.ListItemSkin; + import reflex.skins.ListSkin; + import reflex.skins.ScrollerSkin; + import reflex.skins.Skin; + + /** + * Default Injector is hard coded right now. + * - the idea is that we can create a more dynamic injector later + * - and allow devs to wrap their favorite 3rd party injector using the interface + **/ + public class HardCodedInjector implements IReflexInjector + { + + private var invalidation:Invalidation = new Invalidation(); + private var animator:IAnimator = new Animator(); + private var helper:IDisplayHelper = new FlashDisplayHelper(); + + public function initialize(stage:Stage, app:IEventDispatcher):void { + invalidation.stage = stage; + invalidation.app = app; + invalidation.registerPhase(LifeCycle.INITIALIZE, Event, 400, false); + invalidation.registerPhase(LifeCycle.COMMIT, Event, 300, false); + invalidation.registerPhase(LifeCycle.MEASURE, Event, 200, true); + invalidation.registerPhase(LifeCycle.LAYOUT, Event, 100, false); + } + + public function injectInto(instance:Object):void { + // move concrete references out? - later + // inject on metadata? - later + // 3rd party injection? - later + + if(instance is Container || + instance is Component) { + if(instance.injector == null) { instance.injector = this; } + + } + + if(instance is Container) { + if(instance.animator == null) { instance.animator = animator; } + } + + if(instance is StyleableItem) { + if(instance.helper == null) { instance.helper = helper; } + } + + if(instance is MeasurableItem) { + if(instance.invalidation == null) { instance.invalidation = invalidation; } + } + /* + if(instance is BitmapImage) { + if(instance.display == null) { instance.display = new Bitmap(); } + } + */ + if((instance is MeasurableItem && !(instance is Skin)) || instance is Component) { // skins are assigned the target display + if(instance.display == null) { instance.display = new Sprite(); } + } + + if(instance is Container) { + if(instance.content == null) { instance.content = new SimpleCollection(); } + if(instance.horizontal == null) { instance.horizontal = new Position(); } + if(instance.vertical == null) { instance.vertical = new Position(); } + } + + if(instance is Component) { + if(instance.behaviors == null) { instance.behaviors = new SimpleCollection(); } + } + + // constructor replacement + /* + if(Object(instance).constructor == Scroller) { + instance.layout = new BasicLayout(); + instance.skin = new ScrollerSkin(); + } + + if(Object(instance).constructor == List) { + instance.skin = new ListSkin(); + } + */ + + // default component definitions + + if(Object(instance).constructor == ListItem) { + if(instance.skin == null) { instance.skin = new ListItemSkin(); } + instance.behaviors.addItem(new ButtonBehavior(instance as IEventDispatcher)); + instance.behaviors.addItem(new SelectBehavior(instance as IEventDispatcher)); + } + + if(Object(instance).constructor == Button) { + if(instance.skin == null) { instance.skin = new ButtonSkin(); } + instance.behaviors.addItem(new ButtonBehavior(instance as IEventDispatcher)); + } + + + //if(Object(instance).constructor == Group) { + if(instance is Group) { + if(instance.layout == null) { instance.layout = new BasicLayout(); } + } + //if(Object(instance).constructor == HGroup) { + if(instance is HGroup) { + if(instance.layout == null) { instance.layout = new HorizontalLayout(); } + } + //if(Object(instance).constructor == VGroup) { + if(instance is VGroup) { + if(instance.layout == null) { instance.layout = new VerticalLayout(); } + } + + instance.dispatchEvent(new Event(LifeCycle.INITIALIZE)); + + } + + } +} \ No newline at end of file diff --git a/src/reflex/injection/IReflexInjector.as b/src/reflex/injection/IReflexInjector.as new file mode 100644 index 00000000..b386d49a --- /dev/null +++ b/src/reflex/injection/IReflexInjector.as @@ -0,0 +1,13 @@ +package reflex.injection +{ + import flash.display.Stage; + import flash.events.IEventDispatcher; + + public interface IReflexInjector + { + + function initialize(stage:Stage, app:IEventDispatcher):void; + function injectInto(instance:Object):void; + + } +} \ No newline at end of file diff --git a/src/reflex/invalidation/IReflexInvalidation.as b/src/reflex/invalidation/IReflexInvalidation.as new file mode 100644 index 00000000..9f074310 --- /dev/null +++ b/src/reflex/invalidation/IReflexInvalidation.as @@ -0,0 +1,15 @@ +package reflex.invalidation +{ + import flash.display.DisplayObject; + import flash.events.IEventDispatcher; + + public interface IReflexInvalidation + { + + + // DisplayObject? + function invalidate(instance:IEventDispatcher, phase:String):Boolean; + function validate(instance:IEventDispatcher=null, phase:String=null):void; + function add(instance:IEventDispatcher):void; + } +} \ No newline at end of file diff --git a/src/reflex/invalidation/Interval.as b/src/reflex/invalidation/Interval.as new file mode 100644 index 00000000..b576383c --- /dev/null +++ b/src/reflex/invalidation/Interval.as @@ -0,0 +1,104 @@ +/* +* From the Stealth SDK, a UI framework for the Flash Developer. +* Copyright (c) 2011 Tyler Wright - Flight XD. +* Permission is hereby granted to use, modify, and distribute this file +* in accordance with the terms of the license agreement accompanying it. +*/ + +package reflex.invalidation +{ + import flash.display.DisplayObject; + import flash.display.Shape; + import flash.events.Event; + import flash.events.TimerEvent; + import flash.utils.Timer; + + public class Interval extends Timer + { + private static const display:DisplayObject = new Shape(); + + public static const global:Interval = new Interval(); + global.start(); + + public function Interval(timelineSynced:Boolean = true) + { + super(1000 / 60); + this.timelineSynced = timelineSynced; + } + + override public function get running():Boolean { return _timelineSynced ? _running : super.running; } + private var _running:Boolean; + + override public function get currentCount():int { return _timelineSynced ? _currentCount : super.currentCount; } + private var _currentCount:int = 0; + + override public function set repeatCount(value:int):void + { + super.repeatCount = value; + if (_timelineSynced && super.repeatCount != 0 && super.repeatCount <= currentCount) { + stop(); + dispatchEvent(new TimerEvent(TimerEvent.TIMER_COMPLETE)); + } + } + + public function get timelineSynced():Boolean { return _timelineSynced; } + public function set timelineSynced(value:Boolean):void + { + stop(); + _timelineSynced = value; + start(); + } + private var _timelineSynced:Boolean; + + override public function reset():void + { + if (_timelineSynced) { + stop(); + _currentCount = 0; + } else { + super.reset(); + } + } + + override public function start():void + { + if (repeatCount != 0 && repeatCount <= currentCount) { + return; + } + + if (_timelineSynced) { + display.addEventListener(Event.ENTER_FRAME, onEnterFrame); + _running = true; + } else { + addEventListener(TimerEvent.TIMER, onTimer, false, -10); + super.start(); + } + } + + override public function stop():void + { + if (_timelineSynced) { + display.removeEventListener(Event.ENTER_FRAME, onEnterFrame); + _running = false; + } else { + removeEventListener(TimerEvent.TIMER, onTimer); + super.stop(); + } + } + + private function onEnterFrame(event:Event):void + { + _currentCount++; + dispatchEvent(new TimerEvent(TimerEvent.TIMER)); + if (repeatCount != 0 && repeatCount <= currentCount) { + stop(); + dispatchEvent(new TimerEvent(TimerEvent.TIMER_COMPLETE)); + } + } + + private function onTimer(event:TimerEvent):void + { + event.updateAfterEvent(); + } + } +} \ No newline at end of file diff --git a/src/reflex/invalidation/Invalidation.as b/src/reflex/invalidation/Invalidation.as new file mode 100644 index 00000000..c16b4fc8 --- /dev/null +++ b/src/reflex/invalidation/Invalidation.as @@ -0,0 +1,278 @@ +/* +* Copyright (c) 2010 the original author or authors. +* Permission is hereby granted to use, modify, and distribute this file +* in accordance with the terms of the license agreement accompanying it. +*/ + +package reflex.invalidation +{ + //import flash.display.DisplayObject; + //import flash.display.DisplayObjectContainer; + import flash.display.Stage; + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.utils.Dictionary; + import flash.utils.getQualifiedClassName; + + import mx.collections.IList; + + import reflex.animation.Animator; + import reflex.components.Component; + import reflex.components.List; + import reflex.containers.Container; + import reflex.containers.Group; + import reflex.containers.IContainer; + import reflex.invalidation.callLater; + import reflex.skins.ButtonSkin; + + //import flight.containers.Group; + + //import flight.utils.callLater; + //import flight.utils.getClassName; + + /** + * The Invalidation utility allows potentially expensive processes, such as + * layout, to delay their execution and run just once before the screen is + * rendered. In the case of layout this delayed execution is a performance + * necessity, because the size and position of display objects can directly + * effect each other, through all levels of the display-list. + * + * Invalidation runs in ordered execution by level and depth and supports + * any number of custom phases. For example, the "commit" phase will run + * through the entire display list, starting with the stage and moving down + * to each invalidated child, dispatching an InvalidationEvent + * of type "commit". Later, in its own pass, the "layout" phase will run + * through the display list dispatching a LayoutEvent from + * display objects that have been invalidated by this phase. All + * invalidation happens via these custom phases which must be registered + * before use. A small set of known phases should be documented and + * maintained on their related Event class. + * + * Invalidation is tied to the display list and supports multiple stages. + * Processes unrelated to rendering can be deferred via the package-level + * method callLater(). + * + * @see flight.utils#callLater() + */ + public class Invalidation implements IReflexInvalidation + { + + public var stage:Stage; + public var app:IEventDispatcher; + + /** + * Internal weak-reference registry of all display objects initialized + * by Invalidation, including stages. + */ + private var initialized:Dictionary = new Dictionary(true); + + + /** + * Internal weak-referenced registry of invalidated stages. + */ + private var invalidStages:Dictionary = new Dictionary(true); + + + /** + * An Array of registered phases ordered by priority from highest to + * lowest. + */ + private var phases:Array = []; + + /** + * Phase lookup by phase name, for convenience. + */ + private var lookup:Object = {}; + + public function Invalidation():void { + initialized[null] = true; + invalidStages[null] = true; + } + + /** + * Phases of invalidation such as "measure", "layout" and "draw" allow + * different systems to register their own pass over the display-list + * independent of any other system. Phases are ordered by priority and + * marked as ascending (execution of child then parent) or descending + * (from parent to child). Though phases run in the same render cycle + * they are independent from each other and must be invalidated + * independently. Finally, each phase supports a unique event dispatched + * from the display object which is how systems such as layout execute. + * + * For example: + * + * // phases only need to be registered once + * Invalidation.registerPhase(LayoutEvent.MEASURE, LayoutEvent, 100); + * + * // invalidate/listen on display objects wanting to support measurement + * Invalidation.invalidate(sprite, LayoutEvent.MEASURE); + * sprite.addEventListener(LayoutEvent.MEASURE, onMeasure); + * + * function onMeasure(event:LayoutEvent):void + * { + * // run measurement code on event.target (the invalidated display object) + * } + * + * + * @param phaseName The name by which objects are invalidated and + * the event type dispatched on their validation. + * @param eventType The event class created and dispatched on + * validation. + * @param priority Phase priority relating to other phases, where + * higher priority runs validation first. + * @param ascending Determines order of execution within the phase + * with ascending from child to parent. + * @return Returns true if phase was successful registered + * for the first time, or re-registered with new + * priority/ascending settings. + */ + public function registerPhase(phaseName:String, eventType:Class = null, priority:int = 0, ascending:Boolean = false):Boolean + { + var phase:InvalidationPhase = lookup[phaseName]; + if (!phase) { + phase = new InvalidationPhase(phaseName, eventType, ascending); + phases.push(phase); // keep track of phases in both ordered phases and the lookup + lookup[phase.name] = phase; + } else if (phase.priority == priority) { + return false; + } + + phase.priority = priority; + phases.sortOn("priority", Array.DESCENDING | Array.NUMERIC); // always maintain order - phases shouldn't be registered often + return true; + } + + /** + * Marks a target for delayed execution in the specified phase, to + * execute just once this render cycle regardless of the number times + * invalidate is called. Phase "execution" is carried out through an + * event of type phaseName dispatched from the target, and can be + * listened to by anyone. + * + * @param target The DisplayObject or display object to be + * invalidated. + * @param phaseName The phase to be invalidated by, and the event + * type dispatched on resolution. + * @return Returns true if the target was invalidated for + * the first time this render cycle. + */ + public function invalidate(target:IEventDispatcher, phaseName:String):Boolean + { + var phase:InvalidationPhase = lookup[phaseName]; + if (!phase) { + throw new Error(/*getClassName(target) +*/ "- cannot be invalidated by unknown phase '" + phaseName + "'."); + } + + if (phase.invalidate(target)) { + if (!initialized[target]) { + initialize(stage); //initialize(target); + } + invalidateStage(stage); //invalidateStage(target.stage); + return true; + } + return false; + } + + /** + * Most often internally invoked with the render cycle, validate runs + * each phase of invalidation by priority and level. A specific target + * and phase may be validated manually independent of the render cycle. + * + * @param target Optional invalidated target to be resolved. If + * null, full validation cycle is run. + * are resolved. + * @param phaseName Optional invalidation phase to run. If null, all + * phases will be run in order of priority. + */ + public function validate(target:IEventDispatcher = null, phaseName:String = null):void + { + if (phaseName) { + var phase:InvalidationPhase = lookup[phaseName]; + if (!phase) { + throw new Error(/*getClassName(target) +*/ "- cannot be validated by unknown phase '" + phaseName + "'."); + } + phase.validateNow(target); + } else { + for each (phase in phases) { + phase.validateNow(target); + } + } + } + + private function initialize(target:IEventDispatcher):void + { + if (!initialized[target]) { + initialized[target] = true; + + if (target is Stage) { + target.addEventListener(Event.RENDER, onRender, false, -10, true); // listen to ALL stage render events, also a permanent listener since they only get + // dispatched with a stage.invalidate and add/remove listeners costs some in performance + target.addEventListener(Event.RESIZE, onRender, false, -10, true); // in many environments render and enterFrame events stop firing when stage is resized - + // listening to resize compensates for this shortcoming and continues to run validation + } /*else { + target.addEventListener(Event.ADDED_TO_STAGE, onAdded, false, 20, true); // watch for level changes - this is a permanent listener since these changes happen + // less frequently than invalidation and so require fewer level calculations + if (stage) { // target.stage + initialize(stage); // target.stage + } else { + target.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 30, true); + } + }*/ + } + } + + /** + * Listener responding to both render and stage resize events. + */ + private function onRender(event:Event):void + { + var stage:Stage = Stage(event.currentTarget); + if (invalidStages[stage]) { + invalidStages[stage] = false; + validate(app); + delete invalidStages[stage]; + } + } + + /** + * Listener responding to any previously invalidated display objects + * being added to the display-list, calculating their level (depth + * in display-list hierarchy). + */ + public function add(target:IEventDispatcher):void + { + //var target:IEventDispatcher = event.target as IEventDispatcher;//DisplayObject(event.target); + // correctly invalidate newly added display object on all phases + for each (var phase:InvalidationPhase in phases) { // where it was invalidated while off of the display-list (and set at level -1) + if (phase.invalid[target]) { + var parent:IEventDispatcher = (target as Object).owner; //var parent:DisplayObjectContainer = target.parent; + while (parent) { + phase.invalidContent[parent] = true; + parent = !(parent is Stage) ? (parent as Object).owner : null; //parent.parent; + } + } + } + invalidateStage(stage); // target.stage + } + + private function invalidateStage(stage:Stage):void + { + if (!invalidStages[stage]) { + if (invalidStages[stage] == null) { + invalidStages[stage] = true; + stage.invalidate(); + } else { + callLater(invalidateStage, 1, stage); + } + } + } + /* + private static function onAddedToStage(event:Event):void + { + var target:IEventDispatcher = event.target as IEventDispatcher; // DisplayObject(event.target); + target.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage); + initialize(stage); // target.stage + } + */ + } +} \ No newline at end of file diff --git a/src/reflex/invalidation/InvalidationPhase.as b/src/reflex/invalidation/InvalidationPhase.as new file mode 100644 index 00000000..83f969b9 --- /dev/null +++ b/src/reflex/invalidation/InvalidationPhase.as @@ -0,0 +1,194 @@ +package reflex.invalidation +{ + import flash.display.Stage; + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.utils.Dictionary; + + import mx.collections.IList; + + import reflex.components.Component; + import reflex.containers.Container; + import reflex.containers.IContainer; + + public class InvalidationPhase + { + /** + * The priority of this phase relating to other invalidation phases. + */ + public var priority:int = 0; + + /** + * The event class instantiated for dispatch from invalidation targets. + */ + public var eventType:Class; + + public var ascending:Boolean; + + /** + * Quick reference with invalidated targets as key and value as level. + */ + public var invalid:Dictionary = new Dictionary(true); + + /** + */ + public var invalidContent:Dictionary = new Dictionary(true); + + private var indices:Dictionary = new Dictionary(true); + + /** + * Constructor requiring phase name also used as event type, and + * optionally the class used for event instantiation. + * + * @param name Phase name, also the event type. + * @param eventType Event class used when dispatching from + * invalidation targets. + */ + public function InvalidationPhase(name:String, eventType:Class = null, ascending:Boolean = false) + { + _name = name; + this.eventType = eventType || Event; + this.ascending = ascending; + } + + /** + * Phase name, also used as the event type. + */ + public function get name():String { return _name; } + private var _name:String; + + + /** + * Effectively invalidates target with this phase. + * + * @param target Target to be invalidated. + * @return Returns true the first time target is + * invalidated. + */ + public function invalidate(target:IEventDispatcher):Boolean + { + + if (!target || invalid[target]) { + return false; + } + + invalid[target] = true; + + var parent:IEventDispatcher = (target as Object).owner; //var parent:DisplayObjectContainer = target.parent; + while (parent && !invalidContent[parent]) { + invalidContent[parent] = true; + parent = !(parent is Stage) ? (parent as Object).owner : null; // parent.parent; + } + + return true; + } + + /** + * Execution of the phase by dispatching an event from each target, in order + * ascending or descending by level. Event type and class correlate with + * phase name and eventType respectively. + * + * @param target Optional target may be specified for isolated + * validation. If null, full validation is run on + * all targets in proper level order. + */ + public function validateNow(target:IEventDispatcher):void + { + var current:IEventDispatcher; //DisplayObjectContainer; + var next:IEventDispatcher; + var i:int; + + // flattened recursive process to maintain a shallow stack + if (ascending) { + + if (invalidContent[target]) { + delete invalidContent[target]; + current = target;//DisplayObjectContainer(target); + indices[current] = 0; + + while (current) { + + i = indices[current]++; + var content:IList = null; + if(current is Component) { + next = i == 0 ? (current as Component).skin as IEventDispatcher : null; + //indices[current] = 0; + } else if(current is IContainer) { + content = (current as IContainer).content; + next = (content && i < content.length) ? (current as Container).getRendererForItem(content.getItemAt(i)) as IEventDispatcher : null; + } + //next = i < (current as Stage).numChildren ? (current as Stage).getChildAt(i) : null; // trouble here + if (next) { + if (invalidContent[next]) { + delete invalidContent[next]; + + current = next; + indices[current] = 0; + } else if (invalid[next]) { + delete invalid[next]; + next.dispatchEvent(new eventType(_name)); + } + } else { + if (invalid[current]) { + delete invalid[current]; + current.dispatchEvent(new eventType(_name)); + } + delete indices[current]; + current = current != target ? (current as Object).owner : null;//current = current != target ? current.parent : null; + } + } + + } else if (invalid[target]) { + delete invalid[target]; + target.dispatchEvent(new eventType(_name)); + } + + } else { + + if (invalid[target]) { + delete invalid[target]; + target.dispatchEvent(new eventType(_name)); + } + + if (invalidContent[target]) { + delete invalidContent[target]; + current = target; //DisplayObjectContainer(target); + indices[current] = 0; + + while (current) { + + i = indices[current]++; + content = null; + if(current is Component) { + next = i==0 ? (current as Component).skin as IEventDispatcher : null; + //indices[current] = 0; + } else if(current is IContainer) { + content = (current as IContainer).content; + next = (content && i < content.length) ? (current as Container).getRendererForItem(content.getItemAt(i)) as IEventDispatcher : null; + } + //next = i < (current as Stage).numChildren ? (current as Stage).getChildAt(i) : null; // trouble here + if (next) { + if (invalid[next]) { + delete invalid[next]; + next.dispatchEvent(new eventType(_name)); + } + + if (invalidContent[next]) { + delete invalidContent[next]; + + current = next; // DisplayObjectContainer(next); + indices[current] = 0; + } + } else { + delete indices[current]; + current = current != target ? (current as Object).owner : null; // current = current != target ? current.parent : null; + } + } + } + + } + + + } + } +} \ No newline at end of file diff --git a/src/reflex/invalidation/LifeCycle.as b/src/reflex/invalidation/LifeCycle.as new file mode 100644 index 00000000..5370626a --- /dev/null +++ b/src/reflex/invalidation/LifeCycle.as @@ -0,0 +1,14 @@ +package reflex.invalidation +{ + public class LifeCycle + { + + //static public const CREATE:String = "create"; // + //static public const INJECT:String = "inject"; // 1 + static public const INITIALIZE:String = "initialize"; // 2 + static public const COMMIT:String = "commit"; // 3 + static public const MEASURE:String = "measure"; // 4 + static public const LAYOUT:String = "layout"; // 5 + + } +} \ No newline at end of file diff --git a/src/reflex/invalidation/callLater.as b/src/reflex/invalidation/callLater.as new file mode 100644 index 00000000..a8c41b95 --- /dev/null +++ b/src/reflex/invalidation/callLater.as @@ -0,0 +1,59 @@ +/* +* From the Stealth SDK, a UI framework for the Flash Developer. +* Copyright (c) 2011 Tyler Wright - Flight XD. +* Permission is hereby granted to use, modify, and distribute this file +* in accordance with the terms of the license agreement accompanying it. +*/ + +package reflex.invalidation +{ + import flash.events.TimerEvent; + + public function callLater(method:Function, ticks:uint = 1, ...args):void + { + if (!ticks) { + method.apply(null, args); + return; + } + + calls[method] = args; + calls[method].ticks = ticks; + + if (!enabled) { + enabled = true; + Interval.global.addEventListener(TimerEvent.TIMER, callNow); + } + } +} + +import flash.events.TimerEvent; +import flash.utils.Dictionary; + +import reflex.invalidation.Interval; + +internal var enabled:Boolean; +internal var calls:Dictionary = new Dictionary(); +internal var callsEmpty:Dictionary = new Dictionary(); + +internal function callNow(event:TimerEvent):void +{ + var callsNow:Dictionary = calls; + calls = callsEmpty; + callsEmpty = callsNow; + + enabled = false; + for (var method:Object in callsNow) { + + if (--callsNow[method].ticks) { + enabled = true; + calls[method] = callsNow[method]; + } else { + method.apply(null, callsNow[method]); + } + delete callsNow[method]; + } + + if (!enabled) { + Interval.global.removeEventListener(TimerEvent.TIMER, callNow); + } +} \ No newline at end of file diff --git a/src/reflex/layout/Align.as b/src/reflex/layout/Align.as deleted file mode 100644 index d70de1eb..00000000 --- a/src/reflex/layout/Align.as +++ /dev/null @@ -1,13 +0,0 @@ -package reflex.layout -{ - public class Align - { - public static const NONE:String = "none"; - public static const LEFT:String = "left"; - public static const TOP:String = "top"; - public static const RIGHT:String = "right"; - public static const BOTTOM:String = "bottom"; - public static const CENTER:String = "center"; - public static const FILL:String = "fill"; - } -} \ No newline at end of file diff --git a/src/reflex/layout/Block.as b/src/reflex/layout/Block.as deleted file mode 100644 index 75f4b2f5..00000000 --- a/src/reflex/layout/Block.as +++ /dev/null @@ -1,548 +0,0 @@ -package reflex.layout -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.events.Event; - import flash.geom.Matrix; - import flash.geom.Rectangle; - - import flight.events.PropertyEvent; - - import mx.events.PropertyChangeEvent; - - public class Block extends LayoutWrapper - { - [Bindable] - public var scale:Boolean = false; - - [Bindable] - public var bounds:Bounds = new Bounds(); - - public var snapToPixel:Boolean; - - // holds explicitly set value from setting width/height - protected var explicitWidth:Number; - protected var explicitHeight:Number; - - private var _x:Number; - private var _y:Number; - private var _defaultWidth:Number = 0; - private var _defaultHeight:Number = 0; - private var _width:Number = defaultWidth; - private var _height:Number = defaultHeight; - private var _displayWidth:Number = defaultWidth; - private var _displayHeight:Number = defaultHeight; - private var _measuredWidth:Number = 0; - private var _measuredHeight:Number = 0; - private var _measuredBounds:Bounds = bounds; - private var _blockBounds:Bounds = bounds; - - private var _margin:Box = new Box(); - private var _padding:Box = new Box(); - private var _anchor:Box = new Box(NaN, NaN, NaN, NaN); - private var _dock:String = Align.NONE; - private var _align:String = Align.NONE; - - - public function Block(target:DisplayObject = null, scale:Boolean = false) - { - super(target); - this.scale = scale; - _margin.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onObjectChange); - _padding.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onObjectChange); - _anchor.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onObjectChange); - bounds.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onObjectChange); - _anchor.horizontal = _anchor.vertical = NaN; - algorithm = new Dock(); - } - - override public function set target(value:DisplayObject):void - { - if (target == value) { - return; - } - - if (value != null) { - var rect:Rectangle = value.getRect(value); - defaultWidth = rect.right; - defaultHeight = rect.bottom; - super.target = value; - } else { - super.target = value; - } - } - - [Bindable(event="xChange")] - public function get x():Number - { - return _x; - } - public function set x(value:Number):void - { - if (_x == value) { - return; - } - - _x = snapToPixel ? Math.round(value) : value; - if (target != null && !isNaN(_x) ) { - var m:Matrix = target.transform.matrix; - var d:Number = m.a * _displayWidth + m.c * _displayHeight; - target.x = d < 0 ? _x - d : _x; - } - dispatchEvent(new Event("xChange")); - } - - [Bindable(event="yChange")] - public function get y():Number - { - return _y; - } - public function set y(value:Number):void - { - if (_y == value) { - return; - } - - _y = snapToPixel ? Math.round(value) : value; - if (target != null && !isNaN(_y) ) { - var m:Matrix = target.transform.matrix; - var d:Number = m.d * _displayHeight + m.b * _displayWidth; - target.y = d < 0 ? _y - d : _y; - } - dispatchEvent(new Event("yChange")); - } - - [Bindable(event="widthChange")] - public function get width():Number - { - return _width; - } - public function set width(value:Number):void - { - if (target == null) { // TODO: or if scaling and rotation are default - displayWidth = value; - } else { - var m:Matrix = target.transform.matrix; - if (scale) { - // set scale back to 1 but keep rotation - var skewY:Number = Math.atan2(m.b, m.a); - m.a = Math.cos(skewY); - m.b = Math.sin(skewY); - var skewX:Number = Math.atan2(-m.c, m.d); - m.c = -Math.sin(skewX); - m.d = Math.cos(skewX); - } - m.invert(); - - var w:Number = Math.abs(m.a * value + m.c * _height); - var h:Number = Math.abs(m.d * _height + m.b * value); - - if (w != _displayWidth) { - explicitWidth = w; - } - if (h != _displayHeight) { - explicitHeight = h; - } - updateSize(); - } - } - - [Bindable(event="heightChange")] - public function get height():Number - { - return _height; - } - public function set height(value:Number):void - { - if (target == null) { // TODO: or if scaling and rotation are default - displayHeight = value; - } else { - var m:Matrix = target.transform.matrix; - if (scale) { - // set scale back to 1 but keep rotation - var skewY:Number = Math.atan2(m.b, m.a); - m.a = Math.cos(skewY); - m.b = Math.sin(skewY); - var skewX:Number = Math.atan2(-m.c, m.d); - m.c = -Math.sin(skewX); - m.d = Math.cos(skewX); - } - m.invert(); - - var h:Number = Math.abs(m.d * value + m.b * _width); - var w:Number = Math.abs(m.a * _width + m.c * value); - if (h != _displayHeight) { - explicitHeight = h; - } - if (w != _displayWidth) { - explicitWidth = w; - } - updateSize(); - } - } - - [Bindable(event="displayWidthChange")] - public function get displayWidth():Number - { - return _displayWidth; - } - public function set displayWidth(value:Number):void - { - if (explicitWidth == value) { - return; - } - - explicitWidth = snapToPixel ? Math.round(value) : value; - updateSize(); - } - - [Bindable(event="displayHeightChange")] - public function get displayHeight():Number - { - return _displayHeight; - } - public function set displayHeight(value:Number):void - { - if (explicitHeight == value) { - return; - } - - explicitHeight = snapToPixel ? Math.round(value) : value; - updateSize(); - } - - [Bindable(event="defaultWidthChange")] - public function get defaultWidth():Number - { - return _defaultWidth; - } - public function set defaultWidth(value:Number):void - { - if (_defaultWidth == value) { - return; - } - - _defaultWidth = PropertyEvent.change(this, "defaultWidth", _defaultWidth, snapToPixel ? Math.round(value) : value); - updateSize(); - PropertyEvent.dispatch(this); - } - - [Bindable(event="defaultHeightChange")] - public function get defaultHeight():Number - { - return _defaultHeight; - } - public function set defaultHeight(value:Number):void - { - if (_defaultHeight == value) { - return; - } - - _defaultHeight = PropertyEvent.change(this, "defaultHeight", _defaultHeight, snapToPixel ? Math.round(value) : value); - updateSize(); - PropertyEvent.dispatch(this); - } - - [Bindable("measuredWidthChange")] - public function get measuredWidth():Number - { - return _measuredWidth; - } - - [Bindable("measuredHeightChange")] - public function get measuredHeight():Number - { - return _measuredHeight; - } - - [Bindable("measuredBoundsChange")] - public function get measuredBounds():Bounds - { - return _measuredBounds; - } - - public function get blockBounds():Bounds - { - return _blockBounds; - } - - [Bindable(event="marginChange")] - public function get margin():Box - { - return _margin; - } - public function set margin(value:*):void - { - var margin:Box = (value is String || value is Number) ? Box.fromString( String(value) ) : value as Box; - if (margin == null) { - margin = new Box(); - } - if (_margin.equals(margin)) { - return; - } - - _margin = margin; - dispatchEvent(new Event("marginChange")); - } - - [Bindable(event="paddingChange")] - public function get padding():Box - { - return _padding; - } - public function set padding(value:*):void - { - var padding:Box = (value is String || value is Number) ? Box.fromString( String(value) ) : value as Box; - if (padding == null) { - padding = new Box(); - } - if (_padding.equals(padding)) { - return; - } - - _padding = padding; - dispatchEvent(new Event("paddingChange")); - } - - [Bindable(event="anchorChange")] - public function get anchor():Box - { - return _anchor; - } - public function set anchor(value:*):void - { - var anchor:Box = (value is String || value is Number) ? Box.fromString( String(value) ) : value as Box; - if (anchor == null) { - anchor = new Box(NaN, NaN, NaN, NaN); - } - if (_anchor.equals(anchor)) { - return; - } - - _anchor = anchor; - dispatchEvent(new Event("anchorChange")); - } - - - [Bindable(event="dockChange")] - public function get dock():String - { - return _dock; - } - public function set dock(value:String):void - { - if (value != Align.NONE && value != Align.LEFT && value != Align.TOP && - value != Align.RIGHT && value != Align.BOTTOM && value != Align.FILL) { - value = Align.NONE; - } - - if (_dock == value) { - return; - } - - _dock = value; - - if (_align != Align.NONE && (_dock == Align.NONE || _dock == Align.FILL) ) { - align = Align.NONE; - } - invalidate(); - dispatchEvent( new Event("dockChange") ); - } - - [Bindable(event="alignChange")] - public function get align():String - { - return _align; - } - public function set align(value:String):void - { - if (value != Align.NONE && value != Align.LEFT && value != Align.TOP && - value != Align.RIGHT && value != Align.BOTTOM) { - value = Align.NONE; - } - - if (_align == value) { - return; - } - - _align = value; - // TODO: ensure dock is in the right axis (ie. if align==LEFT then dock cannot equal LEFT or RIGHT) - if (_align != Align.NONE && (_dock == Align.NONE || _dock == Align.FILL) ) { - dock = (_align == Align.LEFT || _align == Align.RIGHT) ? Align.TOP : Align.LEFT; - } - invalidate(); - dispatchEvent( new Event("alignChange") ); - } - - override public function validate():void - { - super.validate(); - updateDisplay(); - } - - override public function measure():void - { - var container:DisplayObjectContainer = target as DisplayObjectContainer; - if (container == null) { - return; - } - - if (algorithm != null) { - algorithm.measure(container); - return; - } - - var measurement:Bounds = new Bounds(); - - for (var i:int = 0; i < container.numChildren; i++) { - var display:DisplayObject = container.getChildAt(i); - var child:Block = getLayout(display) as Block; - if (child == null || child.freeform) { - continue; - } - - measurement.minWidth = measurement.constrainWidth( - display.x + child.width + padding.right + child.margin.right); - - measurement.minHeight = measurement.constrainHeight( - display.y + child.height + padding.bottom + child.margin.bottom); - } - - updateMeasurement(measurement); - } - - public function updateMeasurement(measuredBounds:Bounds, measuredWidth:Number = 0, measuredHeight:Number = 0):void - { - measuredBounds.merge(bounds); - _blockBounds = measuredBounds.clone(); - - if (target != null) { // TODO: and if scaling and rotation are not default - var m:Matrix = target.transform.matrix; - if (scale) { - // set scale back to 1 but keep rotation - var skewY:Number = Math.atan2(m.b, m.a); - m.a = Math.cos(skewY); - m.b = Math.sin(skewY); - var skewX:Number = Math.atan2(-m.c, m.d); - m.c = -Math.sin(skewX); - m.d = Math.cos(skewX); - } - _blockBounds.minWidth = Math.abs(m.a * measuredBounds.minWidth + m.c * measuredBounds.minHeight); - _blockBounds.minHeight = Math.abs(m.d * measuredBounds.minHeight + m.b * measuredBounds.minWidth); - _blockBounds.maxWidth = Math.abs(m.a * measuredBounds.maxWidth + m.c * measuredBounds.maxHeight); - _blockBounds.maxHeight = Math.abs(m.d * measuredBounds.maxHeight + m.b * measuredBounds.maxWidth); - } - - if (_measuredWidth != measuredWidth) { - _measuredWidth = measuredWidth; - dispatchEvent( new Event("measuredWidthChange") ); - } - if (_measuredHeight != measuredHeight) { - _measuredHeight = measuredHeight; - dispatchEvent( new Event("measuredHeightChange") ); - } - if ( !_measuredBounds.equals(measuredBounds) ) { - _measuredBounds = measuredBounds; - dispatchEvent( new Event("measuredBoundsChange") ); - } - - updateSize(); - } - - public function updateDisplay():void - { - if (target == null) { - return; - } - - if (scale) { - target.width = displayWidth; - target.height = displayHeight; - } - } - - protected function updateSize():void - { - var oldWidth:Number = _width; - var oldHeight:Number = _height; - var oldDisplayWidth:Number = _displayWidth; - var oldDisplayHeight:Number = _displayHeight; - - _displayWidth = !isNaN(explicitWidth) ? explicitWidth : - (_measuredWidth >= defaultWidth ? _measuredWidth : defaultWidth); - _displayHeight = !isNaN(explicitHeight) ? explicitHeight : - (defaultHeight >= _measuredHeight ? defaultHeight : _measuredHeight); - - _displayWidth = measuredBounds.constrainWidth(_displayWidth); - _displayHeight = measuredBounds.constrainHeight(_displayHeight); - if (snapToPixel) { - _displayWidth = Math.round(_displayWidth); - _displayHeight = Math.round(_displayHeight); - } - - if (target == null) { // TODO: or if scaling and rotation are default - _width = _displayWidth; - _height = _displayHeight; - } else { - var m:Matrix = target.transform.matrix; - if (scale) { - // reset scale while retaining rotation - var skewY:Number = Math.atan2(m.b, m.a); - m.a = Math.cos(skewY); - m.b = Math.sin(skewY); - var skewX:Number = Math.atan2(-m.c, m.d); - m.c = -Math.sin(skewX); - m.d = Math.cos(skewX); - } - - _width = Math.abs(m.a * _displayWidth + m.c * _displayHeight); - _height = Math.abs(m.d * _displayHeight + m.b * _displayWidth); - if (snapToPixel) { - _width = Math.round(_width); - _height = Math.round(_height); - } - } - - if (_width != oldWidth) { - invalidate(); - dispatchEvent(new Event("widthChange")); - } - if (_height != oldHeight) { - invalidate(); - dispatchEvent(new Event("heightChange")); - } - - if (_displayWidth != oldDisplayWidth) { - dispatchEvent(new Event("displayWidthChange")); - } - if (_displayHeight != oldDisplayHeight) { - dispatchEvent(new Event("displayHeightChange")); - } - } - - private function onObjectChange(event:Event):void - { - switch (event.target) { - case _margin : - invalidate(); - dispatchEvent( new Event("marginChange") ); - break; - case _padding : - invalidate(); - dispatchEvent( new Event("paddingChange") ); - break; - case _padding : - if (dock == Align.NONE) { - invalidate(); - } - dispatchEvent( new Event("anchorChange") ); - break; - case bounds : - invalidate(true); - dispatchEvent( new Event("boundsChange") ); - break; - } - } - - } -} diff --git a/src/reflex/layout/Bounds.as b/src/reflex/layout/Bounds.as deleted file mode 100644 index 6fe5dcdd..00000000 --- a/src/reflex/layout/Bounds.as +++ /dev/null @@ -1,63 +0,0 @@ -package reflex.layout -{ - //import flight.utils.IMerging; - //import flight.utils.IValueObject; - - [Bindable] - public class Bounds - { - public var minWidth:Number = 0; - public var minHeight:Number = 0; - public var maxWidth:Number = Number.MAX_VALUE; - public var maxHeight:Number = Number.MAX_VALUE; - - public function constrainWidth(width:Number):Number - { - return (width <= minWidth) ? minWidth : - (width >= maxWidth) ? maxWidth : width; - } - - public function constrainHeight(height:Number):Number - { - return (height <= minHeight) ? minHeight : - (height >= maxHeight) ? maxHeight : height; - } - - public function merge(bounds:Bounds):Bounds - { - if (bounds == null) { - return this; - } - - minWidth = minWidth >= bounds.minWidth ? minWidth : bounds.minWidth; - minHeight = minHeight >= bounds.minHeight ? minHeight : bounds.minHeight; - - maxWidth = maxWidth <= bounds.maxWidth ? maxWidth : bounds.maxWidth; - maxHeight = maxHeight <= bounds.maxHeight ? maxHeight : bounds.maxHeight; - - return this; - } - - public function equals(bounds:Bounds):Boolean - { - if (bounds == null) { - return false; - } - - return (minWidth == bounds.minWidth && minHeight == bounds.minHeight && - maxWidth == bounds.maxWidth && maxHeight == bounds.maxHeight); - } - - public function clone():Bounds - { - var bounds:Bounds = new Bounds(); - bounds.minWidth = minWidth; - bounds.minHeight = minHeight; - bounds.maxWidth = maxWidth; - bounds.maxHeight = maxHeight; - - return bounds; - } - - } -} \ No newline at end of file diff --git a/src/reflex/layout/Box.as b/src/reflex/layout/Box.as deleted file mode 100644 index c404afaa..00000000 --- a/src/reflex/layout/Box.as +++ /dev/null @@ -1,104 +0,0 @@ -package reflex.layout -{ - import flash.events.EventDispatcher; - - //import flight.utils.IMerging; - //import flight.utils.IValueObject; - - [Bindable] - public class Box extends EventDispatcher - { - public var left:Number; - public var top:Number; - public var right:Number; - public var bottom:Number; - - public var offsetX:Number = 0; - public var offsetY:Number = 0; - public var horizontal:Number = 0; - public var vertical:Number = 0; - - public function Box(left:Number = 0, top:Number = 0, right:Number = 0, bottom:Number = 0) - { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - public function merge(box:Box):Box - { - if (box == null) { - return this; - } - - left = left >= box.left ? left : box.left; - top = top >= box.top ? top : box.top; - right = right >= box.right ? right : box.right; - bottom = bottom >= box.bottom ? bottom : box.bottom; - - return this; - } - - public function equals(box:Box):Boolean - { - if (box == null) { - return false; - } - - return (left == box.left && right == box.right && - top == box.top && bottom == box.bottom && - offsetX == box.offsetX && offsetY == box.offsetY && - horizontal == box.horizontal && vertical == box.vertical); - } - - public function clone():Box - { - var box:Box = new Box(left, top, right, bottom); - box.offsetX = offsetX; - box.offsetY = offsetY; - box.horizontal = horizontal; - box.vertical = vertical; - - return box; - } - - public static function fromString(value:String):Box - { - var box:Box = new Box(); - - if (!value) { - return box; - } - - var values:Array = value.split(" "); - switch (values.length) { - case 1 : - box.left = box.top = box.right = box.bottom = parseFloat( values[0] ); - break; - case 2 : - box.left = box.right = parseFloat( values[1] ); - box.top = box.bottom = parseFloat( values[0] ); - break; - case 3 : - box.left = box.right = parseFloat( values[1] ); - box.top = parseFloat( values[0] ); - box.bottom = parseFloat( values[2] ); - break; - case 4 : - box.left = parseFloat( values[3] ); - box.top = parseFloat( values[0] ); - box.right = parseFloat( values[1] ); - box.bottom = parseFloat( values[2] ); - break; - } - - return box; - } - - override public function toString():String - { - return '[Box(left="' + left + '", top="' + top + ', right="' + right + '", bottom="' + bottom + '")]'; - } - } -} \ No newline at end of file diff --git a/src/reflex/layout/Dock.as b/src/reflex/layout/Dock.as deleted file mode 100644 index 9a679eef..00000000 --- a/src/reflex/layout/Dock.as +++ /dev/null @@ -1,410 +0,0 @@ -package reflex.layout -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.geom.Rectangle; - - public class Dock implements ILayoutAlgorithm - { - public function layout(target:DisplayObjectContainer):void - { - var block:Block = LayoutWrapper.getLayout(target) as Block; - if (block == null) { - return; - } - - var dockMargin:Box = new Box(); - var dockArea:Rectangle = new Rectangle(0, 0, block.displayWidth, block.displayHeight); - dockArea.left += block.padding.left; - dockArea.top += block.padding.top; - dockArea.right -= block.padding.right; - dockArea.bottom -= block.padding.bottom; - - var alignMargin:Box; - var alignArea:Rectangle; - var margin:Box; - - var hPad:Number = block.padding.horizontal; - var vPad:Number = block.padding.vertical; - var lastDock:String = Align.NONE; - - for (var i:int = 0; i < target.numChildren; i++) { - var display:DisplayObject = target.getChildAt(i); - var child:Block = LayoutWrapper.getLayout(display) as Block; - if (child == null || child.freeform) { - continue; - } - - if (child.dock == Align.NONE) { - updateAnchor(child, block); - continue; - } - - if (child.align == Align.NONE) { - dockChild(child, child.dock, dockArea, dockMargin.clone().merge(child.margin)); - updateArea(child, child.dock, dockArea, dockMargin, hPad, vPad); - alignMargin = null; // TODO: reuse rectangle/box objects (cache) - } else { - - if (alignMargin == null || child.dock != lastDock) { - alignMargin = dockMargin.clone(); - alignArea = dockArea.clone(); - margin = alignMargin.clone().merge(child.margin); - } else if (true) { // TODO: wrapping aligned items disabled - either enable or remove - - // reset tiling if child doesn't fit inline - margin = alignMargin.clone().merge(child.margin); - if (child.align == Align.LEFT || child.align == Align.RIGHT) { - if (alignArea.width < child.width + margin.left + margin.right) { - alignMargin = dockMargin.clone(); - alignArea = dockArea.clone(); - margin = alignMargin.clone().merge(child.margin); - } - } - - if (child.align == Align.TOP || child.align == Align.BOTTOM) { - if (alignArea.height < child.height + margin.top + margin.bottom) { - alignMargin = dockMargin.clone(); - alignArea = dockArea.clone(); - margin = alignMargin.clone().merge(child.margin); - } - } - } - - dockChild(child, child.align, alignArea, margin); - updateArea(child, child.align, alignArea, alignMargin, hPad, vPad); - updateArea(child, child.dock, dockArea, dockMargin, hPad, vPad); - } - - lastDock = child.dock; - - } - } - - private function updateArea(child:Block, dock:String, area:Rectangle, margin:Box, hPad:Number, vPad:Number):void - { - var pos:Number; - switch (dock) { - case Align.LEFT : - if (area.left < (pos = child.x + child.width + hPad) ) { - area.left = pos; - } - margin.left = child.margin.right; - break; - case Align.TOP : - if (area.top < (pos = child.y + child.height + vPad) ) { - area.top = pos; - } - margin.top = child.margin.bottom; - break; - case Align.RIGHT : - if (area.right > (pos = child.x - hPad) ) { - area.right = pos; - } - margin.right = child.margin.left; - break; - case Align.BOTTOM : - if (area.bottom > (pos = child.y - vPad) ) { - area.bottom = pos; - } - margin.bottom = child.margin.top; - break; - } - } - - /** - * - */ - private function dockChild(child:Block, dock:String, area:Rectangle, margin:Box):void - { - switch (dock) { - case Align.LEFT : - child.x = area.x + margin.left; - child.y = area.y + margin.top; - if (child.align == Align.NONE) { - child.height = area.height - margin.top - margin.bottom; - } else if (child.dock == Align.BOTTOM) { - child.y = area.y + area.height - child.height - margin.bottom; - } - break; - case Align.TOP : - child.x = area.x + margin.left; - child.y = area.y + margin.top; - if (child.align == Align.NONE) { - child.width = area.width - margin.left - margin.right; - } else if (child.dock == Align.RIGHT) { - child.x = area.x + area.width - child.width - margin.right; - } - break; - case Align.RIGHT : - child.x = area.x + area.width - child.width - margin.right; - child.y = area.y + margin.top; - if (child.align == Align.NONE) { - child.height = area.height - margin.top - margin.bottom; - } else if (child.dock == Align.BOTTOM) { - child.y = area.y + area.height - child.height - margin.bottom; - } - break; - case Align.BOTTOM : - child.x = area.x + margin.left; - child.y = area.y + area.height - child.height - margin.bottom; - if (child.align == Align.NONE) { - child.width = area.width - margin.left - margin.right; - } else if (child.dock == Align.RIGHT) { - child.x = area.x + area.width - child.width - margin.right; - } - break; - case Align.FILL : - child.x = area.x + margin.left; - child.y = area.y + margin.top; - if (child.align == Align.NONE) { - child.height = area.height - margin.top - margin.bottom; - child.width = area.width - margin.left - margin.right; - } - break; - case Align.CENTER : - child.x = area.width/2 - child.width/2; - child.y = area.height/2 - child.height/2; - break; - } - } - - // TODO: factor anchor into the measurement - public function measure(target:DisplayObjectContainer):void - { - var block:Block = LayoutWrapper.getLayout(target) as Block; - if (block == null) { - return; - } - - var measurement:Bounds = new Bounds(); - var anchored:Bounds = new Bounds(); - var staticWidth:Number = block.padding.left + block.padding.right; - var staticHeight:Number = block.padding.top + block.padding.bottom; - var space:Number; - var alignWidth:Number; - var alignHeight:Number; - - var dockMargin:Box = new Box(); - var alignMargin:Box; - var margin:Box; - - var hPad:Number = block.padding.horizontal; - var vPad:Number = block.padding.vertical; - var lastDock:String = Align.NONE; - - for (var i:int = 0; i < target.numChildren; i++) { - var display:DisplayObject = target.getChildAt(i); - var child:Block = LayoutWrapper.getLayout(display) as Block; - if (child == null || child.freeform) { - continue; - } - - if (child.dock == Align.NONE) { - measureAnchored(child, anchored); - continue; - } - - if (child.align == Align.NONE) { - if (alignMargin != null) { - if (lastDock == Align.LEFT || lastDock == Align.RIGHT) { - staticWidth += alignWidth; - measurement.minHeight += -vPad; // TODO: no check for top vs bottom & assumption that minHeight was effected by align - } else { - staticHeight += alignHeight; - measurement.minWidth += -hPad; // TODO: no check for left vs right & assumption that minHeight was effected by align - } - alignMargin = null; - } - margin = dockMargin.clone().merge(child.margin); - } else { - - if (alignMargin == null || child.dock != lastDock) { - alignMargin = dockMargin.clone(); - margin = alignMargin.clone().merge(child.margin); - alignWidth = 0; - alignHeight = 0; - } - - margin = alignMargin.clone().merge(child.margin); - } - - if (child.dock == Align.LEFT || child.dock == Align.RIGHT) { - if (child.align != Align.NONE) { - if (child.align == Align.TOP) { - alignMargin.top = child.margin.bottom; - space = margin.top; - } else { - alignMargin.bottom = child.margin.top; - space = margin.bottom; - } - alignHeight += child.height + space + vPad; - space = child.width + (child.dock == Align.LEFT ? margin.left : margin.right) + hPad; - alignWidth = alignWidth >= space ? alignWidth : space; - measurement.minWidth = measurement.constrainWidth(staticWidth + alignWidth); - measurement.minHeight = measurement.constrainHeight(staticHeight + alignHeight); - } else { - if (child.dock == Align.LEFT) { - dockMargin.left = child.margin.right; - space = margin.left; - } else { - dockMargin.right = child.margin.left; - space = margin.right; - } - staticWidth += child.width + space + hPad; - space = staticHeight + margin.top + margin.bottom; - measurement.minWidth = measurement.constrainWidth(staticWidth); - measurement.minHeight = measurement.constrainHeight(space + child.blockBounds.minHeight); - measurement.maxHeight = measurement.constrainHeight(space + child.blockBounds.maxHeight); - } - } else if (child.dock == Align.TOP || child.dock == Align.BOTTOM) { - if (child.align != Align.NONE) { - if (child.dock == Align.LEFT) { - alignMargin.left = child.margin.right; - space = margin.left; - } else { - alignMargin.right = child.margin.left; - space = margin.right; - } - alignWidth += child.width + space + hPad; - space = child.height + (child.dock == Align.TOP ? margin.top : margin.bottom) + vPad; - alignHeight = alignHeight >= space ? alignHeight : space; - measurement.minWidth = measurement.constrainWidth(staticWidth + alignWidth); - measurement.minHeight = measurement.constrainHeight(staticHeight + alignHeight); - } else { - if (child.dock == Align.TOP) { - dockMargin.top = child.margin.bottom; - space = margin.top; - } else { - dockMargin.bottom = child.margin.top; - space = margin.bottom; - } - staticHeight += child.height + space + vPad; - space = staticWidth + margin.left + margin.right; - measurement.minHeight = measurement.constrainHeight(staticHeight); - measurement.minWidth = measurement.constrainWidth(space + child.blockBounds.minWidth); - measurement.maxWidth = measurement.constrainWidth(space + child.blockBounds.maxWidth); - } - } else { // if (child.dock == FILL) { - space = staticWidth + margin.left + margin.right; - measurement.minWidth = measurement.constrainWidth(space + child.blockBounds.minWidth); - measurement.maxWidth = measurement.constrainWidth(space + child.blockBounds.maxWidth); - - space = staticHeight + margin.top + margin.bottom; - measurement.minHeight = measurement.constrainHeight(space + child.blockBounds.minHeight); - measurement.maxHeight = measurement.constrainHeight(space + child.blockBounds.maxHeight); - } - - lastDock = child.dock; - } - - // remove the last pad and add the last margin - switch (lastDock) { - case Align.LEFT : measurement.minWidth += margin.right - hPad; break; - case Align.TOP : measurement.minHeight += margin.bottom - vPad; break; - case Align.RIGHT : measurement.minWidth += margin.left - hPad; break; - case Align.BOTTOM : measurement.minHeight += margin.top - vPad; break; - } - - measurement.merge(anchored); - block.updateMeasurement(measurement); - } - - private function measureAnchored(block:Block, measurement:Bounds):void - { - var space:Number; - if ( !isNaN(block.anchor.left) ) { - if ( !isNaN(block.anchor.right) ) { - space = block.anchor.left + block.anchor.right; - measurement.minWidth = measurement.constrainWidth(space + block.blockBounds.minWidth); - measurement.maxWidth = measurement.constrainWidth(space + block.blockBounds.maxWidth); - } else if ( !isNaN(block.anchor.horizontal) ) { - space = block.anchor.left - block.anchor.offsetX; - measurement.minWidth = measurement.constrainWidth(space + block.blockBounds.minWidth/block.anchor.horizontal); - measurement.maxWidth = measurement.constrainWidth(space + block.blockBounds.maxWidth/block.anchor.horizontal); - } else { - space = block.anchor.left; - measurement.minWidth = measurement.constrainWidth(space + block.width); - } - } else if ( !isNaN(block.anchor.right) ) { - if ( !isNaN(block.anchor.horizontal) ) { - space = block.anchor.right + block.anchor.offsetX; - measurement.minWidth = measurement.constrainWidth(space + block.blockBounds.minWidth/block.anchor.horizontal); - measurement.maxWidth = measurement.constrainWidth(space + block.blockBounds.maxWidth/block.anchor.horizontal); - } else { - space = block.anchor.right; - measurement.minWidth = measurement.constrainWidth(space + block.width); - } - } else if ( !isNaN(block.anchor.horizontal) ) { - measurement.minWidth = measurement.constrainWidth(Math.abs(block.anchor.offsetX) + block.width); - } - - if ( !isNaN(block.anchor.top) ) { - if ( !isNaN(block.anchor.bottom) ) { - space = block.anchor.top + block.anchor.bottom; - measurement.minHeight = measurement.constrainHeight(space + block.blockBounds.minHeight); - measurement.maxHeight = measurement.constrainHeight(space + block.blockBounds.maxHeight); - } else if ( !isNaN(block.anchor.vertical) ) { - space = block.anchor.top - block.anchor.offsetY; - measurement.minHeight = measurement.constrainHeight(space + block.blockBounds.minHeight/block.anchor.vertical); - measurement.maxHeight = measurement.constrainHeight(space + block.blockBounds.maxHeight/block.anchor.vertical); - } else { - space = block.anchor.top; - measurement.minHeight = measurement.constrainHeight(space + block.height); - } - } else if ( !isNaN(block.anchor.bottom) ) { - if ( !isNaN(block.anchor.vertical) ) { - space = block.anchor.bottom + block.anchor.offsetY; - measurement.minHeight = measurement.constrainHeight(space + block.blockBounds.minHeight/block.anchor.vertical); - measurement.maxHeight = measurement.constrainHeight(space + block.blockBounds.maxHeight/block.anchor.vertical); - } else { - space = block.anchor.bottom; - measurement.minHeight = measurement.constrainHeight(space + block.height); - } - } else if ( !isNaN(block.anchor.vertical) ) { - measurement.minHeight = measurement.constrainHeight(Math.abs(block.anchor.offsetY) + block.height); - } - } - - private function updateAnchor(block:Block, parent:Block):void - { - var anchor:Box = block.anchor; - if ( !isNaN(anchor.left) ) { - if ( !isNaN(anchor.right) ) { - block.width = parent.displayWidth - anchor.left - anchor.right; - } else if ( !isNaN(anchor.horizontal) ) { - block.width = (anchor.horizontal * parent.displayWidth) - anchor.left + anchor.offsetX; - } - - block.x = anchor.left; - } else if ( !isNaN(anchor.right) ) { - if ( !isNaN(anchor.horizontal) ) { - block.width = (anchor.horizontal * parent.displayWidth) - anchor.right + anchor.offsetX; - } - - block.x = parent.displayWidth - block.width - anchor.right; - } else if ( !isNaN(anchor.horizontal) ) { - block.x = anchor.horizontal * (parent.displayWidth - block.width) + anchor.offsetX; - } - - if ( !isNaN(anchor.top) ) { - if ( !isNaN(anchor.bottom) ) { - block.height = parent.displayHeight - anchor.top - anchor.bottom; - } else if ( !isNaN(anchor.vertical) ) { - block.height = (anchor.vertical * parent.displayHeight) - anchor.top + anchor.offsetY; - } - - block.y = anchor.top; - } else if ( !isNaN(anchor.bottom) ) { - if ( !isNaN(anchor.vertical) ) { - block.height = (anchor.vertical * parent.displayHeight) - anchor.bottom + anchor.offsetY; - } - - block.y = parent.displayHeight - block.height - anchor.bottom; - } else if ( !isNaN(anchor.vertical) ) { - block.y = anchor.vertical * (parent.displayHeight - block.height) + anchor.offsetY; - } - } - - } -} diff --git a/src/reflex/layout/ILayoutAlgorithm.as b/src/reflex/layout/ILayoutAlgorithm.as deleted file mode 100644 index 2329a599..00000000 --- a/src/reflex/layout/ILayoutAlgorithm.as +++ /dev/null @@ -1,11 +0,0 @@ -package reflex.layout -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - - public interface ILayoutAlgorithm - { - function measure(target:DisplayObjectContainer):void; - function layout(target:DisplayObjectContainer):void; - } -} \ No newline at end of file diff --git a/src/reflex/layout/ILayoutWrapper.as b/src/reflex/layout/ILayoutWrapper.as deleted file mode 100644 index e600807e..00000000 --- a/src/reflex/layout/ILayoutWrapper.as +++ /dev/null @@ -1,28 +0,0 @@ -package reflex.layout -{ - import flash.display.DisplayObject; - - public interface ILayoutWrapper - { - function get target():DisplayObject; - function set target(value:DisplayObject):void; - - function get algorithm():ILayoutAlgorithm; - function set algorithm(value:ILayoutAlgorithm):void; - - function get freeform():Boolean; // if false, constrain using ILayout algorithm, - function set freeform(value:Boolean):void; // otherwise allow to do its own thing - - function get shift():Number; - function set shift(value:Number):void; - - function get shiftSize():Number; - function set shiftSize(value:Number):void; - - function invalidate(measure:Boolean = false):void; - - function validate():void; - function measure():void; - function layout():void; - } -} \ No newline at end of file diff --git a/src/reflex/layout/LayoutWrapper.as b/src/reflex/layout/LayoutWrapper.as deleted file mode 100644 index 5c886cb3..00000000 --- a/src/reflex/layout/LayoutWrapper.as +++ /dev/null @@ -1,199 +0,0 @@ -package reflex.layout -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.events.Event; - import flash.events.EventDispatcher; - import flash.events.IEventDispatcher; - import flash.utils.Dictionary; - - import flight.events.PropertyEvent; - - import reflex.events.InvalidationEvent; - - public class LayoutWrapper implements IEventDispatcher, ILayoutWrapper - { - public static const MEASURE:String = "measure"; - public static const LAYOUT:String = "layout"; - - public static var layoutIndex:Dictionary = new Dictionary(true); - public static function getLayout(key:DisplayObject):LayoutWrapper - { - return layoutIndex[key]; // TODO: resolve the circular reference holding both display and Layout in memory - var reference:Dictionary = layoutIndex[key]; - for (var i:* in reference) { - return LayoutWrapper(i); - } - return null; - } - - private static var measurePhase:Boolean = InvalidationEvent.registerPhase(MEASURE, 0x80, false); - private static var layoutPhase:Boolean = InvalidationEvent.registerPhase(LAYOUT, 0x40, true); - - [Bindable] - public var freeform:Boolean = false; - - [Bindable] - public var algorithm:ILayoutAlgorithm; - - [Bindable] - public var shift:Number = 0; - - [Bindable] - public var shiftSize:Number = 0; - - protected var dispatcher:IEventDispatcher; - - private var validating:Boolean = false; - private var reference:Dictionary = new Dictionary(true); - - private var _target:DisplayObject; - - public function LayoutWrapper(target:DisplayObject = null) - { - reference[this] = true; // used to maintain a weak-reference - this.target = target; - } - - [Bindable(event="targetChange")] - public function get target():DisplayObject - { - return _target; - } - public function set target(value:DisplayObject):void - { - if (_target == value) { - return; - } - - if (_target != null) { - _target.removeEventListener(MEASURE, onMeasure); - _target.removeEventListener(LAYOUT, onLayout); - delete layoutIndex[_target]; - } - - var oldValue:Object = _target; - _target = value; - - if (_target != null) { - _target.addEventListener(MEASURE, onMeasure, false, 0xF, true); - _target.addEventListener(LAYOUT, onLayout, false, 0xF, true); - if (layoutIndex[_target] != null) { - getLayout(_target).target = null; - } - layoutIndex[_target] = this;//reference; - - invalidate(true); - invalidate(); - } - - PropertyEvent.dispatchChange(this, "target", oldValue, _target); - } - - public function invalidate(children:Boolean = false):void - { - if (validating || _target == null) { - return; - } - - InvalidationEvent.invalidate(_target, LAYOUT); - - if (children) { - InvalidationEvent.invalidate(_target, MEASURE); - } else { - var parent:LayoutWrapper = getLayout(_target.parent); - if (parent != null ) { - parent.invalidate(true); - } - } - } - - public function validate():void - { - if (validating || _target == null) { - return; - } - validating = true; - - if (freeform) { - layout(); - } - - if (algorithm != null && _target is DisplayObjectContainer) { - algorithm.layout( DisplayObjectContainer(_target) ); - } - - validating = false; - } - - public function layout():void - { - } - - public function measure():void - { - var container:DisplayObjectContainer = _target as DisplayObjectContainer; - if (container == null) { - return; - } - - if (algorithm != null) { - algorithm.measure(container); - } - } - - private function onLayout(event:Event):void - { - validate(); - } - - private function onMeasure(event:Event):void - { - measure(); - } - - public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void - { - if (dispatcher == null) { - dispatcher = new EventDispatcher(this); - } - - dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference); - } - - public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void - { - if (dispatcher != null) { - dispatcher.removeEventListener(type, listener, useCapture); - } - } - - public function dispatchEvent(event:Event):Boolean - { - var result:Boolean = false; - if (_target != null && _target.hasEventListener(event.type)) { - result = _target.dispatchEvent(event); - } - if (dispatcher != null && dispatcher.hasEventListener(event.type)) { - result = dispatcher.dispatchEvent(event); - } - return result; - } - - public function hasEventListener(type:String):Boolean - { - if (dispatcher != null) { - return dispatcher.hasEventListener(type); - } - return false; - } - - public function willTrigger(type:String):Boolean - { - if (dispatcher != null) { - return dispatcher.willTrigger(type); - } - return false; - } - } -} \ No newline at end of file diff --git a/src/reflex/layout/ScrollBlock.as b/src/reflex/layout/ScrollBlock.as deleted file mode 100644 index 0708ad54..00000000 --- a/src/reflex/layout/ScrollBlock.as +++ /dev/null @@ -1,145 +0,0 @@ -package reflex.layout -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.geom.Rectangle; - - import flight.binding.Bind; - import flight.events.PropertyEvent; - import flight.position.IPosition; - import flight.position.Position; - - public class ScrollBlock extends Block - { - [Bindable] - public var hPosition:IPosition = new Position(); // TODO: implement lazy instantiation of Position - - [Bindable] - public var vPosition:IPosition = new Position(); - - private var _width:Number = 0; - private var _height:Number = 0; - - public function ScrollBlock(target:DisplayObject=null, scale:Boolean=false) - { - Bind.addBinding(this, "hPosition.space", this, "width", true); - Bind.addBinding(this, "vPosition.space", this, "height", true); - Bind.addBinding(this, "hPosition.size", this, "displayWidth"); - Bind.addBinding(this, "vPosition.size", this, "displayHeight"); - - Bind.addListener(this, onPositionChange, this, "hPosition.value"); - Bind.addListener(this, onPositionChange, this, "vPosition.value"); - Bind.addListener(this, onPositionChange, this, "hPosition.space"); - Bind.addListener(this, onPositionChange, this, "vPosition.space"); - Bind.addListener(this, onPositionChange, this, "hPosition.filled"); - Bind.addListener(this, onPositionChange, this, "vPosition.filled"); - - hPosition.stepSize = vPosition.stepSize = 10; - hPosition.skipSize = vPosition.skipSize = 100; - - super(target, scale); - } - - override public function set target(value:DisplayObject):void - { - if (target == value) { - return; - } - - if (target != null) { - target.scrollRect = null; - } - - super.target = value; - } - - override public function get width():Number - { - return _width; - } - override public function set width(value:Number):void - { - if (_width == value) { - return; - } - - super.width = value; - _width = PropertyEvent.change(this, "width", _width, value); - PropertyEvent.dispatch(this); - } - - override public function get height():Number - { - return _height; - } - override public function set height(value:Number):void - { - if (_height == value) { - return; - } - - super.height = value; - _height = PropertyEvent.change(this, "height", _height, value); - PropertyEvent.dispatch(this); - } - - override public function get blockBounds():Bounds - { - return bounds; - } - - override public function measure():void - { - var container:DisplayObjectContainer = target as DisplayObjectContainer; - if (container == null) { - return; - } - - if (algorithm != null) { - algorithm.measure(container); - return; - } - - var measurement:Bounds = new Bounds(); - - for (var i:int = 0; i < container.numChildren; i++) { - var display:DisplayObject = container.getChildAt(i); - var child:Block = getLayout(display) as Block; - if (child == null || child.freeform) { - continue; - } - - measurement.minWidth = measurement.constrainWidth( - display.x + child.width + padding.right + child.margin.right); - - measurement.minHeight = measurement.constrainHeight( - display.y + child.height + padding.bottom + child.margin.bottom); - } - - updateMeasurement(measurement); - } - - private function onPositionChange(prop:String, olVal:Number, percent:Number):void - { - if (target == null) { - return; - } - - if (hPosition.filled && vPosition.filled) { - target.scrollRect = null; - } else { - var rect:Rectangle = target.scrollRect; - if (rect == null) { - rect = new Rectangle(hPosition.value, vPosition.value, hPosition.space, vPosition.space); - } else { - rect.x = hPosition.value; - rect.y = vPosition.value; - rect.width = hPosition.space; - rect.height = vPosition.space; - } - target.scrollRect = rect; - } - } - - } -} \ No newline at end of file diff --git a/src/reflex/layout/Stack.as b/src/reflex/layout/Stack.as deleted file mode 100644 index 62b3d9f3..00000000 --- a/src/reflex/layout/Stack.as +++ /dev/null @@ -1,97 +0,0 @@ -package reflex.layout -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.geom.Rectangle; - - public class Stack implements ILayoutAlgorithm - { - // TODO: implement horizontal, 'tsall vert right now - public var horizontal:Boolean; - - public function measure(target:DisplayObjectContainer):void - { - var block:Block = LayoutWrapper.getLayout(target) as Block; - if (block == null) { - return; - } - - var measurement:Bounds = new Bounds(); - var staticWidth:Number = block.padding.left + block.padding.right; - var staticHeight:Number = block.padding.top + block.padding.bottom; - - var stackMargin:Box = new Box(); - var margin:Box; - var hPad:Number = block.padding.horizontal; - var vPad:Number = block.padding.vertical; - - for (var i:int = 0; i < target.numChildren; i++) { - var display:DisplayObject = target.getChildAt(i); - var child:Block = LayoutWrapper.getLayout(display) as Block; - if (child == null || child.freeform) { - continue; - } - - margin = stackMargin.clone().merge(child.margin); - - - stackMargin.top = child.margin.bottom; - - var space:Number = margin.left + margin.right; - staticHeight += child.height + margin.top + vPad; - measurement.minHeight = measurement.constrainHeight(staticHeight); - measurement.minWidth = measurement.constrainWidth(space + child.blockBounds.minWidth); - measurement.maxWidth = measurement.constrainWidth(space + child.blockBounds.maxWidth); - } - - if (margin != null) { - measurement.minWidth += margin.bottom - hPad; - } - block.updateMeasurement(measurement); - } - - public function layout(target:DisplayObjectContainer):void - { - var block:Block = LayoutWrapper.getLayout(target) as Block; - if (block == null) { - return; - } - - var stackMargin:Box = new Box(); - var stackArea:Rectangle = new Rectangle(0, 0, block.displayWidth, block.displayHeight); - stackArea.left += block.padding.left; - stackArea.top += block.padding.top; - stackArea.right -= block.padding.right; - stackArea.bottom -= block.padding.bottom; - - var margin:Box; - var pos:Number; - - var hPad:Number = block.padding.horizontal; - var vPad:Number = block.padding.vertical; - var lastDock:String = Align.NONE; - - for (var i:int = 0; i < target.numChildren; i++) { - var display:DisplayObject = target.getChildAt(i); - var child:Block = LayoutWrapper.getLayout(display) as Block; - if (child == null || child.freeform) { - continue; - } - - margin = stackMargin.clone().merge(child.margin); - - child.x = stackArea.x + margin.left; - child.y = stackArea.y + margin.top; - // TODO: implement alignment, not always stretch (fill) - child.width = stackArea.width - margin.left - margin.right; - if (stackArea.top < (pos = child.y + child.height + vPad) ) { - stackArea.top = pos; - stackMargin.top = child.margin.bottom; - } - child.y -= block.shift; - } - } - - - } -} \ No newline at end of file diff --git a/src/reflex/layouts/BasicLayout.as b/src/reflex/layouts/BasicLayout.as index 58183856..b66a25f8 100644 --- a/src/reflex/layouts/BasicLayout.as +++ b/src/reflex/layouts/BasicLayout.as @@ -1,19 +1,17 @@ package reflex.layouts { - import flash.display.DisplayObject; - import flash.events.Event; - import flash.events.IEventDispatcher; + import flash.geom.Point; import flash.geom.Rectangle; - import flight.binding.Bind; - - import reflex.styles.resolveStyle; - import reflex.events.InvalidationEvent; + import reflex.animation.AnimationToken; + import reflex.graphics.IGraphicItem; + import reflex.graphics.Line; import reflex.measurement.resolveHeight; import reflex.measurement.resolveWidth; import reflex.measurement.setSize; import reflex.styles.hasStyle; + import reflex.styles.resolveStyle; [LayoutProperty(name="style.left", measure="true")] [LayoutProperty(name="style.right", measure="true")] @@ -21,64 +19,116 @@ package reflex.layouts [LayoutProperty(name="style.bottom", measure="true")] [LayoutProperty(name="style.horizontalCenter", measure="true")] [LayoutProperty(name="style.verticalCenter", measure="true")] + [LayoutProperty(name="horizontalCenter", measure="true")] [LayoutProperty(name="width", measure="true")] [LayoutProperty(name="height", measure="true")] /** + * Recognizes style elements familiar to Flex developers such as left, right, top, bottom, horizontalCenter and verticalCenter. + * * @alpha **/ public class BasicLayout extends Layout implements ILayout { - override public function measure(children:Array):Point + override public function measure(content:Array):Point { - super.measure(children); - var point:Point = new Point(0, 0); - for each(var item:Object in children) { - var xp:Number = item.x + resolveWidth(item); - var yp:Number = item.y + resolveHeight(item); + var point:Point = super.measure(content); + for each(var child:Object in content) { + + var width:Number = resolveWidth(child); + var height:Number = resolveHeight(child); + var left:Number = resolveStyle(child, "left") as Number; + var right:Number = resolveStyle(child, "right") as Number; + var top:Number = resolveStyle(child, "top") as Number; + var bottom:Number = resolveStyle(child, "bottom") as Number; + var horizontalCenter:Number = resolveStyle(child, "horizontalCenter") as Number; + var verticalCenter:Number = resolveStyle(child, "verticalCenter") as Number; + + var xp:Number = child.x + width; + var yp:Number = child.y + height; + + if(hasStyle(child, "left") && hasStyle(child, "right")) { + xp = left + width + right; + } else if(hasStyle(child, "left")) { + xp = left + width; + } else if(hasStyle(child, "right")) { + xp = width + right; + } else if(hasStyle(child, "horizontalCenter")) { + xp = width + Math.abs(horizontalCenter); + } + + if(hasStyle(child, "top") && hasStyle(child, "bottom")) { + yp = top + height + bottom; + } else if(hasStyle(child, "top")) { + yp = top + height; + } else if(hasStyle(child, "bottom")) { + yp = height + bottom; + } else if(hasStyle(child, "verticalCenter")) { + yp = height + Math.abs(verticalCenter); + } + if(!isNaN(xp)) { point.x = Math.max(point.x, xp); } if(!isNaN(yp)) { point.y = Math.max(point.y, yp); } } return point; } - override public function update(children:Array, rectangle:Rectangle):void + override public function update(content:Array, tokens:Array, rectangle:Rectangle):Array { - super.update(children, rectangle); - for each(var child:Object in children) { - var width:Number = reflex.measurement.resolveWidth(child); - var height:Number = reflex.measurement.resolveHeight(child); - var left:Number = reflex.styles.resolveStyle(child, "left") as Number; - var right:Number = reflex.styles.resolveStyle(child, "right") as Number; - var top:Number = reflex.styles.resolveStyle(child, "top") as Number; - var bottom:Number = reflex.styles.resolveStyle(child, "bottom") as Number; - var horizontalCenter:Number = reflex.styles.resolveStyle(child, "horizontalCenter") as Number; - var verticalCenter:Number = reflex.styles.resolveStyle(child, "verticalCenter") as Number; + super.update(content, tokens, rectangle); + + var length:int = content ? content.length : 0; + for(var i:int = 0; i < length; i++) { + var child:Object = content[i]; + var token:AnimationToken = tokens[i]; + if(token == null) { return []; } + var width:Number = resolveWidth(child, rectangle.width); + var height:Number = resolveHeight(child, rectangle.height); + var left:Number = resolveStyle(child, "left") as Number; + var right:Number = resolveStyle(child, "right") as Number; + var top:Number = resolveStyle(child, "top") as Number; + var bottom:Number = resolveStyle(child, "bottom") as Number; + var horizontalCenter:Number = resolveStyle(child, "horizontalCenter") as Number; + var verticalCenter:Number = resolveStyle(child, "verticalCenter") as Number; if(hasStyle(child, "left") && hasStyle(child, "right")) { - child.x = left; - width = rectangle.width - child.x - right; + token.x = Math.round(left); + width = rectangle.width - token.x - right; } else if(hasStyle(child, "left")) { - child.x = reflex.styles.resolveStyle(child, "left") as Number; + token.x = Math.round(left); } else if(hasStyle(child, "right")) { - child.x = rectangle.width - width - right; + token.x = Math.round(rectangle.width - width - right); } else if(hasStyle(child, "horizontalCenter")) { - child.x = rectangle.width/2 - width/2; + token.x = Math.round(rectangle.width/2 - width/2 + horizontalCenter); } if(hasStyle(child, "top") && hasStyle(child, "bottom")) { - child.y = top; - height = rectangle.height - child.y - bottom; + token.y = Math.round(top); + height = rectangle.height - token.y - bottom; } else if(hasStyle(child, "top")) { - child.y = top; + token.y = Math.round(top); } else if(hasStyle(child, "bottom")) { - child.y = rectangle.height - height - bottom; + token.y = Math.round(rectangle.height - height - bottom); } else if(hasStyle(child, "verticalCenter")) { - child.y = rectangle.height/2 - height/2; + token.y = Math.round(rectangle.height/2 - height/2 + verticalCenter); } - reflex.measurement.setSize(child, width, height); + if(width > 0 && height > 0) { // for shapes which haven't been drawn to yet + //reflex.measurement.setSize(child, Math.round(width), Math.round(height)); + token.width = Math.round(width); + token.height = Math.round(height); + } else if(child is IGraphicItem) { // sometime width/height is 0 for lines + //reflex.measurement.setSize(child, Math.round(width), Math.round(height)); + token.width = Math.round(width); + token.height = Math.round(height); + /* + if(width == 0) { width = (child as Line).stroke.weight; } + if(height == 0) { height = (child as Line).stroke.weight; } + reflex.measurement.setSize(child, Math.round(width), Math.round(height)); + */ + } } + return tokens; } } diff --git a/src/reflex/layouts/DockLayout.as b/src/reflex/layouts/DockLayout.as index f54d1acb..eff00680 100644 --- a/src/reflex/layouts/DockLayout.as +++ b/src/reflex/layouts/DockLayout.as @@ -1,31 +1,38 @@ package reflex.layouts { - import flash.display.DisplayObject; - import flash.events.IEventDispatcher; import flash.geom.Point; import flash.geom.Rectangle; - import flight.binding.Bind; - - import reflex.styles.hasStyle; - import reflex.styles.resolveStyle; - import reflex.events.InvalidationEvent; - import reflex.layout.Align; import reflex.measurement.resolveHeight; import reflex.measurement.resolveWidth; + import reflex.styles.resolveStyle; [LayoutProperty(name="style.dock", measure="true")] [LayoutProperty(name="width", measure="true")] [LayoutProperty(name="height", measure="true")] + + /** + * Provides a docking layout for common use cases which might otherwise require nested containers. + * + * @alpha + */ public class DockLayout extends Layout implements ILayout { - override public function measure(children:Array):Point + static public const NONE:String = null; + static public const LEFT:String = "left"; + static public const RIGHT:String = "right"; + static public const TOP:String = "top"; + static public const BOTTOM:String = "bottom"; + static public const FILL:String = "fill"; + static public const CENTER:String = "center"; + + override public function measure(content:Array):Point { - super.measure(children); + var point:Point = super.measure(content); var gap:Number = 5; - var point:Point = new Point(gap, 0); - for each(var child:Object in children) { + point.x = gap; + for each(var child:Object in content) { var width:Number = reflex.measurement.resolveWidth(child); var height:Number = reflex.measurement.resolveHeight(child); point.x += width + gap; @@ -34,64 +41,66 @@ package reflex.layouts return point; } - override public function update(children:Array, rectangle:Rectangle):void + override public function update(content:Array, tokens:Array, rectangle:Rectangle):Array { - super.update(children, rectangle); - var length:int = children.length; + super.update(content, tokens, rectangle); + var length:int = content.length; for(var i:int = 0; i < length; i++) { - var child:Object = children[i]; + var child:Object = content[i]; var width:Number = reflex.measurement.resolveWidth(child); var height:Number = reflex.measurement.resolveHeight(child); - var dock:String = reflex.styles.resolveStyle(child, "dock", null, Align.NONE) as String; - var align:String = reflex.styles.resolveStyle(child, "align", null, Align.NONE) as String; + var dock:String = reflex.styles.resolveStyle(child, "dock", null, NONE) as String; + var align:String = reflex.styles.resolveStyle(child, "align", null, NONE) as String; switch (dock) { - case Align.LEFT : + case LEFT : child.x = rectangle.x; child.y = rectangle.y; - if (align == Align.NONE) { + if (align == NONE) { child.setSize(width, rectangle.height); - } else if (align == Align.BOTTOM) { + } else if (align == BOTTOM) { child.y = rectangle.y + rectangle.height - height; } break; - case Align.TOP : + case TOP : child.x = rectangle.x; child.y = rectangle.y; - if (align == Align.NONE) { + if (align == NONE) { child.setSize(rectangle.width, height) - } else if (align == Align.RIGHT) { + } else if (align == RIGHT) { child.x = rectangle.x + rectangle.width - width; } break; - case Align.RIGHT : + case RIGHT : child.x = rectangle.x + rectangle.width - width; child.y = rectangle.y; - if (align == Align.NONE) { + if (align == NONE) { child.setSize(width, rectangle.height); - } else if (align == Align.BOTTOM) { + } else if (align == BOTTOM) { child.y = rectangle.y + rectangle.height - height; } break; - case Align.BOTTOM : + case BOTTOM : child.x = rectangle.x; child.y = rectangle.y + rectangle.height - height; - if (align == Align.NONE) { + if (align == NONE) { child.setSize(rectangle.width, height); - } else if (align == Align.RIGHT) { + } else if (align == RIGHT) { child.x = rectangle.x + rectangle.width - width; } break; - case Align.FILL : + case FILL : child.x = rectangle.x; child.y = rectangle.y; child.setSize(rectangle.width, rectangle.height) break; - case Align.CENTER : + case CENTER : + default: child.x = rectangle.width/2 - width/2; child.y = rectangle.height/2 - height/2; break; } } + return tokens; } } diff --git a/src/reflex/layouts/HorizontalLayout.as b/src/reflex/layouts/HorizontalLayout.as index d30439da..0e13eca9 100644 --- a/src/reflex/layouts/HorizontalLayout.as +++ b/src/reflex/layouts/HorizontalLayout.as @@ -1,53 +1,122 @@ package reflex.layouts { - import flash.display.DisplayObject; - import flash.events.IEventDispatcher; + import flash.geom.Point; import flash.geom.Rectangle; - import flight.binding.Bind; - - import reflex.events.InvalidationEvent; + import reflex.animation.AnimationToken; + import reflex.data.IPosition; + import reflex.measurement.calculateAvailableSpace; + import reflex.measurement.calculatePercentageTotals; + import reflex.measurement.resolveHeight; import reflex.measurement.resolveWidth; - - + import reflex.measurement.setSize; + import reflex.styles.resolveStyle; + [LayoutProperty(name="width", measure="true")] [LayoutProperty(name="height", measure="true")] /** + * Provides a measured layout from left to right. + * * @alpha **/ public class HorizontalLayout extends Layout implements ILayout { - [Bindable] public var gap:Number = 5; + public var gap:Number = 5; + public var edging:Boolean = false; + public var verticalAlign:String = "top"; // bottom, middle, top, justify + public var horizontalAlign:String = "left"; // left, center, right + + private var _position:IPosition; + + [Binding(target="target.horizontal")] - override public function measure(children:Array):Point + public function get position():IPosition { return _position; } + public function set position(value:IPosition):void { + /*if(_position is IEventDispatcher) { + (_position as IEventDispatcher).removeEventListener("valueChange", onPositionChange, false); + }*/ + _position = value; + /*if(_position is IEventDispatcher) { + (_position as IEventDispatcher).addEventListener("valueChange", onPositionChange, false, 0, true); + }*/ + } + + public function HorizontalLayout(gap:Number = 5, verticalAlign:String = "top", edging:Boolean = false):void { + super(); + this.gap = gap; + this.verticalAlign = verticalAlign; + this.edging = edging; + } + + override public function measure(content:Array):Point { - super.measure(children); - var point:Point = new Point(gap, 0); - for each(var child:Object in children) { + var point:Point = super.measure(content); + point.x = edging ? gap/2 : 0; + for each(var child:Object in content) { var width:Number = reflex.measurement.resolveWidth(child); var height:Number = reflex.measurement.resolveHeight(child); point.x += width + gap; point.y = Math.max(point.y, height); } + point.x -= edging ? gap/2 : gap; return point; } - override public function update(children:Array, rectangle:Rectangle):void + override public function update(content:Array, tokens:Array, rectangle:Rectangle):Array { - super.update(children, rectangle); - var position:Number = gap; - var length:int = children.length; - for(var i:int = 0; i < length; i++) { - var child:Object = children[i]; - var width:Number = reflex.measurement.resolveWidth(child); - var height:Number = reflex.measurement.resolveHeight(child); - child.x = position; - child.y = rectangle.height/2 - height/2; - position += width + gap; + super.update(content, tokens, rectangle); + if(content) { + + var gap:Number = reflex.styles.resolveStyle(target, "gap", null, this.gap) as Number; + var verticalAlign:String = reflex.styles.resolveStyle(target, "verticalAlign", null, this.verticalAlign) as String; + //var horizontalAlign:String = reflex.styles.resolveStyle(target, "horizontalAlign", null, this.horizontalAlign) as String; + + // this takes a few passes for percent-based measurement. we can probably speed it up later + var availableSpace:Point = reflex.measurement.calculateAvailableSpace(content, rectangle); + var percentageTotals:Point = reflex.measurement.calculatePercentageTotals(content); + + var position:Number = edging ? gap/2 : 0; + var length:int = content.length; + + if(_position) { + position -= _position.value*100; + } + + availableSpace.x -= edging ? gap*length : gap*(length-1); + for(var i:int = 0; i < length; i++) { + var child:Object = content[i]; + var token:AnimationToken = tokens[i]; + var width:Number = reflex.measurement.resolveWidth(token, availableSpace.x, percentageTotals.x); // calculate percentWidths based on available width and normalized percentages + var height:Number = reflex.measurement.resolveHeight(token, rectangle.height); // calculate percentHeights based on full height and with no normalization + if(verticalAlign == "justify") + height = rectangle.height; + + token.x = Math.round(position); + + switch(verticalAlign) { + case "middle": + case "center": + token.y = Math.round(rectangle.height/2 - height/2); + break; + case "top": + case "justify": + token.y = 0; + break; + case "bottom": + token.y = Math.round(rectangle.height - height); + break; + } + //child.y = Math.round(rectangle.height/2 - height/2); + //reflex.measurement.setSize(child, Math.round(width), Math.round(height)); + token.width = Math.round(width); + token.height = Math.round(height); + position += width + gap; + } } + return tokens; } } diff --git a/src/reflex/layouts/ILayout.as b/src/reflex/layouts/ILayout.as index 77d33362..87f22aef 100644 --- a/src/reflex/layouts/ILayout.as +++ b/src/reflex/layouts/ILayout.as @@ -5,6 +5,9 @@ package reflex.layouts import flash.geom.Rectangle; /** + * This interface is used to Integrate custom layouts into the Reflex layout and measurement system. + * You must implement this interface when creating a custom layout. + * * @alpha */ public interface ILayout @@ -13,7 +16,8 @@ package reflex.layouts function get target():IEventDispatcher; function set target(value:IEventDispatcher):void; - function measure(children:Array):Point; - function update(children:Array, rectangle:Rectangle):void; + function measure(content:Array):Point; + function update(content:Array, tokens:Array, rectangle:Rectangle):Array; + } } \ No newline at end of file diff --git a/src/reflex/layouts/Layout.as b/src/reflex/layouts/Layout.as index 6549bb2e..42e8c9af 100644 --- a/src/reflex/layouts/Layout.as +++ b/src/reflex/layouts/Layout.as @@ -7,38 +7,49 @@ package reflex.layouts import flash.geom.Rectangle; import flash.utils.Dictionary; - import flight.binding.Bind; - - import reflex.events.InvalidationEvent; + import reflex.binding.Bind; + import reflex.display.PropertyDispatcher; + import reflex.events.DataChangeEvent; + import reflex.invalidation.Invalidation; import reflex.metadata.resolveBindings; + import reflex.metadata.resolveDataListeners; import reflex.metadata.resolveEventListeners; import reflex.metadata.resolveLayoutProperties; - import reflex.metadata.resolvePropertyListeners; //[LayoutProperty(name="layout", measure="true")] //[LayoutProperty(name="measurements", measure="true")] /** + * The Layout class provides automated metadata handling for layouts which extend it. + * It is recommended that you extend this class to create custom layouts, but it's not required. + * * @alpha **/ - public class Layout extends EventDispatcher + public class Layout extends PropertyDispatcher { private var attached:Dictionary = new Dictionary(true); + private var _target:IEventDispatcher; - [Bindable] public var target:IEventDispatcher; + [Bindable(event="targetChange")] + public function get target():IEventDispatcher { return _target; } + public function set target(value:IEventDispatcher):void + { + notify("target", _target, _target = value); + } public function Layout() { reflex.metadata.resolveBindings(this); reflex.metadata.resolveEventListeners(this); - reflex.metadata.resolvePropertyListeners(this); + reflex.metadata.resolveDataListeners(this); + Bind.addListener(this, onInvalidateLayout, this, "target.width"); Bind.addListener(this, onInvalidateLayout, this, "target.height"); } - public function measure(children:Array):Point { + public function measure(content:Array):Point { // this method of listening for layout invalidating changes is very much experimental - for each(var child:IEventDispatcher in children) { - if(attached[child] != true) { + for each(var child:IEventDispatcher in content) { + if (attached[child] != true) { reflex.metadata.resolveLayoutProperties(this, child, onInvalidateLayout); attached[child] = true; } @@ -46,20 +57,21 @@ package reflex.layouts return new Point(0, 0); } - public function update(children:Array, rectangle:Rectangle):void { + public function update(content:Array, tokens:Array, rectangle:Rectangle):Array { // this method of listening for layout invalidating changes is very much experimental - for each(var child:IEventDispatcher in children) { - if(attached[child] != true) { - reflex.metadata.resolveLayoutProperties(this, child, onInvalidateLayout); + for each(var child:Object in content) { + if (child is IEventDispatcher && attached[child] != true) { + reflex.metadata.resolveLayoutProperties(this, child as IEventDispatcher, onInvalidateLayout); attached[child] = true; } } + return null; } private function onInvalidateLayout(object:*):void { - if(target is DisplayObject) { - InvalidationEvent.invalidate(target as DisplayObject, "measure"); - InvalidationEvent.invalidate(target as DisplayObject, "layout"); + if (target is DisplayObject) { + //Invalidation.invalidate(target as DisplayObject, "measure"); + //Invalidation.invalidate(target as DisplayObject, "layout"); } } diff --git a/src/reflex/layouts/TileLayout.as b/src/reflex/layouts/TileLayout.as new file mode 100644 index 00000000..347c9b70 --- /dev/null +++ b/src/reflex/layouts/TileLayout.as @@ -0,0 +1,51 @@ +package reflex.layouts +{ + import flash.geom.Point; + import flash.geom.Rectangle; + + import reflex.animation.AnimationToken; + + public class TileLayout extends Layout implements ILayout + { + + private var columns:int = 3; + public var gap:int = 10; + + override public function measure(content:Array):Point + { + var point:Point = super.measure(content); + /*point.y = edging ? gap/2 : 0; + for each(var child:Object in content) { + var width:Number = reflex.measurement.resolveWidth(child); + var height:Number = reflex.measurement.resolveHeight(child); + point.x = Math.max(point.x, width); + point.y += height + gap; + } + point.y -= edging ? gap/2 : gap;*/ + return point; + } + + override public function update(content:Array, tokens:Array, rectangle:Rectangle):Array + { + super.update(content, tokens, rectangle); + + var point:Point = new Point(gap, gap); + var length:int = content ? content.length : 0; + for(var i:int = 0; i < length; i++) { + var item:Object = content[i]; + var token:AnimationToken = tokens[i]; + token.x = point.x; + token.y = point.y; + if(token.x + token.width > rectangle.width) { + point.x = gap; + point.y += token.height + gap; + token.x = point.x; + token.y = point.y; + } + point.x += token.width + gap; + } + return tokens; + } + + } +} \ No newline at end of file diff --git a/src/reflex/layouts/VerticalLayout.as b/src/reflex/layouts/VerticalLayout.as index 8bab265a..bc9da537 100644 --- a/src/reflex/layouts/VerticalLayout.as +++ b/src/reflex/layouts/VerticalLayout.as @@ -1,54 +1,109 @@ package reflex.layouts { - import flash.display.DisplayObject; - import flash.events.IEventDispatcher; + import flash.geom.Point; import flash.geom.Rectangle; - import flight.binding.Bind; - - import reflex.events.InvalidationEvent; + import reflex.animation.AnimationToken; + import reflex.framework.IMeasurablePercent; + import reflex.measurement.calculateAvailableSpace; + import reflex.measurement.calculatePercentageTotals; + import reflex.measurement.resolveHeight; import reflex.measurement.resolveWidth; + import reflex.measurement.setSize; + import reflex.styles.resolveStyle; [LayoutProperty(name="width", measure="true")] [LayoutProperty(name="height", measure="true")] /** + * Provides a measured layout from top to bottom. + * * @alpha **/ public class VerticalLayout extends Layout implements ILayout { - [Bindable] public var gap:Number = 5; + // not sure where these constants will go yet, use string for now + //static public const ALIGN_LEFT:String = "left"; + //static public const ALIGN_RIGHT:String = "right"; + //static public const ALIGN_CENTER:String = "center"; + //static public const ALIGN_JUSTIFY:String = "justify"; + + public var gap:Number = 5; + public var edging:Boolean = false; + public var horizontalAlign:String = "left"; + + public function VerticalLayout(gap:Number = 5, horizontalAlign:String = "top", edging:Boolean = false):void { + super(); + this.gap = gap; + this.horizontalAlign = horizontalAlign; + this.edging = edging; + } - override public function measure(children:Array):Point + override public function measure(content:Array):Point { - super.measure(children); - var point:Point = new Point(gap, 0); - for each(var child:Object in children) { + var point:Point = super.measure(content); + point.y = edging ? gap/2 : 0; + for each(var child:Object in content) { var width:Number = reflex.measurement.resolveWidth(child); var height:Number = reflex.measurement.resolveHeight(child); point.x = Math.max(point.x, width); point.y += height + gap; } + point.y -= edging ? gap/2 : gap; return point; } - override public function update(children:Array, rectangle:Rectangle):void + override public function update(content:Array, tokens:Array, rectangle:Rectangle):Array { - super.update(children, rectangle); - if(children) { - var position:Number = gap; - var length:int = children.length; + super.update(content, tokens, rectangle); + if(content) { + + // some style-binding might take care of this later + var gap:Number = reflex.styles.resolveStyle(target, "gap", Number, this.gap) as Number; + var horizontalAlign:String = reflex.styles.resolveStyle(target, "horizontalAlign", String, this.horizontalAlign) as String; + + // this takes a few passes for percent-based measurement. we can probably speed it up later + var availableSpace:Point = reflex.measurement.calculateAvailableSpace(content, rectangle); + var percentageTotals:Point = reflex.measurement.calculatePercentageTotals(content); + + + var position:Number = edging ? gap/2 : 0; + var length:int = content.length; + + availableSpace.y -= edging ? gap*length : gap*(length-1); for(var i:int = 0; i < length; i++) { - var child:Object = children[i]; - var width:Number = reflex.measurement.resolveWidth(child); - var height:Number = reflex.measurement.resolveHeight(child); - child.x = rectangle.width/2 - width/2; - child.y = position; + var child:Object = content[i]; + var token:AnimationToken = tokens[i]; + var width:Number = reflex.measurement.resolveWidth(token, rectangle.width); // calculate percentWidths based on full width and with no normalization + if(horizontalAlign == "justify") + width = rectangle.width; + var height:Number = reflex.measurement.resolveHeight(token, availableSpace.y, percentageTotals.y); // calculate percentHeights based on available height and normalized percentages + + + switch(horizontalAlign) { + case "center": + case "middle": + token.x = Math.round(rectangle.width/2 - width/2); + break; + case "justify": + case "left": + token.x = 0; + break; + case "right": + token.x = Math.round(rectangle.width - width); + break; + } + //child.x = Math.round(rectangle.width/2 - width/2); + token.y = Math.round(position); + //reflex.measurement.setSize(child, Math.round(width), Math.round(height)); + token.width = Math.round(width); + token.height = Math.round(height); position += height + gap; } } + return tokens; } } diff --git a/src/reflex/layouts/XYLayout.as b/src/reflex/layouts/XYLayout.as index 13949ae0..dca44807 100644 --- a/src/reflex/layouts/XYLayout.as +++ b/src/reflex/layouts/XYLayout.as @@ -1,14 +1,8 @@ package reflex.layouts { - import flash.display.DisplayObject; - import flash.events.Event; - import flash.events.IEventDispatcher; import flash.geom.Point; import flash.geom.Rectangle; - import flight.binding.Bind; - - import reflex.events.InvalidationEvent; import reflex.measurement.resolveHeight; import reflex.measurement.resolveWidth; @@ -18,47 +12,30 @@ package reflex.layouts [LayoutProperty(name="height", measure="true")] /** + * Provides basic measurement for containers which want to adjust children manually using x/y coordinates. + * * @alpha **/ public class XYLayout extends Layout implements ILayout { - override public function measure(children:Array):Point + override public function measure(content:Array):Point { - var point:Point = new Point(0, 0); - for each(var item:Object in children) { + var point:Point = super.measure(content); + for each(var item:Object in content) { var xp:Number = item.x + resolveWidth(item); var yp:Number = item.y + resolveHeight(item); point.x = Math.max(point.x, xp); point.y = Math.max(point.y, yp); } - //attachBindings(children); return point; } - override public function update(children:Array, rectangle:Rectangle):void + override public function update(content:Array, tokens:Array, rectangle:Rectangle):Array { - attachBindings(children); - } - - // update this for correct binding - // find the easiest Flash/AS3 option (add metadata functionality as well) - private function attachBindings(children:Array):void { - for each(var child:IEventDispatcher in children) { - Bind.addListener(child, onInvalidateMeasure, child, "x"); - Bind.addListener(child, onInvalidateMeasure, child, "y"); - Bind.addListener(child, onInvalidateMeasure, child, "width"); - Bind.addListener(child, onInvalidateMeasure, child, "height"); - Bind.addListener(child, onInvalidateMeasure, child, "measurements"); - Bind.addListener(child, onInvalidateMeasure, child, "layout"); - } - } - - private function onInvalidateMeasure(object:*):void { - if(target is DisplayObject) { - InvalidationEvent.invalidate(target as DisplayObject, "measure"); - } + super.update(content, tokens, rectangle); + return tokens; } } diff --git a/src/reflex/measurement/IMeasurable.as b/src/reflex/measurement/IMeasurable.as deleted file mode 100644 index 06d1d8ff..00000000 --- a/src/reflex/measurement/IMeasurable.as +++ /dev/null @@ -1,10 +0,0 @@ -package reflex.measurement -{ - public interface IMeasurable - { - - function get measurements():IMeasurements; - function setSize(width:Number, height:Number):void; - - } -} \ No newline at end of file diff --git a/src/reflex/measurement/IMeasurements.as b/src/reflex/measurement/IMeasurements.as deleted file mode 100644 index 5b1d0adf..00000000 --- a/src/reflex/measurement/IMeasurements.as +++ /dev/null @@ -1,18 +0,0 @@ -package reflex.measurement -{ - public interface IMeasurements - { - - function get expliciteWidth():Number; - function set expliciteWidth(value:Number):void; - - function get expliciteHeight():Number; - function set expliciteHeight(value:Number):void; - - function get measuredWidth():Number; - function set measuredWidth(value:Number):void; - - function get measuredHeight():Number; - function set measuredHeight(value:Number):void; - } -} \ No newline at end of file diff --git a/src/reflex/measurement/Measurements.as b/src/reflex/measurement/Measurements.as deleted file mode 100644 index 787e51c2..00000000 --- a/src/reflex/measurement/Measurements.as +++ /dev/null @@ -1,35 +0,0 @@ -package reflex.measurement -{ - - /** - * @alpha - */ - public class Measurements implements IMeasurements - { - - // todo: update for defined events - - [Bindable] public var minWidth:Number; - [Bindable] public var minHeight:Number; - - [Bindable] public var maxWidth:Number; - [Bindable] public var maxHeight:Number; - - [Bindable] public var expliciteWidth:Number; - [Bindable] public var expliciteHeight:Number; - - [Bindable] public var percentWidth:Number; - [Bindable] public var percentHeight:Number; - - [Bindable] public var measuredWidth:Number; - [Bindable] public var measuredHeight:Number; - - //private var _target:Object; - - public function Measurements(defaultWidth:Number = 160, defaultHeight:Number = 22) { - measuredWidth = defaultWidth; - measuredHeight = defaultHeight; - } - - } -} \ No newline at end of file diff --git a/src/reflex/measurement/calculateAvailableSpace.as b/src/reflex/measurement/calculateAvailableSpace.as new file mode 100644 index 00000000..2a4c3f7b --- /dev/null +++ b/src/reflex/measurement/calculateAvailableSpace.as @@ -0,0 +1,22 @@ +package reflex.measurement +{ + import flash.geom.Point; + import flash.geom.Rectangle; + import reflex.framework.IMeasurablePercent; + + public function calculateAvailableSpace(children:Array, rectangle:Rectangle):Point + { + var point:Point = new Point(rectangle.width, rectangle.height); + for each(var child:Object in children) { + if(!(child is IMeasurablePercent) || isNaN((child as IMeasurablePercent).percentWidth)) { + point.x -= reflex.measurement.resolveWidth(child); // excludes percent-based width + } + if(!(child is IMeasurablePercent) || isNaN((child as IMeasurablePercent).percentHeight)) { + point.y -= reflex.measurement.resolveHeight(child); // excludes percent-based height + } + } + point.x = Math.max(point.x, 0); + point.y = Math.max(point.y, 0); + return point; + } +} \ No newline at end of file diff --git a/src/reflex/measurement/calculatePercentageTotals.as b/src/reflex/measurement/calculatePercentageTotals.as new file mode 100644 index 00000000..b9bca319 --- /dev/null +++ b/src/reflex/measurement/calculatePercentageTotals.as @@ -0,0 +1,24 @@ +package reflex.measurement +{ + import flash.geom.Point; + import reflex.framework.IMeasurablePercent; + + public function calculatePercentageTotals(children:Array):Point + { + var point:Point = new Point(0, 0); + var length:int = children.length; + for(var i:int = 0; i < length; i++) { + var child:Object = children[i]; + if(child is IMeasurablePercent && !isNaN((child as IMeasurablePercent).percentWidth) ) { + point.x += (child as IMeasurablePercent).percentWidth; + } + if(child is IMeasurablePercent && !isNaN((child as IMeasurablePercent).percentHeight) ) { + point.y += (child as IMeasurablePercent).percentHeight; + } + } + point.x = Math.max(point.x, 100); + point.y = Math.max(point.y, 100); + return point; + } + +} \ No newline at end of file diff --git a/src/reflex/measurement/resolveHeight.as b/src/reflex/measurement/resolveHeight.as index 5f700fc1..0a552456 100644 --- a/src/reflex/measurement/resolveHeight.as +++ b/src/reflex/measurement/resolveHeight.as @@ -1,23 +1,29 @@ package reflex.measurement { - import reflex.display.ReflexDisplay; + import reflex.framework.IMeasurable; + import reflex.framework.IMeasurablePercent; /** * @alpha */ - public function resolveHeight(object:Object):Number + public function resolveHeight(object:Object, total:Number = NaN, percentageTotal:Number = 100):Number { - var measurements:IMeasurements; - if(object is IMeasurable) { - measurements = (object as IMeasurable).measurements; - return isNaN(measurements.expliciteHeight) ? measurements.measuredHeight : measurements.expliciteHeight; - } else if(object is IMeasurements) { - measurements = (object as IMeasurements); - return isNaN(measurements.expliciteHeight) ? measurements.measuredHeight : measurements.expliciteHeight; + //var explicit:IMeasurements; + //var measured:IMeasurements; + //var percent:IMeasurablePercent; + if (object is IMeasurable && !isNaN((object as IMeasurable).explicitHeight)) { // explicit height is defined + return (object as IMeasurable).explicitHeight; + } else if(object is IMeasurablePercent && !isNaN(total) && !isNaN((object as IMeasurablePercent).percentHeight)) { // support percent-based measurement + return total * (object as IMeasurablePercent).percentHeight/percentageTotal; + } else if(object is IMeasurable && !isNaN((object as IMeasurable).measuredHeight)) { // measured width is defined + return (object as IMeasurable).measuredHeight; + } else if(object != null) { // we'll try to find a width anyways (even on non-DisplayObjects) + try { + return object.height; + } catch(e:Error) {} + return 0; } else { - return (object!=null) ? object.height : NaN; + return 0; } - } - } \ No newline at end of file diff --git a/src/reflex/measurement/resolveWidth.as b/src/reflex/measurement/resolveWidth.as index 99fde8e4..b4d62ecf 100644 --- a/src/reflex/measurement/resolveWidth.as +++ b/src/reflex/measurement/resolveWidth.as @@ -1,23 +1,30 @@ package reflex.measurement { - import reflex.display.ReflexDisplay; + import reflex.framework.IMeasurable; + import reflex.framework.IMeasurablePercent; /** * @alpha */ - public function resolveWidth(object:Object):Number + public function resolveWidth(object:Object, total:Number = NaN, percentageTotal:Number = 100):Number // this utility is getting interesting, more docs soon { - var measurements:IMeasurements; - if(object is IMeasurable) { - measurements = (object as IMeasurable).measurements; - return isNaN(measurements.expliciteWidth) ? measurements.measuredWidth : measurements.expliciteWidth; - } else if(object is IMeasurements) { - measurements = (object as IMeasurements); - return isNaN(measurements.expliciteWidth) ? measurements.measuredWidth : measurements.expliciteWidth; + //var explicit:IMeasurements; + //var measured:IMeasurements; + //var percent:IMeasurablePercent; + if (object is IMeasurable && !isNaN((object as IMeasurable).explicitWidth)) { // explicit width is defined + return (object as IMeasurable).explicitWidth; + } else if(object is IMeasurablePercent && !isNaN(total) && !isNaN((object as IMeasurablePercent).percentWidth)) { // support percent-based measurement + return total * (object as IMeasurablePercent).percentWidth/percentageTotal; + } else if(object is IMeasurable && !isNaN((object as IMeasurable).measuredWidth)) { // measured width is defined + return (object as IMeasurable).measuredWidth; + } else if(object != null) { // we'll try to find a width anyways (even on non-DisplayObjects) + try { + return object.width; + } catch(e:Error) {} + return 0; } else { - return object.width; + return 0; } - } } \ No newline at end of file diff --git a/src/reflex/measurement/setSize.as b/src/reflex/measurement/setSize.as index 24d2ced5..6e779217 100644 --- a/src/reflex/measurement/setSize.as +++ b/src/reflex/measurement/setSize.as @@ -1,14 +1,12 @@ package reflex.measurement { - import reflex.display.ReflexDisplay; - import reflex.graphics.Rect; - + import reflex.framework.IMeasurable; + public function setSize(child:Object, width:Number, height:Number):void { - // update to interface later of course - if(child is ReflexDisplay || child is Rect) { + if(child is IMeasurable) { // || child is IDrawable child.setSize(width, height); - } else { + } else if(child != null) { child.width = width; child.height = height; } diff --git a/src/reflex/metadata/CommitUtility.as b/src/reflex/metadata/CommitUtility.as new file mode 100644 index 00000000..025880d6 --- /dev/null +++ b/src/reflex/metadata/CommitUtility.as @@ -0,0 +1,79 @@ +package reflex.metadata +{ + import flash.display.DisplayObject; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + import flash.utils.Dictionary; + import flash.utils.getQualifiedClassName; + + import reflex.behaviors.IBehavior; + import reflex.binding.Bind; + import reflex.graphics.IGraphicItem; + import reflex.invalidation.Invalidation; + import reflex.skins.ISkin; + + // for lack of a better name + + /** + * Helps implement CommitProperties metadata functionality. + * + * @experimental + */ + public class CommitUtility extends EventDispatcher + { + /* + static public var instance:CommitUtility = new CommitUtility(); + + private var dictionary:Dictionary = new Dictionary(true); + private var bindings:Array = []; + + public function register(instance:IEventDispatcher, method:String, properties:Array, resolver:Function):void { + var token:String = flash.utils.getQualifiedClassName(instance) + "_" + method + "Commit"; + Invalidation.registerPhase(token, Event, 0, true); + for each(var sourcePath:String in properties) { + var sourceToken:String = flash.utils.getQualifiedClassName(instance) + "_" + sourcePath; + var array:Array = dictionary[sourceToken]; + if(array == null) { + dictionary[sourceToken] = array = []; + } + array.push(token); + + bindings.push( Bind.addListener(instance, invalidationHandler, instance, sourcePath) ); + //bindings.push( Bind.bindEventListener(token, invalidationHandler, instance, sourcePath, false, 0, false) ); + } + + var f:Function = resolver != null ? resolver(method) : instance[method]; + //instance.addEventListener(token, commitHandler, false, 0, true); + if(instance is IDrawable || instance is IBehavior || instance is ISkin) { // we really need a common interface for ITarget or something + if((instance as Object).target) { + (instance as Object).target.addEventListener(token, f, false, 0, true); + } + } else { + instance.addEventListener(token, f, false, 0, true); + } + } + + // this is still a little hacky, but works for now + // + it's in a good spot to clean up later + private function invalidationHandler(s1:Object, s2:Object = null, s3:Object = null, s4:Object = null):void { + //var binding:Binding = s1 as Binding; + if(s2 is IEventDispatcher) { + var sourceToken:String = flash.utils.getQualifiedClassName(s2) + "_" + s1.sourcePath; + var tokens:Array = dictionary[sourceToken]; + for each(var token:String in tokens) { + if(s2 is IDrawable) { // we really need a common interface for ITarget or something + Invalidation.invalidate((s2 as IDrawable).target as DisplayObject, token); + } else if(s2 is IBehavior) { + Invalidation.invalidate((s2 as IBehavior).target as DisplayObject, token); + } else if(s2 is ISkin) { + Invalidation.invalidate((s2 as ISkin).target as DisplayObject, token); + } else { + Invalidation.invalidate(s2 as DisplayObject, token); + } + } + } + } + */ + } +} \ No newline at end of file diff --git a/src/reflex/metadata/Type.as b/src/reflex/metadata/Type.as new file mode 100644 index 00000000..0dedcb78 --- /dev/null +++ b/src/reflex/metadata/Type.as @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2009-2010 the original author or authors + * + * Permission is hereby granted to use, modify, and distribute this file + * in accordance with the terms of the license agreement accompanying it. + */ + +package reflex.metadata +{ + import flash.net.registerClassAlias; + import flash.utils.Dictionary; + import flash.utils.describeType; + import flash.utils.getDefinitionByName; + import flash.utils.getQualifiedClassName; + + /** + * The static Type class contains a collection of type-specific utilities, + * including object reflection. Reflection is described in XML, which + * format is documented in the flash.utils package. XML + * descriptions are cached for performance. + * + * @see flash.utils#describeType + */ + public class Type + { + // global cache improves reflection performance significantly + private static var typeCache:Dictionary = new Dictionary(); + + /** + * Evaluates whether an object or class is derived from a specific + * data type, class or interface. The isType() method is comparable to + * ActionScript's is operator except that it also makes + * class to class evaluations. + * + * @param value The object or class to evaluate. + * @param type The data type to check against. + * + * @return True if the object or class is derived from + * the data type. + */ + public static function isType(value:Object, type:Class):Boolean + { + if ( !(value is Class) ) { + return value is type; + } + + if (value == type) { + return true; + } + + var inheritance:XMLList = describeInheritance(value); + return Boolean( inheritance.(@type == getQualifiedClassName(type)).length() > 0 ); + } + + /** + * Determines the property type without accessing the property directly. + * Evaluation is based on the property definition and not by its value. + * + * @param value An object or class containing the property + * definition to evaluate. + * @param property The name of the property to be evaluated + * + * @return The class definition of the property type, + * as described by the property definition, or + * null if no definition was found. + */ + public static function getPropertyType(value:Object, property:String):Class + { + if ( !(value is Class) && !(property in value) ) { + return null; + } + + // retrieve the correct property from the property list + var propList:XMLList = describeProperties(value).(@name == property); + + return (propList.length() > 0) ? getDefinitionByName( propList[0].@type ) as Class : null; + } + + /** + * Ensures a class has a registered alias for object serialization and + * remoting. If the class doesn't yet have an alias it will be + * registered with it's full qualified class name, for example: + * flight.utils.Type. If the class has already been + * assigned an alias then its previous registration will be honored. + * + * @param value The object or class to register. + * + * @return True if the registration was successful, + * otherwise the object already has an alias. + * + * @see flash.net#registerClassAlias + */ + public static function registerType(value:Object):Boolean + { + if ( !(value is Class) ) { + value = getType(value); + } + + // if not already registered + var alias:String = describeType(value).@alias; + if (alias.length) { + return false; + } + + registerClassAlias(getQualifiedClassName(value).split("::").join("."), value as Class); + return true; + } + + /** + * Primary reflection method, producing an XML description of the object + * or class specified. Results are cached internally for performance. + * + * @param value The object or class to introspect. + * @param refreshCache Forces a new description to be generated, + * useful only when a class alias has changed. + * + * @return An XML description, which format is + * documented in the flash.utils + * package. + * + * @see flash.utils#describeType + */ + public static function describeType(value:Object, refreshCache:Boolean = false):XML + { + if ( !(value is Class) ) { + value = getType(value); + } + + if (refreshCache || typeCache[value] == null) { + typeCache[value] = flash.utils.describeType(value); + } + + return typeCache[value]; + } + + /** + * Targeted reflection describing an object's inheritance, including + * extended classes and implemented interfaces. + * + * @param value The object or class to introspect. + * + * @return A list of XML inheritance descriptions. + */ + public static function describeInheritance(value:Object):XMLList + { + return describeType(value).factory.*.(localName() == "extendsClass" || localName() == "implementsInterface"); + } + + /** + * Targeted reflection describing an object's properties, including both + * accessor's (getter/setters) and pure properties. + * + * @param value The object or class to introspect. + * @param metadataType Optional filter to return only those + * property descritions containing the + * specified metadata. + * + * @return A list of XML property descriptions. + */ + public static function describeProperties(value:Object, metadataType:String = null):XMLList + { + var properties:XMLList = describeType(value).factory.*.(localName() == "accessor" || localName() == "variable"); + + return (metadataType == null) ? properties : properties.(child("metadata").(@name == metadataType).length() > 0); + } + + + /** + * Targeted reflection describing an object's methods. + * + * @param value The object or class to introspect. + * @param metadataType Optional filter to return only those + * method descritions containing the + * specified metadata. + * + * @return A list of XML method descriptions. + */ + public static function describeMethods(value:Object, metadataType:String = null):XMLList + { + var methods:XMLList = describeType(value).factory.method; + + return (metadataType == null) ? methods : methods.(child("metadata").(@name == metadataType).length() > 0); + } + + } +} \ No newline at end of file diff --git a/src/reflex/metadata/getClassName.as b/src/reflex/metadata/getClassName.as new file mode 100644 index 00000000..a87b49f8 --- /dev/null +++ b/src/reflex/metadata/getClassName.as @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2009-2010 the original author or authors + * + * Permission is hereby granted to use, modify, and distribute this file + * in accordance with the terms of the license agreement accompanying it. + */ + +package reflex.metadata +{ + import flash.utils.getQualifiedClassName; + + /** + * Returns the exact class name, without package or path. + * + * @param value The object for which the class name is desired. + * Any ActionScript value may be passed including + * all ActionScript types, object instances, + * primitive types such as uint, and class objects. + */ + public function getClassName(value:Object):String + { + return getQualifiedClassName(value).split("::").pop(); + } +} \ No newline at end of file diff --git a/src/reflex/metadata/getType.as b/src/reflex/metadata/getType.as new file mode 100644 index 00000000..a5545953 --- /dev/null +++ b/src/reflex/metadata/getType.as @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2009-2010 the original author or authors + * + * Permission is hereby granted to use, modify, and distribute this file + * in accordance with the terms of the license agreement accompanying it. + */ + +package reflex.metadata +{ + import flash.utils.Proxy; + import flash.utils.getDefinitionByName; + import flash.utils.getQualifiedClassName; + + /** + * Global method for getting an objects type. + * + * @param value The object being evaluated. If the object is a + * class it will be returned as the type and not + * the type Class. + */ + public function getType(value:Object):Class + { + if (value is Class) { + return value as Class; + } else if (value is Proxy) { + return getDefinitionByName( getQualifiedClassName(value) ) as Class; + } else { + return value.constructor as Class; + } + } +} \ No newline at end of file diff --git a/src/reflex/metadata/resolveBindings.as b/src/reflex/metadata/resolveBindings.as index d18d1f28..36ff229f 100644 --- a/src/reflex/metadata/resolveBindings.as +++ b/src/reflex/metadata/resolveBindings.as @@ -2,8 +2,7 @@ package reflex.metadata { import flash.events.IEventDispatcher; - import flight.binding.Bind; - import flight.utils.Type; + import reflex.binding.Bind; /** * @experimental @@ -20,7 +19,8 @@ package reflex.metadata tag.arg.(@key == "target").@value : tag.arg.@value; - Bind.addBinding(instance, targ, instance, prop.@name, true); + //Bind.addBinding(instance, targ, instance, prop.@name, true); + Bind.addBinding(instance, prop.@name, instance, targ, true); } } } diff --git a/src/reflex/metadata/resolveCommitProperties.as b/src/reflex/metadata/resolveCommitProperties.as new file mode 100644 index 00000000..cd8e8a97 --- /dev/null +++ b/src/reflex/metadata/resolveCommitProperties.as @@ -0,0 +1,25 @@ +package reflex.metadata +{ + import flash.events.IEventDispatcher; + + /** + * @experimental + */ + // this needs some updates to handle non-DisplayObjects (like Rect) better + + public function resolveCommitProperties(instance:IEventDispatcher, resolver:Function = null):void + { + var desc:XMLList = Type.describeMethods(instance, "Commit"); + for each (var meth:XML in desc) { + var meta:XMLList = meth.metadata.(@name == "Commit"); + + // to support multiple CommitProperties metadata tags on a single method + for each (var tag:XML in meta) { + var target:String = ( tag.arg.(@key == "properties").length() > 0 ) ? tag.arg.(@key == "properties").@value : tag.arg.@value; + var properties:Array = target.replace(/\s+/g, "").split(","); + //CommitUtility.instance.register(instance, meth.@name, properties, resolver); + } + } + } + +} \ No newline at end of file diff --git a/src/reflex/metadata/resolvePropertyListeners.as b/src/reflex/metadata/resolveDataListeners.as similarity index 53% rename from src/reflex/metadata/resolvePropertyListeners.as rename to src/reflex/metadata/resolveDataListeners.as index dac1fdf0..e43ef42b 100644 --- a/src/reflex/metadata/resolvePropertyListeners.as +++ b/src/reflex/metadata/resolveDataListeners.as @@ -2,19 +2,18 @@ package reflex.metadata { import flash.events.IEventDispatcher; - import flight.binding.Bind; - import flight.utils.Type; + import reflex.binding.Bind; /** * @experimental */ - public function resolvePropertyListeners(instance:IEventDispatcher):void + public function resolveDataListeners(instance:IEventDispatcher):void { - var desc:XMLList = Type.describeMethods(instance, "PropertyListener"); + var desc:XMLList = Type.describeMethods(instance, "DataListener"); for each (var meth:XML in desc) { - var meta:XMLList = meth.metadata.(@name == "PropertyListener"); + var meta:XMLList = meth.metadata.(@name == "DataListener"); - // to support multiple PropertyListener metadata tags on a single method + // to support multiple DataListener metadata tags on a single method for each (var tag:XML in meta) { var targ:String = ( tag.arg.(@key == "target").length() > 0 ) ? tag.arg.(@key == "target").@value : diff --git a/src/reflex/metadata/resolveEventListeners.as b/src/reflex/metadata/resolveEventListeners.as index c9e7789e..7d02173e 100644 --- a/src/reflex/metadata/resolveEventListeners.as +++ b/src/reflex/metadata/resolveEventListeners.as @@ -2,8 +2,7 @@ package reflex.metadata { import flash.events.IEventDispatcher; - import flight.binding.Bind; - import flight.utils.Type; + import reflex.binding.Bind; /** * @experimental @@ -16,8 +15,8 @@ package reflex.metadata // to support multiple EventListener metadata tags on a single method for each (var tag:XML in meta) { - var type:String = ( tag.arg.(@key == "type").length() > 0 ) ? - tag.arg.(@key == "type").@value : + var type:String = ( tag.arg.(@key == "event").length() > 0 ) ? + tag.arg.(@key == "event").@value : tag.arg.@value; var targ:String = tag.arg.(@key == "target").@value; diff --git a/src/reflex/metadata/resolveLayoutProperties.as b/src/reflex/metadata/resolveLayoutProperties.as index bd97faa7..7f309f6d 100644 --- a/src/reflex/metadata/resolveLayoutProperties.as +++ b/src/reflex/metadata/resolveLayoutProperties.as @@ -2,8 +2,7 @@ package reflex.metadata { import flash.events.IEventDispatcher; - import flight.binding.Bind; - import flight.utils.Type; + import reflex.binding.Bind; // this method of listening for layout invalidating changes is very much experimental /** @@ -14,13 +13,10 @@ package reflex.metadata var desc:XML = Type.describeType(instance); for each (var meth:XML in desc.factory[0]) { var meta:XMLList = meth.metadata.(@name == "LayoutProperty"); - - // to support multiple PropertyListener metadata tags on a single method for each (var tag:XML in meta) { var sourcePath:String = tag.arg.(@key == "name").@value; Bind.addListener(child, listener, child, sourcePath); } - } } diff --git a/src/reflex/skins/BackgroundSkin.as b/src/reflex/skins/BackgroundSkin.as deleted file mode 100644 index 122ca504..00000000 --- a/src/reflex/skins/BackgroundSkin.as +++ /dev/null @@ -1,135 +0,0 @@ -package reflex.skins -{ - import flash.display.Graphics; - import flash.display.GraphicsGradientFill; - import flash.display.IGraphicsData; - import flash.geom.Matrix; - - import flight.binding.Bind; - import flight.events.PropertyEvent; - - import reflex.graphics.attributes.Angle; - import reflex.graphics.attributes.Color; - import reflex.graphics.attributes.CornerRadius; - - public class BackgroundSkin extends DrawingSkin - { - protected static var splitter:RegExp = /\s*,\s*/; - public var backgroundColors:String; - public var backgroundAlphas:String; - public var backgroundRatios:String; - public var backgroundAngle:String; - public var borderColors:String; - public var borderAlphas:String; - public var borderRatios:String; - public var borderAngle:String; - public var borderWidth:Number; - public var radius:String; - - - override public function redraw(g:Graphics):void - { - g.clear(); - var d:Vector. = new Vector.(1); - var r:CornerRadius = CornerRadius.fromString(radius); - - var x:Number = 0; - var y:Number = 0; - var w:Number = target.width; - var h:Number = target.height; - - var colors:Array; - var alphas:Array; - var ratios:Array; - var angle:Number; - var fill:GraphicsGradientFill; - var m:Matrix = new Matrix(); - - if (borderColors) { - colors = getColors(borderColors); - alphas = getAlphas(borderAlphas, colors.length); - ratios = getRatios(borderRatios, colors.length); - angle = Angle.fromString(borderAngle || "90"); - var bwidth:Number = borderWidth || 1; - m.createGradientBox(w, h, angle, x, y); - fill = new GraphicsGradientFill("linear", colors, alphas, ratios, m); - d[0] = fill; - g.drawGraphicsData(d); - g.drawRoundRectComplex(x, y, w, h, r.topLeft, r.topRight, r.bottomLeft, r.bottomRight); - x += bwidth; - y += bwidth; - w -= bwidth*2; - h -= bwidth*2; - r.inset(bwidth); - g.drawRoundRectComplex(x, y, w, h, r.topLeft, r.topRight, r.bottomLeft, r.bottomRight); - g.endFill(); - } - - if (backgroundColors) { - colors = getColors(backgroundColors); - alphas = getAlphas(backgroundAlphas, colors.length); - ratios = getRatios(backgroundRatios, colors.length); - angle = Angle.fromString(backgroundAngle || "90"); - m.createGradientBox(w, h, angle, x, y); - fill = new GraphicsGradientFill("linear", colors, alphas, ratios, m); - d[0] = fill; - g.drawGraphicsData(d); - g.drawRoundRectComplex(x, y, w, h, r.topLeft, r.topRight, r.bottomLeft, r.bottomRight); - } - - r.dispose(); - } - - protected function getColors(colors:String):Array - { - return colors.split(splitter).map(toColor); - } - - protected function getAlphas(alphas:String, count:int):Array - { - if (!alphas) alphas = "1"; - - var result:Array = alphas.split(splitter).map(toAlpha); - while (result.length < count) { - result.push(result[0]); - } - result.length = count; - return result; - } - - protected function getRatios(ratios:String, count:int):Array - { - if (!ratios) ratios = "1"; - - var result:Array = ratios.split(splitter).map(toRatio); - - // if we aren't accurate override - if (result.length != count) { - var stepSize:Number = 255/(count - 1); - for (var i:int = 0; i < count; i++) { - result[i] = i * stepSize; - } - result.length = count; - } - return result; - } - - protected static function toColor(item:String, index:int, array:Array):uint - { - if (!item.length) return 0; - return Color.fromString(item); - } - - protected static function toAlpha(item:String, index:int, array:Array):Number - { - var alpha:Number = parseFloat(item); - return isNaN(alpha) ? 1 : alpha; - } - - protected static function toRatio(item:String, index:int, array:Array):Number - { - var percent:Number = parseFloat(item); - return isNaN(percent) ? 255 : percent*255; - } - } -} \ No newline at end of file diff --git a/src/reflex/skins/ButtonSkin.as b/src/reflex/skins/ButtonSkin.as index a8afaac1..d7cf737b 100644 --- a/src/reflex/skins/ButtonSkin.as +++ b/src/reflex/skins/ButtonSkin.as @@ -1,65 +1,79 @@ package reflex.skins { - import flash.display.FrameLabel; + import flash.display.GradientType; + import flash.events.Event; + import flash.geom.Matrix; + import flash.text.TextField; + import flash.text.TextFormat; - import flight.binding.Bind; - import flight.events.PropertyEvent; - - import reflex.components.ButtonGraphic; - import reflex.layout.Block; + import reflex.collections.SimpleCollection; + import reflex.invalidation.LifeCycle; + import reflex.layouts.BasicLayout; + import reflex.text.Label; + import reflex.text.TextFieldDisplay; public class ButtonSkin extends GraphicSkin { - [Bindable] - public var label:String; - [Bindable] - public var selected:Boolean; + private var _labelDisplay:Label; + + [Bindable(event="labelDisplayChange")] + public function get labelDisplay():Label { return _labelDisplay; } + public function set labelDisplay(value:Label):void { + notify("labelDisplay", _labelDisplay, _labelDisplay = value); + } + - private var buttonGraphic:ButtonGraphic; + override protected function initialize():void { + super.initialize(); + labelDisplay = new Label(); + labelDisplay.color = 0xFFFFFF; + labelDisplay.fontFamily = "sans-serif"; + labelDisplay.fontSize = 33; + labelDisplay.style = "left: 10; right: 10; top: 5; bottom: 5;"; + layout = new BasicLayout(); + content = new SimpleCollection();//[labelDisplay]; + content.addItem(labelDisplay); + //labelDisplay.invalidate(LifeCycle.INITIALIZE); + } - public function ButtonSkin(graphic:ButtonGraphic = null) - { - graphic = buttonGraphic = graphic || new ButtonGraphic() - super(buttonGraphic); - graphic.cacheAsBitmap = true; - - Bind.addBinding(this, "selected", this, "target.selected"); - removeStatefulChild(graphic); - addStatefulChild(graphic.background); - addStatefulChild(graphic.foreground); - - var block:Block = new Block(graphic.background); - block.scale = true; - block.anchor = 0; - block = new Block(graphic.foreground); - block.scale = true; - block.anchor = 0; - block.anchor.bottom = NaN; - block.anchor.vertical = .5; - block.anchor.offsetY = 10; - block = new Block(graphic.label); - block.scale = true; - block.anchor.left = block.anchor.right = 0; - block.anchor.vertical = .5; - - Bind.addListener(this, setLabel, this, "label"); - Bind.addListener(this, onSelectedChange, this, "selected"); + override protected function render(currentState:String):void { + if(graphics) { + graphics.clear(); + switch(currentState) { + case "up": + renderUp(); + break; + case "over": + renderOver(); + break; + case "down": + renderDown(); + break; + default: + renderUp(); + break; + } + } } - protected function setLabel(label:String):void - { - buttonGraphic.label.text = label || ""; + private function renderUp():void { + graphics.beginFill(0xFCA527, 1); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); + graphics.endFill(); } - override protected function gotoState(state:String):void - { - super.gotoState(selected ? "selected" : state); + private function renderOver():void { + graphics.beginFill(0xFCA527, 1); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); + graphics.endFill(); } - protected function onSelectedChange(selected:Boolean):void - { - super.gotoState(selected ? "selected" : currentState); + private function renderDown():void { + graphics.beginFill(0x363636, 1); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); + graphics.endFill(); } + } } \ No newline at end of file diff --git a/src/reflex/skins/CheckBoxSkin.as b/src/reflex/skins/CheckBoxSkin.as new file mode 100644 index 00000000..bacfb469 --- /dev/null +++ b/src/reflex/skins/CheckBoxSkin.as @@ -0,0 +1,76 @@ +package reflex.skins +{ + import flash.events.Event; + import flash.text.TextField; + import flash.text.TextFormat; + + import reflex.layouts.BasicLayout; + import reflex.text.Label; + import reflex.text.TextFieldDisplay; + + public class CheckBoxSkin extends GraphicSkin + { + + public var labelDisplay:Label; + + public function CheckBoxSkin() + { + super(); + labelDisplay = new Label(); + labelDisplay.color = 0xFFFFFF; + labelDisplay.fontFamily = "sans-serif"; + labelDisplay.fontSize = 24; + labelDisplay.style = "left: 37; right: 0; top: 0; bottom: 0;"; + layout = new BasicLayout(); + content = [labelDisplay]; + } + + override protected function render(currentState:String):void { + graphics.clear(); + graphics.lineStyle(0,0,0); + graphics.beginFill(0, 0); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); + graphics.endFill(); + + switch(currentState) { + case "up": + renderUp(); + break; + case "over": + renderUp(); + break; + case "down": + renderUp(); + break; + case "upAndSelected": + renderUpAndSelected(); + break; + case "overAndSelected": + renderUpAndSelected(); + break; + case "downAndSelected": + renderUpAndSelected(); + break; + default: + renderUp(); + break; + } + } + + private function renderUp():void { + graphics.beginFill(0xFFFFFF, 1); + graphics.drawRect(0, unscaledHeight/2-15, 30, 30); + graphics.endFill(); + } + + private function renderUpAndSelected():void { + graphics.beginFill(0xFFFFFF, 1); + graphics.drawRect(0, unscaledHeight/2-15, 30, 30); + graphics.endFill(); + graphics.beginFill(0x1E1E1E, 1); + graphics.drawRect(7, unscaledHeight/2-8, 16, 16); + graphics.endFill(); + } + + } +} \ No newline at end of file diff --git a/src/reflex/skins/DragStepperSkin.as b/src/reflex/skins/DragStepperSkin.as deleted file mode 100644 index 16f6817c..00000000 --- a/src/reflex/skins/DragStepperSkin.as +++ /dev/null @@ -1,42 +0,0 @@ -package reflex.skins -{ - import flash.display.Sprite; - import flash.text.TextFormat; - - import flight.binding.Bind; - import flight.position.IPosition; - - import reflex.components.DragStepperGraphic; - import reflex.cursors.Cursor; - - public class DragStepperSkin extends GraphicSkin - { - [Bindable] - public var position:IPosition; - - private var stepperGraphic:DragStepperGraphic; - - public function DragStepperSkin(graphic:DragStepperGraphic = null) - { - graphic = stepperGraphic = graphic || new DragStepperGraphic() - super(stepperGraphic); - - stepperGraphic.label.defaultTextFormat = new TextFormat(null, null, null, null, null, true); - - Bind.addBinding(this, "position", this, "target.position"); - Bind.addListener(this, updateLabel, this, "position.value"); - Bind.addListener(this, onTargetChange, this, "target"); - } - - protected function onTargetChange(oldTarget:Sprite, newTarget:Sprite):void - { - if (oldTarget) Cursor.useCursor(oldTarget, Cursor.AUTO); - if (newTarget) Cursor.useCursor(newTarget, Cursor.EAST_WEST); - } - - protected function updateLabel(position:int):void - { - stepperGraphic.label.text = String(position); - } - } -} \ No newline at end of file diff --git a/src/reflex/skins/DrawingSkin.as b/src/reflex/skins/DrawingSkin.as deleted file mode 100644 index 554c26b0..00000000 --- a/src/reflex/skins/DrawingSkin.as +++ /dev/null @@ -1,43 +0,0 @@ -package reflex.skins -{ - import flash.display.Graphics; - - import flight.binding.Bind; - - import reflex.events.InvalidationEvent; - - public class DrawingSkin extends Skin - { - - static public const DRAW:String = "draw"; - - InvalidationEvent.registerPhase(DRAW); - - public function DrawingSkin() - { - Bind.addListener(this, invalidateRedraw, this, "target"); - Bind.addListener(this, invalidateRedraw, this, "state"); - Bind.addListener(this, invalidateRedraw, this, "target.width"); - Bind.addListener(this, invalidateRedraw, this, "target.height"); - Bind.bindEventListener(DRAW, onDraw, this, "target"); - } - - public function invalidateRedraw():void - { - if (target) { - InvalidationEvent.invalidate(target, DRAW); - } - } - - public function redraw(graphics:Graphics):void - { - } - - protected function onDraw(event:InvalidationEvent):void - { - if (target != null) { - redraw(target.graphics); - } - } - } -} \ No newline at end of file diff --git a/src/reflex/skins/GraphicSkin.as b/src/reflex/skins/GraphicSkin.as index ae65bb47..7e0ce9c7 100644 --- a/src/reflex/skins/GraphicSkin.as +++ b/src/reflex/skins/GraphicSkin.as @@ -1,113 +1,52 @@ package reflex.skins { - import flash.display.DisplayObject; - import flash.display.FrameLabel; - import flash.display.InteractiveObject; - import flash.display.MovieClip; + import flash.display.Graphics; import flash.display.Sprite; - import flash.events.EventDispatcher; + import flash.events.Event; + import flash.events.IEventDispatcher; - import flight.binding.Bind; - import flight.events.PropertyEvent; + import reflex.metadata.resolveCommitProperties; - import reflex.layout.Block; - //import reflex.layout.ILayoutAlgorithm; - import reflex.layout.LayoutWrapper; - - public class GraphicSkin extends Skin { - private var _statefulChildren:Array = []; - private var _graphic:Sprite; - private var _graphicBlock:Block; - - public function GraphicSkin(graphic:Sprite) - { - // initialize graphic with smart layout - _graphic = graphic; - _graphicBlock = new Block(graphic); - // TODO: set that layout's scaling if the graphic has no children (slice-9) - - // override default width/height if a guide is present - if ("defaultSize" in graphic && graphic["defaultSize"] is DisplayObject) { - var defaultSize:DisplayObject = graphic["defaultSize"] as DisplayObject; - _graphicBlock.defaultWidth = defaultSize.width; - _graphicBlock.defaultHeight = defaultSize.height; - } - - // TODO: resolve API .. addstatefulchild, etc - if (_graphic is MovieClip) { - Bind.addListener(this, gotoState, this, "state"); - addStatefulChild(_graphic as MovieClip); - } - } - public function get graphic():Sprite - { - return _graphic; - } + protected var graphics:Graphics; - public function get graphicBlock():Block + public function GraphicSkin() { - return _graphicBlock; + super(); + //reflex.metadata.resolveCommitProperties(this); + } - override public function set target(value:Sprite):void - { - if (target == value) { - return; - } - - if (target != null && target != graphic) { - target.removeChild(graphic); - _graphicBlock.anchor = null; - } - + override public function set target(value:IEventDispatcher):void { super.target = value; - var targetBlock:Block = LayoutWrapper.getLayout(target) as Block; - if (targetBlock != null) { - targetBlock.defaultWidth = _graphicBlock.defaultWidth; - targetBlock.defaultHeight = _graphicBlock.defaultHeight; - } - - if (target != null && target != graphic) { - target.addChild(graphic); - _graphicBlock.anchor = 0; + graphics = null; + if(value) { + helper = Object(value).helper; + graphics = helper.getGraphics(display); + value.addEventListener("layout", onRender, false, 0, true); } } - override public function getSkinPart(part:String):InteractiveObject - { - return (part in graphic) ? graphic[part] : (part in this) ? this[part] : (part in target) ? target[part] : null; + override public function set currentState(value:String):void { + super.currentState = value; + render(value); } - public function addStatefulChild(child:MovieClip):void + override public function hasState(state:String):Boolean { - if (_statefulChildren.indexOf(child) != -1) return; - - _statefulChildren.push(child); - - // each label should be for a state. - var labels:Array = child.currentLabels; - for each (var label:FrameLabel in labels) { - child.addFrameScript(label.frame - 1, child.stop); - } - child.addFrameScript(child.totalFrames - 1, child.stop); + return true; } - public function removeStatefulChild(child:MovieClip):void - { - var index:int = _statefulChildren.indexOf(child); - if (index != -1) { - _statefulChildren.splice(index, 1); - } + private function onRender(event:Event):void { + render(currentState); } - protected function gotoState(state:String):void - { - for each (var child:MovieClip in _statefulChildren) { - child.gotoAndStop(state); - } + //[Commit(properties="width, height, currentState")] + protected function render(currentState:String):void { + } + } } \ No newline at end of file diff --git a/src/reflex/skins/HSliderSkin.as b/src/reflex/skins/HSliderSkin.as new file mode 100644 index 00000000..c8f83560 --- /dev/null +++ b/src/reflex/skins/HSliderSkin.as @@ -0,0 +1,54 @@ +package reflex.skins +{ + import flash.display.GradientType; + import flash.display.Graphics; + import flash.display.Sprite; + import flash.events.Event; + import flash.geom.Matrix; + import flash.text.TextField; + import flash.text.TextFormat; + + import reflex.layouts.BasicLayout; + import reflex.text.Label; + import reflex.text.TextFieldDisplay; + + public class HSliderSkin extends GraphicSkin + { + + public var thumb:Sprite; + public var track:Sprite; + + public function HSliderSkin() + { + super(); + unscaledHeight = 14; + track = new Sprite(); + renderTrack(track.graphics); + thumb = new Sprite(); + renderThumb(thumb.graphics); + content = [track, thumb]; + _measuredWidth = 170; + _measuredHeight = 14; + } + + override protected function render(currentState:String):void { + renderTrack(track.graphics); + renderThumb(thumb.graphics); + } + + private function renderThumb(g:Graphics):void { + g.clear(); + g.beginFill(0xFFFFFF, 1); + g.drawRect(0, 0, 14, unscaledHeight); + g.endFill(); + } + + private function renderTrack(g:Graphics):void { + g.clear(); + g.beginFill(0x363636, 1); + g.drawRect(0, 0, unscaledWidth, unscaledHeight); + g.endFill(); + } + + } +} \ No newline at end of file diff --git a/src/reflex/skins/ISkin.as b/src/reflex/skins/ISkin.as index e311a476..3bf0c8a0 100644 --- a/src/reflex/skins/ISkin.as +++ b/src/reflex/skins/ISkin.as @@ -1,29 +1,30 @@ package reflex.skins { + import flash.display.DisplayObjectContainer; import flash.display.InteractiveObject; import flash.display.Sprite; + import flash.events.IEventDispatcher; //import reflex.layout.ILayoutAlgorithm; /** + * Implemented by objects which will provide the visual definition for a component's display. + * This interface is NOT required for items to be used as a skin in Reflex's skinning system. + * For instance animated MovieClips or other display classes may also act as a skin. + * * @alpha - **/ + */ public interface ISkin { - function get target():Sprite; // but I prefer ISkinnable targets, they're my favorite - function set target(value:Sprite):void; // cause then I'll use data, children, layout, state, etc - /* - function get layout():ILayoutAlgorithm; - function set layout(value:ILayoutAlgorithm):void; - */ - /* - function get data():Object; - function set data(value:Object):void; - */ - /* - function get state():String; - function set state(value:String):void; - */ - function getSkinPart(part:String):InteractiveObject; + + function get target():IEventDispatcher; + function set target(value:IEventDispatcher):void; + + // not sure if getSkinPart should be required + // maybe we could make it part of an extended interfaces? + //function getSkinPart(part:String):InteractiveObject; + + function hasState( state:String ):Boolean; + } } \ No newline at end of file diff --git a/src/reflex/skins/ISkinnable.as b/src/reflex/skins/ISkinnable.as deleted file mode 100644 index ad429805..00000000 --- a/src/reflex/skins/ISkinnable.as +++ /dev/null @@ -1,34 +0,0 @@ -package reflex.skins -{ - import flash.events.IEventDispatcher; - - import flight.list.IList; - - import reflex.display.IContainer; - //import reflex.layout.ILayoutAlgorithm; - - /** - * @alpha - **/ - public interface ISkinnable - { - /* - function get data():Object; - function set data(value:Object):void; - */ - - /** - * The component's current state. - **/ - function get currentState():String; - function set currentState(value:String):void; - - /** - * An Object to be used for the component's visual display. - * This is commonly an MXML class extending reflex.skins.Skin or a custom MovieClip. - * However, any DisplayObject or ISkin implementation may be used. - */ - function get skin():Object; - function set skin(value:Object):void; - } -} \ No newline at end of file diff --git a/src/reflex/skins/ListBoxSkin.as b/src/reflex/skins/ListBoxSkin.as deleted file mode 100644 index 7a271f30..00000000 --- a/src/reflex/skins/ListBoxSkin.as +++ /dev/null @@ -1,72 +0,0 @@ -package reflex.skins -{ - import flight.binding.Bind; - import flight.list.ArrayList; - import flight.list.IList; - import flight.position.IPosition; - import flight.position.Position; - - import reflex.behaviors.SlideBehavior; - import reflex.components.ScrollPaneGraphic; - import reflex.display.Replicator; - import reflex.display.ScrollContainer; - import reflex.layout.Align; - import reflex.layout.Block; - - public class ListBoxSkin extends GraphicSkin - { - [Bindable] - public var position:IPosition = new Position(); - /* - [Bindable] - public var template:Class; - */ - [Bindable] - public var dataProvider:IList = new ArrayList(); - - public var container:ScrollContainer; - - private var replicator:Replicator; - private var scrollPaneGraphic:ScrollPaneGraphic; - - public function ListBoxSkin(graphic:ScrollPaneGraphic = null) - { - graphic = scrollPaneGraphic = graphic || new ScrollPaneGraphic() - super(scrollPaneGraphic); - - container = new ScrollContainer(); - //container.dock = Align.FILL; - graphic.addChild(container); - /* - var block:Block; - block = new Block(graphic.corner); - block.scale = true; - block.anchor.right = block.anchor.bottom = 0; - */ - var slideBehavior:SlideBehavior; - var scrollBarSkin:ScrollBarSkin; - /* - slideBehavior = new SlideBehavior(graphic.hScroll); - scrollBarSkin = new ScrollBarSkin(graphic.hScroll); - scrollBarSkin.target = graphic.hScroll; - scrollBarSkin.position = slideBehavior.position = container.hPosition; - scrollBarSkin.graphicBlock.dock = Align.BOTTOM; - scrollBarSkin.graphicBlock.margin.right = graphic.corner.width; - - slideBehavior = new SlideBehavior(graphic.vScroll); - scrollBarSkin = new ScrollBarSkin(graphic.vScroll); - scrollBarSkin.target = graphic.vScroll; - position = scrollBarSkin.position = slideBehavior.position = container.vPosition; - scrollBarSkin.graphicBlock.dock = Align.RIGHT; - - replicator = new Replicator(container); - replicator.position = position; - - Bind.addBinding(replicator, "template", this, "template", true); - Bind.addBinding(replicator, "dataProvider", this, "dataProvider", true); - Bind.addBinding(this, "target.children", replicator, "children"); - Bind.addBinding(replicator, "coverageSize", container, "height");*/ - } - - } -} \ No newline at end of file diff --git a/src/reflex/skins/ListItemSkin.as b/src/reflex/skins/ListItemSkin.as new file mode 100644 index 00000000..91f4f699 --- /dev/null +++ b/src/reflex/skins/ListItemSkin.as @@ -0,0 +1,88 @@ +package reflex.skins +{ + import flash.display.GradientType; + import flash.events.Event; + import flash.geom.Matrix; + import flash.text.TextField; + import flash.text.TextFormat; + + import reflex.layouts.BasicLayout; + import reflex.text.Label; + import reflex.text.TextFieldDisplay; + + public class ListItemSkin extends GraphicSkin + { + + private var _labelDisplay:Label; + + [Bindable(event="labelDisplayChange")] + public function get labelDisplay():Label { return _labelDisplay; } + public function set labelDisplay(value:Label):void { + notify("labelDisplay", _labelDisplay, _labelDisplay = value); + } + + public function ListItemSkin() + { + super(); + labelDisplay = new Label(); + labelDisplay.color = 0xFFFFFF; + labelDisplay.fontFamily = "sans-serif"; + labelDisplay.fontSize = 33; + labelDisplay.style = "left: 10; right: 10; top: 5; bottom: 5;"; + layout = new BasicLayout(); + content = [labelDisplay]; + _measuredWidth = 250; + _measuredHeight = 64; + } + + override protected function render(currentState:String):void { + if(graphics) { + graphics.clear(); + if(unscaledWidth > 0 && unscaledHeight > 0) { + switch(currentState) { + case "up": + renderUp(); + break; + case "over": + renderOver(); + break; + case "down": + renderDown(); + break; + case "upAndSelected": + renderDown(); + break; + case "overAndSelected": + renderDown(); + break; + case "downAndSelected": + renderDown(); + break; + default: + renderUp(); + break; + } + } + } + } + + private function renderUp():void { + graphics.beginFill(0xFCA527, 1); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); + graphics.endFill(); + } + + private function renderOver():void { + graphics.beginFill(0xFCA527, 1); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); + graphics.endFill(); + } + + private function renderDown():void { + graphics.beginFill(0x363636, 1); + graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); + graphics.endFill(); + } + + } +} \ No newline at end of file diff --git a/src/reflex/skins/ListSkin.as b/src/reflex/skins/ListSkin.as index 33781491..d7ce60a2 100644 --- a/src/reflex/skins/ListSkin.as +++ b/src/reflex/skins/ListSkin.as @@ -1,28 +1,38 @@ package reflex.skins { - import flight.binding.Bind; - import flight.list.ArrayList; + import flash.events.Event; + + import reflex.binding.Bind; + import reflex.collections.SimpleCollection; + import reflex.components.Button; + import reflex.components.Component; + import reflex.containers.Container; + import reflex.containers.Group; + import reflex.containers.HGroup; + import reflex.layouts.BasicLayout; + import reflex.layouts.VerticalLayout; + import reflex.layouts.XYLayout; - import reflex.display.Container; - import reflex.events.InvalidationEvent; - public class ListSkin extends Skin { [Bindable] public var container:Container; - public function ListSkin() - { - super(); + override protected function initialize():void { + super.initialize(); + container = this; + /* + layout = new BasicLayout(); container = new Container(); - Bind.addBinding(this, "container.children", this, "target.dataProvider"); - Bind.addBinding(this, "container.template", this, "target.template"); - Bind.addBinding(this, "container.layout", this, "target.layout"); - children = new ArrayList([container]); - //children.addItem(container); - + container.layout = new XYLayout(); + container.setStyle("left", 0); + container.setStyle("right", 0); + container.setStyle("top", 0); + container.setStyle("bottom", 0); + content = new SimpleCollection([container]); + */ } } diff --git a/src/reflex/skins/RadioButtonSkin.as b/src/reflex/skins/RadioButtonSkin.as new file mode 100644 index 00000000..3d7c7c13 --- /dev/null +++ b/src/reflex/skins/RadioButtonSkin.as @@ -0,0 +1,70 @@ +package reflex.skins +{ + import flash.events.Event; + import flash.text.TextField; + import flash.text.TextFormat; + + import reflex.layouts.BasicLayout; + import reflex.text.Label; + import reflex.text.TextFieldDisplay; + + public class RadioButtonSkin extends GraphicSkin + { + + public var labelDisplay:Label; + + public function RadioButtonSkin() + { + super(); + labelDisplay = new Label(); + labelDisplay.color = 0xFFFFFF; + labelDisplay.fontFamily = "sans-serif"; + labelDisplay.fontSize = 24; + labelDisplay.style = "left: 37; right: 0; top: 0; bottom: 0;"; + layout = new BasicLayout(); + content = [labelDisplay]; + } + + override protected function render(currentState:String):void { + graphics.clear(); + switch(currentState) { + case "up": + renderUp(); + break; + case "over": + renderUp(); + break; + case "down": + renderUp(); + break; + case "upAndSelected": + renderUpAndSelected(); + break; + case "overAndSelected": + renderUpAndSelected(); + break; + case "downAndSelected": + renderUpAndSelected(); + break; + default: + renderUp(); + } + } + + private function renderUp():void { + graphics.beginFill(0xFFFFFF, 1); + graphics.drawCircle(15, unscaledHeight/2, 15); + graphics.endFill(); + } + + private function renderUpAndSelected():void { + graphics.beginFill(0xFFFFFF, 1); + graphics.drawCircle(15, unscaledHeight/2, 15); + graphics.endFill(); + graphics.beginFill(0x1E1E1E, 1); + graphics.drawCircle(15, unscaledHeight/2, 8); + graphics.endFill(); + } + + } +} \ No newline at end of file diff --git a/src/reflex/skins/ScrollBarSkin.as b/src/reflex/skins/ScrollBarSkin.as deleted file mode 100644 index 860dd098..00000000 --- a/src/reflex/skins/ScrollBarSkin.as +++ /dev/null @@ -1,74 +0,0 @@ -package reflex.skins -{ - import flash.events.Event; - - import flight.binding.Bind; - import flight.position.IPosition; - - import legato.components.ScrollBarGraphic; - - import reflex.components.Button; - import reflex.layout.Align; - import reflex.layout.Block; - import reflex.layout.LayoutWrapper; - - public class ScrollBarSkin extends GraphicSkin - { - // skinParts with StepBehavior - public var fwdBtn:Button; - public var bwdBtn:Button; - - // skinParts with SlideBehavior - public var track:Button; - public var thumb:Button; - - [Bindable] - public var position:IPosition; - - private var scrollBarGraphic:ScrollBarGraphic; - - public function ScrollBarSkin(graphic:ScrollBarGraphic = null) - { - graphic = scrollBarGraphic = graphic || new ScrollBarGraphic() - super(scrollBarGraphic); - - var block:Block; - block = new Block(graphic.background); - block.scale = true; - block.dock = Align.FILL; - block = new Block(graphic.bwdBtn); - block.scale = true; - block.dock = Align.LEFT; - block = new Block(graphic.fwdBtn); - block.scale = true; - block.dock = Align.RIGHT; - block = new Block(graphic.track); - block.scale = true; - block.dock = Align.FILL; - block = new Block(graphic.thumb); - block.scale = true; - block.anchor.top = block.anchor.bottom = 0; - block.bounds.minWidth = block.bounds.minHeight = 10; - - Bind.addListener(this, onSizeChange, this, "position.size"); - Bind.addListener(this, onSizeChange, this, "position.space"); - } - - private function onSizeChange(size:Number):void - { - if (position == null) { - return; - } - - if (position.filled) { - scrollBarGraphic.thumb.visible = false; - return; - } - - scrollBarGraphic.thumb.visible = true; - var block:Block = LayoutWrapper.getLayout(scrollBarGraphic.thumb) as Block; - block.width = scrollBarGraphic.track.width/position.size * position.space; - } - - } -} \ No newline at end of file diff --git a/src/reflex/skins/ScrollerSkin.as b/src/reflex/skins/ScrollerSkin.as new file mode 100644 index 00000000..39149c35 --- /dev/null +++ b/src/reflex/skins/ScrollerSkin.as @@ -0,0 +1,50 @@ +package reflex.skins +{ + import flash.display.DisplayObject; + import flash.display.Shape; + import flash.events.Event; + + import mx.graphics.SolidColor; + + import reflex.binding.Bind; + import reflex.collections.SimpleCollection; + import reflex.containers.Container; + import reflex.graphics.Rect; + import reflex.injection.HardCodedInjector; + import reflex.layouts.BasicLayout; + import reflex.layouts.XYLayout; + + public class ScrollerSkin extends Skin + { + + [Bindable] + public var container:Container; + + //[Bindable] + //public var mask:DisplayObject; + + public function ScrollerSkin() + { + super(); + } + + + override protected function initialize():void { + super.initialize(); + layout = new BasicLayout(); + container = new Container(); + container.injector = new HardCodedInjector(); + //container.layout = new XYLayout(); + container.percentWidth = 100; + container.percentHeight = 100; + + var mask:Rect = new Rect(); + mask.fill = new SolidColor(0, 1); + mask.percentWidth = 100; + mask.percentHeight = 100; + //container.mask = mask; + content = new SimpleCollection([container, mask]); + } + + } +} \ No newline at end of file diff --git a/src/reflex/skins/Skin.as b/src/reflex/skins/Skin.as index 2546d0b2..c81aa57e 100644 --- a/src/reflex/skins/Skin.as +++ b/src/reflex/skins/Skin.as @@ -1,31 +1,45 @@ package reflex.skins { + import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; import flash.display.InteractiveObject; import flash.display.Sprite; import flash.events.Event; import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; import flash.geom.Point; import flash.geom.Rectangle; - import flight.binding.Bind; - import flight.events.ListEvent; - import flight.events.ListEventKind; - import flight.events.PropertyEvent; - import flight.list.ArrayList; - import flight.list.IList; + import mx.collections.IList; + import mx.events.CollectionEvent; + import mx.events.CollectionEventKind; - import reflex.components.IStateful; - import reflex.display.IContainer; - import reflex.display.ReflexDataTemplate; - import reflex.display.addItemsAt; - import reflex.events.InvalidationEvent; - import reflex.layout.LayoutWrapper; + import reflex.animation.AnimationToken; + import reflex.animation.Animator; + import reflex.animation.IAnimator; + import reflex.binding.Bind; + import reflex.collections.SimpleCollection; + import reflex.collections.convertToIList; + import reflex.framework.IStateful; + import reflex.containers.Container; + import reflex.containers.IContainer; + import reflex.display.FlashDisplayHelper; + import reflex.display.IDisplayHelper; + import reflex.display.PropertyDispatcher; + import reflex.injection.HardCodedInjector; + import reflex.injection.IReflexInjector; + import reflex.invalidation.IReflexInvalidation; + import reflex.invalidation.LifeCycle; + import reflex.layouts.BasicLayout; import reflex.layouts.ILayout; - import reflex.measurement.IMeasurable; - import reflex.measurement.resolveHeight; - import reflex.measurement.resolveWidth; + import reflex.framework.IMeasurable; + import reflex.framework.IMeasurablePercent; + //import reflex.measurement.IMeasurements; + import reflex.metadata.resolveBindings; + import reflex.metadata.resolveCommitProperties; + import reflex.states.applyState; + import reflex.states.removeState; + import reflex.templating.getDataRenderer; /** * Skin is a convenient base class for many skins, a swappable graphical @@ -33,367 +47,57 @@ package reflex.skins * adding children to the Sprite, or both. * @alpha */ - [DefaultProperty("children")] - public class Skin extends EventDispatcher implements ISkin, IContainer, IStateful//, IMeasurable + [DefaultProperty("content")] + public class Skin extends Container implements ISkin { - static public const MEASURE:String = "measure"; - static public const LAYOUT:String = "layout"; - InvalidationEvent.registerPhase(MEASURE, 0, true); - InvalidationEvent.registerPhase(LAYOUT, 0, true); - private var renderers:Array = []; + private var _target:IEventDispatcher; + private var _template:Object; + private var _content:IList; private var _layout:ILayout; - [Bindable(event="layoutChange")] - public function get layout():ILayout { return _layout; } - public function set layout(value:ILayout):void { - if(_layout) { _layout.target = null; } - _layout = value; - _layout.target = target; - if(target) { - InvalidationEvent.invalidate(target, MEASURE); - InvalidationEvent.invalidate(target, LAYOUT); - } - } - - [Bindable] - public var template:Object = new ReflexDataTemplate(); - - private var _currentState:String; [Bindable] - public function get currentState():String { return _currentState; } - public function set currentState(value:String):void { - _currentState = value; - } - - [Bindable] - public var states:Array; - - [Bindable] - public var transitions:Array; - - public function hasState(state:String):Boolean { - return true; - } - - protected var containerPart:DisplayObjectContainer; - protected var defaultContainer:Boolean = true; - private var _target:Sprite; - private var _children:IList = new ArrayList(); - public function Skin() { - _children.addEventListener(ListEvent.LIST_CHANGE, onChildrenChange); - Bind.addListener(this, onLayoutChange, this, "target.layout"); - Bind.addListener(this, onLayoutChange, this, "layout"); - //Bind.addBinding(this, "data", this, "target.data"); - Bind.addBinding(this, "state", this, "target.state"); - //addEventListener(MEASURE, onMeasure, false, 0, true); - //addEventListener(LAYOUT, onLayout, false, 0, true); + super(); + reflex.metadata.resolveBindings(this); } - //[Bindable] - //public var hostComponent:Object; - [Bindable] - public function get target():Sprite - { - return _target; - } - public function set target(value:Sprite):void + [Bindable(event="targetChange")] + public function get target():IEventDispatcher { return _target; } + public function set target(value:IEventDispatcher):void { if (_target == value) { return; } - - /* - var skinnable:IContainer; - if (_target != null && _target is IContainer) { - skinnable = _target as IContainer; - //skinnable.children.removeEventListener(ListEvent.LIST_CHANGE, onContentChange); - for (var i:int = 0; i < _children.length; i++) { - _target.removeChild(_children.getItemAt(i) as DisplayObject); - } - } - */ var oldValue:Object = _target; _target = value; - if(layout) { - layout.target = _target; - } - if(this.hasOwnProperty('hostComponent')) { + if (this.hasOwnProperty('hostComponent')) { this['hostComponent'] = _target; } if (_target != null) { - /* - //var i:int; - //for (i = 0; i < _children.length; i++) { - //_target.addChildAt(_children.getItemAt(i) as DisplayObject, i); - //} - var items:Array = []; - for (i = 0; i < _children.length; i++) { - items.push(_children.getItemAt(i)); - } - reflex.display.addItemsAt(_target, items, 0); - /* - containerPart = getSkinPart("container") as DisplayObjectContainer; - if (_target is IContainer && containerPart != null) { - - skinnable = _target as IContainer; - skinnable.children.addEventListener(ListEvent.LIST_CHANGE, onContentChange, false, 0xF); - if (skinnable.children.length > 0) { - defaultContainer = false; - Bind.addBinding(containerPart, "padding", this, "target.padding"); - while (containerPart.numChildren) { - removeContainerChildAt(containerPart.numChildren-1); - } - for (i = 0; i < skinnable.children.length; i++) { - addContainerChildAt(skinnable.children.getItemAt(i) as DisplayObject, i); - } - } - } - */ - target.addEventListener(MEASURE, onMeasure, false, 0, true); - target.addEventListener(LAYOUT, onLayout, false, 0, true); - InvalidationEvent.invalidate(target, MEASURE); - InvalidationEvent.invalidate(target, LAYOUT); - } - - PropertyEvent.dispatchChange(this, "target", oldValue, _target); - var items:Array = []; - for (var i:int = 0; i < _children.length; i++) { - items.push(_children.getItemAt(i)); - } - reset(items); - } - - protected function init():void - { - } - - [ArrayElementType("Object")] - public function get children():IList - { - return _children; - } - public function set children(value:*):void - { - if(_children == value) { - return; - } - - if(_children) { - _children.removeEventListener(ListEvent.LIST_CHANGE, onChildrenChange); - } - - if(value == null) { - _children = null; - } else if(value is IList) { - _children = value as IList; - } else if(value is Array || value is Vector) { - _children = new ArrayList(value); - } else { - _children = new ArrayList([value]); - } - - if(_children) { - _children.addEventListener(ListEvent.LIST_CHANGE, onChildrenChange); - var items:Array = []; - for (var i:int = 0; i < _children.length; i++) { - items.push(_children.getItemAt(i)); - } - reset(items); - } - } - - public function getSkinPart(part:String):InteractiveObject - { - return (part in this) ? this[part] : null; - } - - private function onChildrenChange(event:ListEvent):void - { - if (_target == null) { - return; - } - var child:DisplayObject; - var loc:int = event.location1; - switch (event.kind) { - case ListEventKind.ADD : - add(event.items, loc++); - break; - case ListEventKind.REMOVE : - for each (child in event.items) { - _target.removeChild(child); - } - break; - case ListEventKind.REPLACE : - _target.removeChild(event.items[1]); - _target.addChildAt(event.items[0], loc); - break; - case ListEventKind.RESET : - reset(event.items); - break; - } - } - - - private function add(items:Array, index:int):void { - var children:Array = reflex.display.addItemsAt(_target, items, index, template); - renderers.concat(children); // todo: correct ordering - } - - private function reset(items:Array):void { - if(_target) { - while (_target.numChildren) { - _target.removeChildAt(_target.numChildren-1); - } - renderers = reflex.display.addItemsAt(_target, items, 0, template); // todo: correct ordering - InvalidationEvent.invalidate(_target, MEASURE); - InvalidationEvent.invalidate(_target, LAYOUT); - } - } - - /* - private function onContentChange(event:ListEvent):void - { - event.stopImmediatePropagation(); - var skinnable:IContainer = _target as IContainer; - if (defaultContainer) { - defaultContainer = false; - Bind.addBinding(containerPart, "padding", this, "target.padding"); - while (containerPart.numChildren) { - removeContainerChildAt(containerPart.numChildren-1); - } - } - - var child:DisplayObject; - var loc:int = event.location1; - switch (event.kind) { - case ListEventKind.ADD : - for each (child in event.items) { - //addContainerChildAt(child, loc++); - addChildAt(target, child, loc++); - } - break; - case ListEventKind.REMOVE : - for each (child in event.items) { - removeContainerChild(child); - } - break; - case ListEventKind.REPLACE : - removeContainerChild(event.items[1]); - addContainerChildAt(event.items[0], loc); - break; - case ListEventKind.RESET : - while (containerPart.numChildren) { - removeContainerChildAt(containerPart.numChildren-1); - } - - for (var i:int = 0; i < skinnable.children.length; i++) { - addContainerChildAt(skinnable.children.getItemAt(i) as DisplayObject, i); - } - break; - } - - trace("invalidate"); - - var containerLayout:LayoutWrapper = LayoutWrapper.getLayout(containerPart); - if (containerLayout != null) { - containerLayout.invalidate(true); - } - } - - protected function addContainerChildAt(child:DisplayObject, index:int):DisplayObject - { - if (containerPart is IContainer) { - return IContainer(containerPart).children.addItemAt(child, index) as DisplayObject; - } else { - return containerPart.addChildAt(child, index); - } - } - - protected function removeContainerChildAt(index:int):DisplayObject - { - if (containerPart is IContainer) { - return IContainer(containerPart).children.removeItemAt(index) as DisplayObject; - } else { - return containerPart.removeChildAt(index); - } - } - - protected function removeContainerChild(child:DisplayObject):DisplayObject - { - if (containerPart is IContainer) { - return IContainer(containerPart).children.removeItem(child) as DisplayObject; - } else { - return containerPart.removeChild(child); - } - } - */ - private function onLayoutChange(value:ILayout):void - { - if (_target == null) { - return; - } - /* - var targetLayout:LayoutWrapper = LayoutWrapper.getLayout(_target); - - if (containerPart != null && _target is IContainer) { - var skinnable:IContainer = _target as IContainer; - var containerLayout:LayoutWrapper = LayoutWrapper.getLayout(containerPart); - if (containerLayout != null) { - //containerLayout.algorithm = skinnable.layout; - } else if (targetLayout != null) { - containerLayout = new targetLayout["constructor"](); - containerLayout.target = containerPart; - //containerLayout.algorithm = skinnable.layout; - } - } - - if (targetLayout != null) { - //targetLayout.algorithm = layout; - }*/ + display = (_target as Object).display; + // skin measurement occurs before component measurement + //target.addEventListener(LifeCycle.MEASURE, onMeasure, false, 1, true); + //target.addEventListener(LifeCycle.LAYOUT, onLayout, false, 1, true); + + + //reflex.metadata.resolveCommitProperties(this); + + } + this.owner = _target; + //if(invalidation) { invalidation.add(this); } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("target", oldValue, _target); } - private function onMeasure(event:InvalidationEvent):void { - var target:Object = this.target as Object; - if(layout && target && (isNaN(target.measurements.expliciteWidth) || isNaN(target.measurements.expliciteHeight))) { - var items:Array = []; - var length:int = _children.length; - for(var i:int = 0; i < length; i++) { - items.push(_children.getItemAt(i)); - } - var point:Point = layout.measure(items); - // this if statement blocks an infinite loop - // the lifecycle should be handled better here in some way - if(point.x != target.measurements.measuredWidth || point.y != target.measurements.measuredHeight) { - target.measurements.measuredWidth = point.x; - target.measurements.measuredHeight = point.y; - target.dispatchEvent(new Event("widthChange")); - target.dispatchEvent(new Event("heightChange")); - } - - } - InvalidationEvent.invalidate(this.target, LAYOUT); - } - - private function onLayout(event:InvalidationEvent):void { - if(layout) { - var items:Array = []; - var length:int = _children.length; - for(var i:int = 0; i < length; i++) { - items.push(_children.getItemAt(i)); - } - var rectangle:Rectangle = new Rectangle(0, 0, resolveWidth(target), resolveHeight(target)); - layout.update(items, rectangle); - } - } } } diff --git a/src/reflex/skins/StatefulBackgroundSkin.as b/src/reflex/skins/StatefulBackgroundSkin.as deleted file mode 100644 index 24b9e3ce..00000000 --- a/src/reflex/skins/StatefulBackgroundSkin.as +++ /dev/null @@ -1,63 +0,0 @@ -package reflex.skins -{ - import flash.display.Sprite; - - import flight.binding.Bind; - - [DefaultProperty("backgrounds")] - public class StatefulBackgroundSkin extends Skin - { - public var backgroundColors:String; - public var backgroundAlphas:String; - public var backgroundRatios:String; - public var backgroundAngle:String; - public var borderColors:String; - public var borderAlphas:String; - public var borderRatios:String; - public var borderAngle:String; - public var borderWidth:Number; - public var radius:String; - - protected var _backgrounds:Object = {}; - protected var currentBackground:BackgroundSkin; - - public function StatefulBackgroundSkin() - { - Bind.addListener(this, onStateChange, this, "state"); - } - - protected function onStateChange(state:String):void - { - if (currentBackground) { - currentBackground.target = null; - } - - if (state in _backgrounds) { - currentBackground = _backgrounds[state]; - currentBackground.target = target; - } - } - - private var props:Array = ["backgroundColors", "backgroundAlphas", "backgroundRatios", "backgroundAngle", - "borderColors", "borderAlphas", "borderRatios", "borderAngle", "borderWidth", "radius"]; - public function set backgrounds(value:Vector.):void - { - for each (var background:BackgroundSkin in value) { - for each (var prop:String in props) { - if (this[prop] && !background[prop]) { - background[prop] = this[prop]; - } - } - - _backgrounds[background.currentState] = background; - } - - if (currentState in _backgrounds) { - currentBackground = _backgrounds[currentState]; - currentBackground.target = target; - } - } - } - - -} \ No newline at end of file diff --git a/src/reflex/skins/StepperSkin.as b/src/reflex/skins/StepperSkin.as index 23a71ef3..998599cf 100644 --- a/src/reflex/skins/StepperSkin.as +++ b/src/reflex/skins/StepperSkin.as @@ -1,31 +1,32 @@ package reflex.skins { - import flight.binding.Bind; - import flight.position.IPosition; - - import reflex.components.StepperGraphic; + import reflex.components.Button; + import reflex.components.TextInput; + import reflex.layouts.HorizontalLayout; - public class StepperSkin extends GraphicSkin + public class StepperSkin extends Skin { - [Bindable] - public var position:IPosition; - private var stepperGraphic:StepperGraphic; + public var textField:TextInput; - public function StepperSkin(graphic:StepperGraphic = null) - { - graphic = stepperGraphic = graphic || new StepperGraphic() - super(stepperGraphic); - - stepperGraphic.label.borderColor = 0x999999; - - Bind.addBinding(this, "position", this, "target.position"); - Bind.addListener(this, updateLabel, this, "position.value"); - } + public var decrementButton:Button; + public var incrementButton:Button; - protected function updateLabel(position:int):void + public function StepperSkin() { - stepperGraphic.label.text = String(position); + super(); + layout = new HorizontalLayout(); + textField = new TextInput(""); + textField.width = 100; + textField.percentHeight = 100; + + decrementButton = new Button("-"); + decrementButton.width = 60; + decrementButton.height = 60; + incrementButton = new Button("+"); + incrementButton.width = 60; + incrementButton.height = 60; + content = [textField, decrementButton, incrementButton]; } } } \ No newline at end of file diff --git a/src/reflex/skins/TextAreaSkin.as b/src/reflex/skins/TextAreaSkin.as new file mode 100644 index 00000000..8ca9df2b --- /dev/null +++ b/src/reflex/skins/TextAreaSkin.as @@ -0,0 +1,60 @@ +package reflex.skins +{ + + import flash.events.Event; + import flash.text.TextFieldType; + import flash.text.TextFormat; + import flash.text.TextFormatAlign; + + import mx.graphics.SolidColor; + import mx.graphics.SolidColorStroke; + + import reflex.components.VSlider; + import reflex.graphics.Rect; + import reflex.layouts.BasicLayout; + import reflex.text.TextFieldDisplay; + + public class TextAreaSkin extends Skin + { + + public var textField:TextFieldDisplay; + public var verticalScrollBar:VSlider; + + public function TextAreaSkin(multiline:Boolean = false) + { + super(); + textField = new TextFieldDisplay(); + textField.type = TextFieldType.INPUT; + textField.multiline = multiline; + textField.wordWrap = true; + textField.defaultTextFormat = new TextFormat(null, 24, 0x000000, null, null, null, null, null, TextFormatAlign.JUSTIFY, 0, 0, 0, 0); + textField.defaultTextFormat.display = "bottom"; + textField.style = "left: 20; right: 0; top: 10; bottom: 5;"; + + var rect:Rect = new Rect(); + rect.fill = new SolidColor(0xFFFFFF); + rect.stroke = new SolidColorStroke(0x000000, 2); + rect.style = "left: 0; right: 0; top: 0; bottom: 0;"; + + verticalScrollBar = new VSlider(); + verticalScrollBar.style = "right: 0; top: 0; bottom: 0;"; + + layout = new BasicLayout(); + content.addItem(rect); + content.addItem(textField); + content.addItem(verticalScrollBar); + } + + override public function setSize(width:Number, height:Number):void { + super.setSize(width, height); + /* + if(scrollbar && textField && textField.maxScrollV > -1) { + scrollbar.visible = true; + } else { + scrollbar.visible = false; + } + */ + } + + } +} \ No newline at end of file diff --git a/src/reflex/skins/TextInputSkin.as b/src/reflex/skins/TextInputSkin.as new file mode 100644 index 00000000..14457217 --- /dev/null +++ b/src/reflex/skins/TextInputSkin.as @@ -0,0 +1,41 @@ +package reflex.skins +{ + + import flash.text.TextFieldType; + import flash.text.TextFormat; + import flash.text.TextFormatAlign; + + import mx.graphics.SolidColor; + import mx.graphics.SolidColorStroke; + + import reflex.graphics.Rect; + import reflex.layouts.BasicLayout; + import reflex.text.TextFieldDisplay; + + public class TextInputSkin extends Skin + { + + public var textField:TextFieldDisplay; + + public function TextInputSkin(multiline:Boolean = false) + { + super(); + textField = new TextFieldDisplay(); + textField.type = TextFieldType.INPUT; + textField.multiline = multiline; + textField.defaultTextFormat = new TextFormat(null, 24, 0x000000, null, null, null, null, null, TextFormatAlign.JUSTIFY, 0, 0, 0, 0); + textField.defaultTextFormat.display = "bottom"; + textField.style = "left: 20; right: 0; top: 10; bottom: 5;"; + + var rect:Rect = new Rect(); + rect.fill = new SolidColor(0xFFFFFF); + rect.stroke = new SolidColorStroke(0x000000, 2); + rect.style = "left: 0; right: 0; top: 0; bottom: 0;"; + + this.layout = new BasicLayout(); + content.addItem(rect); + content.addItem(textField); + } + + } +} \ No newline at end of file diff --git a/src/reflex/skins/VSliderSkin.as b/src/reflex/skins/VSliderSkin.as new file mode 100644 index 00000000..82b8a38d --- /dev/null +++ b/src/reflex/skins/VSliderSkin.as @@ -0,0 +1,55 @@ +package reflex.skins +{ + import flash.display.GradientType; + import flash.display.Graphics; + import flash.display.Sprite; + import flash.events.Event; + import flash.geom.Matrix; + import flash.text.TextField; + import flash.text.TextFormat; + + import reflex.layouts.BasicLayout; + import reflex.text.Label; + import reflex.text.TextFieldDisplay; + + public class VSliderSkin extends GraphicSkin + { + + public var thumb:Sprite; + public var track:Sprite; + + public function VSliderSkin() + { + super(); + unscaledWidth = 14; + track = new Sprite(); + renderTrack(track.graphics); + thumb = new Sprite(); + renderThumb(thumb.graphics); + //layout = new BasicLayout(); + content = [track, thumb]; + _measuredWidth = 14; + _measuredHeight = 170; + } + + override protected function render(currentState:String):void { + renderTrack(track.graphics); + renderThumb(thumb.graphics); + } + + private function renderThumb(g:Graphics):void { + g.clear(); + g.beginFill(0xFFFFFF, 1); + g.drawRect(0, 0, unscaledWidth, 14); + g.endFill(); + } + + private function renderTrack(g:Graphics):void { + g.clear(); + g.beginFill(0x363636, 1); + g.drawRect(0, 0, unscaledWidth, unscaledHeight); + g.endFill(); + } + + } +} \ No newline at end of file diff --git a/src/reflex/states/applyState.as b/src/reflex/states/applyState.as new file mode 100644 index 00000000..fa10b0fc --- /dev/null +++ b/src/reflex/states/applyState.as @@ -0,0 +1,19 @@ +package reflex.states +{ + + import mx.states.IOverride; + import mx.states.State; + + public function applyState(target:Object, name:String, states:Array):void + { + for each(var state:State in states) { + if(state.name == name) { + for each(var o:IOverride in state.overrides) { + o.apply(target); + } + return; + } + } + } + +} \ No newline at end of file diff --git a/src/reflex/states/removeState.as b/src/reflex/states/removeState.as new file mode 100644 index 00000000..194b8a78 --- /dev/null +++ b/src/reflex/states/removeState.as @@ -0,0 +1,19 @@ +package reflex.states +{ + + import mx.states.IOverride; + import mx.states.State; + + public function removeState(target:Object, name:String, states:Array):void + { + for each(var state:State in states) { + if(state.name == name) { + for each(var o:IOverride in state.overrides) { + o.remove(target); + } + return; + } + } + } + +} \ No newline at end of file diff --git a/src/reflex/styles/IStylable.as b/src/reflex/styles/IStylable.as deleted file mode 100644 index 0c3106c1..00000000 --- a/src/reflex/styles/IStylable.as +++ /dev/null @@ -1,16 +0,0 @@ -package reflex.styles -{ - - public interface IStylable - { - function get id():String; - function set id(value:String):void; - - function get styleName():String; - function set styleName(value:String):void; - - function get state():String; - function set state(value:String):void; - } - -} diff --git a/src/reflex/styles/Style.as b/src/reflex/styles/Style.as new file mode 100644 index 00000000..e13a2a27 --- /dev/null +++ b/src/reflex/styles/Style.as @@ -0,0 +1,9 @@ +package reflex.styles +{ + dynamic public class Style + { + public function Style() + { + } + } +} \ No newline at end of file diff --git a/src/reflex/styles/hasStyle.as b/src/reflex/styles/hasStyle.as index f241ef94..977f87b2 100644 --- a/src/reflex/styles/hasStyle.as +++ b/src/reflex/styles/hasStyle.as @@ -1,9 +1,14 @@ package reflex.styles { + import reflex.framework.IStyleable; + public function hasStyle(child:Object, property:String):Boolean { - if(child.hasOwnProperty("style") && child["style"] != null) { - return (child.style[property] != null); + //if(child.hasOwnProperty("style") && child["style"] != null) { + //return (child.style[property] != null); + if(child is IStyleable) { + var v:* = (child as IStyleable).getStyle(property); + return v != null; } else { return false; } diff --git a/src/reflex/styles/parseStyles.as b/src/reflex/styles/parseStyles.as new file mode 100644 index 00000000..8a4abe41 --- /dev/null +++ b/src/reflex/styles/parseStyles.as @@ -0,0 +1,20 @@ +package reflex.styles +{ + public function parseStyles(style:Object, token:String):void + { + var assignments:Array = token.split(";"); + for each(var assignment:String in assignments) { + var split:Array = assignment.split(":"); + if (split.length == 2) { + var property:String = split[0].replace(/\s+/g, ""); + var v:String = split[1].replace(/\s+/g, ""); + if(!isNaN( Number(v) )) { + style[property] = Number(v); + } else { + style[property] = v; + } + } + } + } + +} \ No newline at end of file diff --git a/src/reflex/styles/resolveStyle.as b/src/reflex/styles/resolveStyle.as index a751257a..6f032a98 100644 --- a/src/reflex/styles/resolveStyle.as +++ b/src/reflex/styles/resolveStyle.as @@ -1,9 +1,14 @@ package reflex.styles { + import reflex.framework.IStyleable; + public function resolveStyle(child:Object, property:String, type:Object = null, standard:* = null):Object { - if(child.hasOwnProperty("style") && child["style"] != null) { - return child.style[property]; + //if(child.hasOwnProperty("style") && child["style"] != null) { + //return child.style[property]; + if(child is IStyleable) { + var v:* = (child as IStyleable).getStyle(property); + return v != null ? v : standard; } else { return standard; } diff --git a/src/reflex/display/IDataTemplate.as b/src/reflex/templating/IDataTemplate.as similarity index 87% rename from src/reflex/display/IDataTemplate.as rename to src/reflex/templating/IDataTemplate.as index dd88aba0..b47033e0 100644 --- a/src/reflex/display/IDataTemplate.as +++ b/src/reflex/templating/IDataTemplate.as @@ -1,4 +1,4 @@ -package reflex.display +package reflex.templating { public interface IDataTemplate { diff --git a/src/reflex/templating/getDataRenderer.as b/src/reflex/templating/getDataRenderer.as new file mode 100644 index 00000000..727b2ebe --- /dev/null +++ b/src/reflex/templating/getDataRenderer.as @@ -0,0 +1,43 @@ +package reflex.templating +{ + import flash.display.DisplayObject; + + import mx.core.IDataRenderer; + import mx.core.IFactory; + + import reflex.display.MeasurableItem; + import reflex.graphics.IGraphicItem; + + /** + * Returns a renderer to be used for the given data according to the given template. + */ + public function getDataRenderer(container:Object, data:*, template:Object):Object + { + var instance:*; + if (template is IDataTemplate) { + instance = (template as IDataTemplate).createDisplayObject(data); + } else if (template is IFactory) { + instance = (template as IFactory).newInstance(); + } else if (template is Class) { + var C:Class = template as Class; + instance = new C(); + } else if (template is Function) { + instance = (template as Function)(data); + } else if (data is DisplayObject) { + instance = data as DisplayObject; + } /*(else if (template is DisplayObject) { + // clone + + }*/ else { + //instance = new MeasurableItem(); + } + if (instance is IDataRenderer) { + (instance as IDataRenderer).data = data; + } + //if (data is IGraphicItem) { + // (data as IGraphicItem).target = container; + //} + return instance != null ? instance : data; + } + +} \ No newline at end of file diff --git a/src/reflex/text/FTELabel.as b/src/reflex/text/FTELabel.as deleted file mode 100644 index f96017a5..00000000 --- a/src/reflex/text/FTELabel.as +++ /dev/null @@ -1,396 +0,0 @@ -package reflex.text -{ - import flash.display.Sprite; - import flash.events.Event; - import flash.text.engine.CFFHinting; - import flash.text.engine.ElementFormat; - import flash.text.engine.FontDescription; - import flash.text.engine.FontLookup; - import flash.text.engine.FontPosture; - import flash.text.engine.FontWeight; - import flash.text.engine.RenderingMode; - import flash.text.engine.TextBlock; - import flash.text.engine.TextElement; - import flash.text.engine.TextLine; - import flash.text.engine.TextLineValidity; - import flash.text.engine.TypographicCase; - - import flight.binding.Bind; - - import reflex.events.InvalidationEvent; - import reflex.layout.Block; - import reflex.layout.Bounds; - import reflex.layout.Box; - import reflex.layout.ILayoutAlgorithm; - import reflex.layout.LayoutWrapper; - - public class FTELabel extends Sprite - { - public static const TEXT_RENDER:String = "textRender"; - private static var textPhase:Boolean = InvalidationEvent.registerPhase(TEXT_RENDER, 0x90, false); - - [Bindable] - public var freeform:Boolean = false; - - public var block:Block; - - - protected var fontFormat:FontDescription; - protected var format:ElementFormat; - protected var textElement:TextElement; - public var textBlock:TextBlock; - protected var line:TextLine; - - - public function FTELabel() - { - fontFormat = new FontDescription(); - format = new ElementFormat(); - textElement = new TextElement(""); - textBlock = new TextBlock(textElement); - mouseChildren = false; - - initLayout(); - addEventListener(Event.ADDED, onInit); - addEventListener(TEXT_RENDER, onTextRender); - addEventListener(LayoutWrapper.LAYOUT, onRender); - } - - public function get text():String - { - return textElement.text; - } - - public function set text(value:String):void - { - if (value == textElement.text) return; - textElement.text = value; - InvalidationEvent.invalidate(this, TEXT_RENDER); - } - - public function get embed():Boolean - { - return (fontFormat.fontLookup == FontLookup.EMBEDDED_CFF); - } - - public function set embed(value:Boolean):void - { - fontFormat.fontLookup = value ? FontLookup.EMBEDDED_CFF : FontLookup.DEVICE; - } - - public function get color():uint - { - return format.color; - } - - public function set color(value:uint):void - { - if (value == format.color) return; - format.color = value; - InvalidationEvent.invalidate(this, TEXT_RENDER); - } - - public function get fontFamily():String - { - return fontFormat.fontName; - } - - public function set fontFamily(value:String):void - { - if (value == fontFormat.fontName) return; - fontFormat.fontName = value; - InvalidationEvent.invalidate(this, TEXT_RENDER); - } - - public function get fontSize():Number - { - return format.fontSize; - } - - public function set fontSize(value:Number):void - { - if (value == format.fontSize) return; - format.fontSize = value; - InvalidationEvent.invalidate(this, TEXT_RENDER); - } - - public function get bold():Boolean - { - return fontFormat.fontWeight == FontWeight.BOLD; - } - - public function set bold(value:Boolean):void - { - if (value == (fontFormat.fontWeight == FontWeight.BOLD)) return; - fontFormat.fontWeight = value ? FontWeight.BOLD : FontWeight.NORMAL; - InvalidationEvent.invalidate(this, TEXT_RENDER); - } - - public function get italic():Boolean - { - return fontFormat.fontPosture == FontPosture.ITALIC; - } - - public function set italic(value:Boolean):void - { - if (value == (fontFormat.fontPosture == FontPosture.ITALIC)) return; - fontFormat.fontPosture = value ? FontPosture.ITALIC : FontPosture.NORMAL; - InvalidationEvent.invalidate(this, TEXT_RENDER); - } - - protected function onTextRender(event:InvalidationEvent):void - { - validateText(); - } - - public function validateText():void - { - while (numChildren) removeChildAt(0); - - format.fontDescription = fontFormat; - textElement.elementFormat = format; - format = format.clone(); - fontFormat = fontFormat.clone(); - - line = textBlock.createTextLine(); - - if (line) { - line.x = 2; - line.y = line.ascent + 2; - addChild(line); - block.defaultWidth = Math.round(line.textWidth + 4); - block.defaultHeight = Math.round(line.textHeight + 4); - } else { - block.defaultWidth = 4; - block.defaultHeight = format.fontSize + 4; - } - } - - [Bindable(event="xChange")] - override public function get x():Number - { - return super.x; - } - override public function set x(value:Number):void - { - if (super.x == value) { - return; - } - - super.x = value; - block.x = value; - } - - [Bindable(event="yChange")] - override public function get y():Number - { - return block.y; - } - override public function set y(value:Number):void - { - if (super.y == value) { - return; - } - - super.y = value; - block.y = value; - } - - [Bindable(event="widthChange")] - override public function get width():Number - { - return displayWidth * scaleX; - } - override public function set width(value:Number):void - { - displayWidth = value / scaleY; - } - - [Bindable(event="heightChange")] - override public function get height():Number - { - return displayHeight * scaleY; - } - override public function set height(value:Number):void - { - displayHeight = value / scaleY; - } - - - [Bindable(event="displayWidthChange")] - public function get displayWidth():Number - { - return block.displayWidth; - } - public function set displayWidth(value:Number):void - { - block.displayWidth = value; - } - - [Bindable(event="displayHeightChange")] - public function get displayHeight():Number - { - return block.displayHeight; - } - public function set displayHeight(value:Number):void - { - block.displayHeight = value; - } - - [Bindable(event="snapToPixelChange")] - public function get snapToPixel():Boolean - { - return block.snapToPixel; - } - public function set snapToPixel(value:Boolean):void - { - block.snapToPixel = value; - } - - - [Bindable(event="layoutChange")] - public function get layout():ILayoutAlgorithm - { - return block.algorithm; - } - public function set layout(value:ILayoutAlgorithm):void - { - block.algorithm = value; - } - - [Bindable(event="boundsChange")] - public function get bounds():Bounds - { - return block.bounds; - } - public function set bounds(value:Bounds):void - { - block.bounds = value; - } - - [Bindable(event="marginChange")] - public function get margin():Box - { - return block.margin; - } - public function set margin(value:*):void - { - block.margin = value; - } - - [Bindable(event="paddingChange")] - public function get padding():Box - { - return block.padding; - } - public function set padding(value:*):void - { - block.padding = value; - } - - [Bindable(event="anchorChange")] - public function get anchor():Box - { - return block.anchor; - } - public function set anchor(value:*):void - { - block.anchor = value; - } - - [Bindable(event="dockChange")] - public function get dock():String - { - return block.dock; - } - public function set dock(value:String):void - { - block.dock = value; - } - - [Bindable(event="alignChange")] - public function get align():String - { - return block.align; - } - public function set align(value:String):void - { - block.align = value; - } - - - - public function invalidate(children:Boolean = false):void - { - block.invalidate(children); - } - - public function validate():void - { - block.validate(); - } - - - protected function draw():void - { - graphics.clear(); - graphics.beginFill(0, 0); - graphics.drawRect(0, 0, displayWidth, displayHeight); - graphics.endFill(); - } - - protected function init():void - { - } - - protected function initLayout():void - { - block = new Block(); - block.addEventListener("xChange", forwardEvent); - block.addEventListener("yChange", forwardEvent); - block.addEventListener("displayWidthChange", forwardEvent); - block.addEventListener("displayWidthChange", onWidthChange); - block.addEventListener("displayHeightChange", forwardEvent); - block.addEventListener("displayHeightChange", onHeightChange); - block.addEventListener("snapToPixelChange", forwardEvent); - block.addEventListener("layoutChange", forwardEvent); - block.addEventListener("boundsChange", forwardEvent); - block.addEventListener("marginChange", forwardEvent); - block.addEventListener("paddingChange", forwardEvent); - block.addEventListener("dockChange", forwardEvent); - block.addEventListener("alignChange", forwardEvent); - Bind.addBinding(block, "freeform", this, "freeform", true); - } - - private function onRender(event:Event):void - { - draw(); - } - - private function onInit(event:Event):void - { - if (event.target != this) { - return; - } - removeEventListener(Event.ADDED, onInit); - - block.target = this; - init(); - } - - private function forwardEvent(event:Event):void - { - dispatchEvent(event); - } - - private function onWidthChange(event:Event):void - { - dispatchEvent( new Event("widthChange") ); - } - - private function onHeightChange(event:Event):void - { - dispatchEvent( new Event("heightChange") ); - } - } -} \ No newline at end of file diff --git a/src/reflex/text/Label.as b/src/reflex/text/Label.as new file mode 100644 index 00000000..393d1a58 --- /dev/null +++ b/src/reflex/text/Label.as @@ -0,0 +1,265 @@ +package reflex.text +{ + import flash.display.Sprite; + import flash.events.Event; + import flash.text.engine.CFFHinting; + import flash.text.engine.ElementFormat; + import flash.text.engine.FontDescription; + import flash.text.engine.FontLookup; + import flash.text.engine.FontPosture; + import flash.text.engine.FontWeight; + import flash.text.engine.LineJustification; + import flash.text.engine.RenderingMode; + import flash.text.engine.SpaceJustifier; + import flash.text.engine.TextBlock; + import flash.text.engine.TextElement; + import flash.text.engine.TextJustifier; + import flash.text.engine.TextLine; + import flash.text.engine.TextLineValidity; + import flash.text.engine.TypographicCase; + + import reflex.display.MeasurableItem; + import reflex.invalidation.Invalidation; + import reflex.invalidation.LifeCycle; + import reflex.styles.resolveStyle; + + + [Style(name="align")] + [Style(name="txtAlign", format="String", enumeration="left,right,center,justify")] + //[Style(name="verticalAlign")] + public class Label extends MeasurableItem + { + + public static const LEFT:String = "left"; + public static const RIGHT:String = "right"; + public static const CENTER:String = "center"; + public static const JUSTIFY:String = "justify"; + + //public static const TEXT_RENDER:String = "textRender"; + //private static var textPhase:Boolean = Invalidation.registerPhase(TEXT_RENDER, 0, true); + + protected var fontFormat:FontDescription; + protected var format:ElementFormat; + protected var textElement:TextElement; + protected var textBlock:TextBlock; + protected var line:TextLine; + protected var lineJustifier:SpaceJustifier = new SpaceJustifier("en", LineJustification.UNJUSTIFIED); + + protected var _allowWrap:Boolean = false; + protected var _clipText:Boolean = false; + /* + override public function set display(value:Object):void { + super.display = value; + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + } + */ + public function Label(text:String = "") + { + fontFormat = new FontDescription(); + format = new ElementFormat(null, 24); + textElement = new TextElement(""); + textBlock = new TextBlock(textElement); + this.text = text; + } + /* + override protected function initialize():void { + super.initialize(); + + } + */ + [Bindable(event="textChange")] + public function get text():String { return textElement.text; } + public function set text(value:String):void { + if (value == textElement.text) { + return; + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("text", textElement.text, textElement.text = value); + } + + [Bindable(event="allowWrapChange")] + public function get allowWrap():Boolean { + return _allowWrap; + } + public function set allowWrap(value:Boolean):void { + if(value == _allowWrap) { + return; + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("allowWrap", _allowWrap, _allowWrap = value); + } + + [Bindable(event="clipTextChange")] + public function get clipText():Boolean { + return _clipText; + } + public function set clipText(value:Boolean):void { + if(value == _clipText) { + return; + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("clipText", _clipText, _clipText = value); + } + + [Bindable(event="embedChange")] + public function get embed():Boolean { + return (fontFormat.fontLookup == FontLookup.EMBEDDED_CFF); + } + public function set embed(value:Boolean):void { + notify("embed", fontFormat.fontLookup == FontLookup.EMBEDDED_CFF, fontFormat.fontLookup = value ? FontLookup.EMBEDDED_CFF : FontLookup.DEVICE); + } + + [Bindable(event="colorChange")] + public function get color():uint { return format.color; } + public function set color(value:uint):void { + if (value == format.color) { + return; + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("color", format.color, format.color = value); + } + + [Bindable(event="fontFamilyChange")] + public function get fontFamily():String { return fontFormat.fontName; } + public function set fontFamily(value:String):void { + if (value == fontFormat.fontName) { + return; + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("fontFamily", fontFormat.fontName, fontFormat.fontName = value); + } + + [Bindable(event="fontSizeChange")] + public function get fontSize():Number { return format.fontSize; } + public function set fontSize(value:Number):void { + if (value == format.fontSize) { + return; + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("fontSize", format.fontSize, format.fontSize = value); + } + + [Bindable(event="boldChange")] + public function get bold():Boolean { + return fontFormat.fontWeight == FontWeight.BOLD; + } + public function set bold(value:Boolean):void { + if (value == (fontFormat.fontWeight == FontWeight.BOLD)) { + return; + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("bold", fontFormat.fontWeight == FontWeight.BOLD, fontFormat.fontWeight = value ? FontWeight.BOLD : FontWeight.NORMAL); + } + + [Bindable(event="italicChange")] + public function get italic():Boolean { + return fontFormat.fontPosture == FontPosture.ITALIC; + } + public function set italic(value:Boolean):void + { + if (value == (fontFormat.fontPosture == FontPosture.ITALIC)) { + return; + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + notify("italic", fontFormat.fontPosture == FontPosture.ITALIC, fontFormat.fontPosture = value ? FontPosture.ITALIC : FontPosture.NORMAL); + } + + protected function alignText(align:String, line:TextLine):void { + switch(align) { + case LEFT: + line.x = 0; + break; + case RIGHT: + line.x = unscaledWidth-line.textWidth; + break; + case CENTER: + line.x = unscaledWidth/2-line.textWidth/2; + break; + } + } + + protected function verticalAlignText(line:TextLine):void { + line.y = unscaledHeight/2 + line.textHeight/2 - 5; // fuzzy math here + } + + override protected function onMeasure():void { + super.onMeasure(); + onLayout(); // todo: need proper measurement + } + + override protected function onLayout():void + { + super.onLayout(); + while (helper.getNumChildren(display)) helper.removeChildAt(display, 0); + + format.fontDescription = fontFormat; + textElement.elementFormat = format; + + format = format.clone(); + fontFormat = fontFormat.clone(); + + var startY:int = 0; + var align:String = resolveStyle(this, "txtAlign", String, CENTER) as String; + + lineJustifier.lineJustification = (align == JUSTIFY) ? LineJustification.ALL_BUT_LAST : LineJustification.UNJUSTIFIED; + textBlock.textJustifier = lineJustifier; + + if(allowWrap) { + var l:TextLine = line = textBlock.createTextLine(null, unscaledWidth); + while(l) { + startY += l.height; + l.y = startY; + alignText(align, l); + helper.addChild(display, l); + l = textBlock.createTextLine(l, unscaledWidth); + } + _measuredHeight = startY; + } else { + line = textBlock.createTextLine(null, clipText ? unscaledWidth : 100000); + if(line) { + _measuredWidth = line.textWidth; + _measuredHeight = line.textHeight; + line.y = line.height; //height/2 + line.height/2-3; + alignText(align, line); + verticalAlignText(line); + helper.addChild(display, line); + } else { + _measuredWidth = 0; + _measuredHeight = 0; + } + } + + } + + + override public function setSize(width:Number, height:Number):void { + super.setSize(width, height); + // we'll need to invalidate seperate measurement and layout passes later + if(line) { + if(!allowWrap) { + verticalAlignText(line); + } + var l:TextLine = line; + var align:String = resolveStyle(this, "txtAlign", String, CENTER) as String; + while(l) { + alignText(align, l); + l = l.nextLine; + } + + } + invalidate(LifeCycle.MEASURE); + invalidate(LifeCycle.LAYOUT); + } + + + } +} \ No newline at end of file diff --git a/src/reflex/text/Text.as b/src/reflex/text/Text.as index 899aa765..230f2c14 100644 --- a/src/reflex/text/Text.as +++ b/src/reflex/text/Text.as @@ -1,473 +1,29 @@ package reflex.text { + import flash.events.Event; - import flash.events.KeyboardEvent; - import flash.geom.Rectangle; - import flash.text.AntiAliasType; - import flash.text.TextExtent; - import flash.text.TextField; import flash.text.TextFieldType; - import flash.text.TextFormat; import flash.text.TextLineMetrics; - import flight.binding.Bind; - import flight.observers.PropertyChange; - - import mx.events.ScrollEvent; - - import reflex.events.InvalidationEvent; - import reflex.layout.Block; - import reflex.layout.Bounds; - import reflex.layout.Box; - import reflex.layout.ILayoutAlgorithm; - import reflex.layout.LayoutWrapper; + import reflex.metadata.resolveCommitProperties; + [Style(name="left")] + [Style(name="right")] + [Style(name="top")] + [Style(name="bottom")] + [Style(name="horizontalCenter")] + [Style(name="verticalCenter")] + [Style(name="dock")] + [Style(name="align")] - public class Text extends TextField + public class Text extends TextFieldDisplay { - public static const TEXT_CHANGE:String = "textChange"; - InvalidationEvent.registerPhase(TEXT_CHANGE, 0x90); // before measure - - [Bindable] - public var freeform:Boolean = false; - - public var block:Block; - - protected var offsetY:Number = 0; - protected var _lineHeight:Number; - protected var format:TextFormat; - - public function Text() { - format = defaultTextFormat; - antiAliasType = AntiAliasType.ADVANCED; - background = true; - backgroundColor = 0xeeeeee; - multiline = true; - - initLayout(); - updateDefaultSize(); - addEventListener(Event.ADDED, onInit); - addEventListener(LayoutWrapper.LAYOUT, onRender); - - addEventListener(Event.CHANGE, onChange); - addEventListener(TEXT_CHANGE, onTextChange); - } - - private function onChange(event:Event):void - { - InvalidationEvent.invalidate(this, TEXT_CHANGE); - } - - private function onTextChange(event:InvalidationEvent):void - { - updateDefaultSize(); - } - - protected function updateDefaultSize():void - { - block.defaultWidth = Math.ceil(textWidth) + 4; // gutter is 2px all around - block.defaultHeight = Math.ceil(textHeight) + 4; - scrollH = 0; - } - - public function get editable():Boolean - { - return type == TextFieldType.INPUT; - } - - public function set editable(value:Boolean):void - { - if (value == (type == TextFieldType.INPUT)) return; - var change:PropertyChange = PropertyChange.begin(); - value = change.add(this, "editable", (type == TextFieldType.INPUT), value); - type = value ? TextFieldType.INPUT : TextFieldType.DYNAMIC; - change.commit(); - } - - public function get lineHeight():Number - { - if (isNaN(_lineHeight)) { - var metrics:TextLineMetrics = getLineMetrics(0); - return Math.round(metrics.ascent + metrics.descent + metrics.leading); - } else { - return _lineHeight; - } - } - - public function set lineHeight(value:Number):void - { - var change:PropertyChange = PropertyChange.begin(); - _lineHeight = change.add(this, "lineHeight", _lineHeight, value); - - if (change.hasChanged()) { - if (isNaN(_lineHeight)) { - format.leading = 0; - super.y = y; - defaultTextFormat = format; - setTextFormat(format); - } else { - updateLineHeight(); - } - } - change.commit(); - } - - protected function updateLineHeight():void - { - if (isNaN(_lineHeight)) return; - - var fixing:Boolean; - if (text == "") { - fixing = true; - text = " "; - } - - var metrics:TextLineMetrics = getLineMetrics(0); - format.leading = _lineHeight - metrics.ascent - metrics.descent; - offsetY = Number(format.leading)/2; - super.y = y + offsetY; - defaultTextFormat = format; - setTextFormat(format); - - if (fixing) { - text = ""; - } - } - - override public function set text(value:String):void - { - super.text = value; - InvalidationEvent.invalidate(this, TEXT_CHANGE); - } - - override public function set htmlText(value:String):void - { - super.htmlText = value; - InvalidationEvent.invalidate(this, TEXT_CHANGE); - } - - public function get font():String - { - return format.font; - } - - public function set font(value:String):void - { - if (value == format.font) return; - format.font = value; - defaultTextFormat = format; - setTextFormat(format); - updateLineHeight(); - } - - public function get size():Number - { - return format.size as Number; - } - - public function set size(value:Number):void - { - if (value == format.size) return; - format.size = value; - defaultTextFormat = format; - setTextFormat(format); - updateLineHeight(); - } - - public function get color():uint - { - return format.color as uint; - } - - public function set color(value:uint):void - { - if (value == format.color) return; - format.color = value; - defaultTextFormat = format; - setTextFormat(format); - } - - public function get bold():Boolean - { - return format.bold as Boolean; - } - - public function set bold(value:Boolean):void - { - if (value == format.bold) return; - format.bold = value; - defaultTextFormat = format; - - } - - public function get italic():Boolean - { - return format.italic; - } - - public function set italic(value:Boolean):void - { - if (value == format.italic) return; - format.italic = value; - defaultTextFormat = format; - setTextFormat(format); - } - - public function get underline():Boolean - { - return format.underline; - } - - public function set underline(value:Boolean):void - { - if (value == format.underline) return; - format.underline = value; - defaultTextFormat = format; - setTextFormat(format); - } - - public function get align():String - { - return format.align; - } - - public function set align(value:String):void - { - if (value == format.align) return; - format.align = value; - defaultTextFormat = format; - setTextFormat(format); - } - - [Bindable(event="xChange")] - override public function get x():Number - { - return super.x; - } - override public function set x(value:Number):void - { - if (super.x == value) { - return; - } - - super.x = value; - block.x = value; - } - - [Bindable(event="yChange")] - override public function get y():Number - { - return block.y; - } - override public function set y(value:Number):void - { - if (super.y + offsetY == value) { - return; - } - - super.y = value + offsetY; - block.y = value; - } - - [Bindable(event="widthChange")] - override public function get width():Number - { - return displayWidth * scaleX; - } - override public function set width(value:Number):void - { - displayWidth = value / scaleY; - } - - [Bindable(event="heightChange")] - override public function get height():Number - { - return displayHeight * scaleY; - } - override public function set height(value:Number):void - { - displayHeight = value / scaleY; - } - - public function get actualWidth():Number - { - return super.width; - } - - public function get actualHeight():Number - { - return super.height; - } - - - [Bindable(event="displayWidthChange")] - public function get displayWidth():Number - { - return block.displayWidth; - } - public function set displayWidth(value:Number):void - { - block.displayWidth = value; - } - - [Bindable(event="displayHeightChange")] - public function get displayHeight():Number - { - return block.displayHeight; - } - public function set displayHeight(value:Number):void - { - block.displayHeight = value; + super(); + this.selectable = false; + reflex.metadata.resolveCommitProperties(this); } - [Bindable(event="snapToPixelChange")] - public function get snapToPixel():Boolean - { - return block.snapToPixel; - } - public function set snapToPixel(value:Boolean):void - { - block.snapToPixel = value; - } - - - [Bindable(event="layoutChange")] - public function get layout():ILayoutAlgorithm - { - return block.algorithm; - } - public function set layout(value:ILayoutAlgorithm):void - { - block.algorithm = value; - } - - [Bindable(event="boundsChange")] - public function get bounds():Bounds - { - return block.bounds; - } - public function set bounds(value:Bounds):void - { - block.bounds = value; - } - - [Bindable(event="marginChange")] - public function get margin():Box - { - return block.margin; - } - public function set margin(value:*):void - { - block.margin = value; - } - - [Bindable(event="paddingChange")] - public function get padding():Box - { - return block.padding; - } - public function set padding(value:*):void - { - block.padding = value; - } - - [Bindable(event="anchorChange")] - public function get anchor():Box - { - return block.anchor; - } - public function set anchor(value:*):void - { - block.anchor = value; - } - - [Bindable(event="dockChange")] - public function get dock():String - { - return block.dock; - } - public function set dock(value:String):void - { - block.dock = value; - } - - [Bindable(event="alignChange")] - public function get layoutAlign():String - { - return block.align; - } - public function set layoutAlign(value:String):void - { - block.align = value; - } - - - - public function invalidate(children:Boolean = false):void - { - block.invalidate(children); - } - - public function validate():void - { - block.validate(); - } - - protected function init():void - { - } - - protected function initLayout():void - { - block = new Block(); - block.addEventListener("xChange", forwardEvent); - block.addEventListener("yChange", forwardEvent); - block.addEventListener("displayWidthChange", forwardEvent); - block.addEventListener("displayWidthChange", onWidthChange); - block.addEventListener("displayHeightChange", forwardEvent); - block.addEventListener("displayHeightChange", onHeightChange); - block.addEventListener("snapToPixelChange", forwardEvent); - block.addEventListener("layoutChange", forwardEvent); - block.addEventListener("boundsChange", forwardEvent); - block.addEventListener("marginChange", forwardEvent); - block.addEventListener("paddingChange", forwardEvent); - block.addEventListener("dockChange", forwardEvent); - block.addEventListener("alignChange", forwardEvent); - Bind.addBinding(block, "freeform", this, "freeform", true); - } - - private function onRender(event:Event):void - { - super.width = width; - super.height = height; - } - - private function onInit(event:Event):void - { - if (event.target != this) { - return; - } - removeEventListener(Event.ADDED, onInit); - - block.target = this; - init(); - } - - private function forwardEvent(event:Event):void - { - dispatchEvent(event); - } - - private function onWidthChange(event:Event):void - { - dispatchEvent( new Event("widthChange") ); - } - - private function onHeightChange(event:Event):void - { - dispatchEvent( new Event("heightChange") ); - } } } \ No newline at end of file diff --git a/src/reflex/text/TextFieldDisplay.as b/src/reflex/text/TextFieldDisplay.as new file mode 100644 index 00000000..5d833376 --- /dev/null +++ b/src/reflex/text/TextFieldDisplay.as @@ -0,0 +1,257 @@ +package reflex.text +{ + import flash.events.Event; + import flash.events.IEventDispatcher; + import flash.events.TextEvent; + import flash.text.TextField; + import flash.text.TextFieldType; + import flash.text.TextFormat; + import flash.text.TextLineMetrics; + + import reflex.events.DataChangeEvent; + import reflex.framework.IMeasurable; + import reflex.framework.IMeasurablePercent; + //import reflex.measurement.IMeasurements; + import reflex.framework.IStyleable; + import reflex.styles.Style; + + [Style(name="left")] + [Style(name="right")] + [Style(name="top")] + [Style(name="bottom")] + [Style(name="horizontalCenter")] + [Style(name="verticalCenter")] + public class TextFieldDisplay extends TextField implements IStyleable, IMeasurable, IMeasurablePercent + { + + private var _id:String; + private var _styleName:String; + private var _style:Style; + + //private var _explicit:IMeasurements; + //private var _measured:IMeasurements; + + private var _explicitWidth:Number; + private var _explicitHeight:Number; + protected var _measuredWidth:Number; + protected var _measuredHeight:Number; + + private var _percentWidth:Number; + private var _percentHeight:Number; + + private var _unscaledWidth:Number = 160; + private var _unscaledHeight:Number = 22; + + + protected function get unscaledWidth():Number { return _unscaledWidth; } + protected function set unscaledWidth(value:Number):void { + _unscaledWidth = value; + super.width = _unscaledWidth; + } + + protected function get unscaledHeight():Number { return _unscaledHeight; } + protected function set unscaledHeight(value:Number):void { + _unscaledHeight = value; + super.height = _unscaledHeight; + } + + public function TextFieldDisplay() { + super(); + _style = new Style(); // need to make object props bindable - something like ObjectProxy but lighter? + //_explicit = new Measurements(this); + //_measured = new Measurements(this, 160, 22); + _measuredWidth = 160; + _measuredHeight = 22; + addEventListener(Event.CHANGE, textChange, false, 0, true); + addEventListener(Event.CHANGE, onMeasure, false, 0, true); + //onMeasure(null); + } + + [Bindable(event="textChange", noEvent)] + override public function get text():String { return super.text; } + override public function set text(value:String):void { + if(value == null) { + notify("text", super.text, null); + super.text = ""; + } else { + notify("text", super.text, super.text = value); + } + onMeasure(null); + } + + [Bindable(event="htmlTextChange", noEvent)] + override public function get htmlText():String { return super.text; } + override public function set htmlText(value:String):void { + if(value == null) { + notify("htmlText", super.htmlText, null); + super.htmlText = ""; + } else { + notify("htmlText", super.htmlText, super.htmlText = value); + } + onMeasure(null); + } + + override public function set defaultTextFormat(value:TextFormat):void { + notify("defaultTextFormat", super.defaultTextFormat, super.defaultTextFormat = value); + super.text = text; + } + + // IStyleable implementation + + [Bindable(event="idChange", noEvent)] + public function get id():String { return _id; } + public function set id(value:String):void { + notify("id", _id, _id = value); + } + + [Bindable(event="styleNameChange", noEvent)] + public function get styleName():String { return _styleName;} + public function set styleName(value:String):void { + notify("styleName", _styleName, _styleName= value); + } + + [Bindable(event="styleChange", noEvent)] + public function get style():Style { return _style; } + public function set style(value:*):void { // this needs expanding in the future + if (value is String) { + var token:String = value as String; + var assignments:Array = token.split(";"); + for each(var assignment:String in assignments) { + var split:Array = assignment.split(":"); + if (split.length == 2) { + var property:String = split[0].replace(/\s+/g, ""); + var v:String = split[1].replace(/\s+/g, ""); + if(!isNaN( Number(v) )) { + _style[property] = Number(v); + } else { + _style[property] = v; + } + } + } + } + } + + public function getStyle(property:String):* { + return style[property]; + } + + public function setStyle(property:String, value:*):void { + style[property] = value; + } + + [Bindable(event="xChange", noEvent)] + override public function get x():Number { return super.x; } + override public function set x(value:Number):void { + notify("x", super.x, super.x = value); + } + + [Bindable(event="yChange", noEvent)] + override public function get y():Number { return super.y; } + override public function set y(value:Number):void { + notify("y", super.y, super.y = value); + } + + // IMeasurable implementation + + // these width/height setters need review in regards to scaling. + // I think I would perfer following Flex's lead here. + + /** + * @inheritDoc + */ + [PercentProxy("percentWidth")] + [Bindable(event="widthChange", noEvent)] + override public function get width():Number { return unscaledWidth; } + override public function set width(value:Number):void { + unscaledWidth = _explicitWidth = value; // this will dispatch for us if needed + // excluding super to avoid double event dispatch + } + + /** + * @inheritDoc + */ + [PercentProxy("percentHeight")] + [Bindable(event="heightChange", noEvent)] + override public function get height():Number { return unscaledHeight; } + override public function set height(value:Number):void { + unscaledHeight = _explicitHeight = value; // this will dispatch for us if needed (order is important) + // excluding super to avoid double event dispatch + } + + /** + * @inheritDoc + */ + //[Bindable(event="explicitChange", noEvent)] + public function get explicitWidth():Number { return _explicitWidth; } + public function get explicitHeight():Number { return _explicitHeight; } + + /** + * @inheritDoc + */ + //[Bindable(event="measuredChange", noEvent)] + public function get measuredWidth():Number { return _measuredWidth; } + public function get measuredHeight():Number { return _measuredHeight; } + + /** + * @inheritDoc + */ + [Bindable(event="percentWidthChange", noEvent)] + public function get percentWidth():Number { return _percentWidth; } + public function set percentWidth(value:Number):void { + notify("percentWidth", _percentWidth, _percentWidth = value); + } + + /** + * @inheritDoc + */ + [Bindable(event="percentHeightChange", noEvent)] + public function get percentHeight():Number { return _percentHeight; } + public function set percentHeight(value:Number):void { + notify("percentHeight", _percentHeight, _percentHeight = value); + } + + [Bindable(event="visibleChange")] + override public function get visible():Boolean { return super.visible; } + override public function set visible(value:Boolean):void { + notify("visible", super.visible, super.visible = value); + } + + /** + * @inheritDoc + */ + public function setSize(width:Number, height:Number):void { + if (unscaledWidth != width) { notify("width", unscaledWidth, unscaledWidth = width); } + if (unscaledHeight != height) { notify("height", unscaledHeight, unscaledHeight = height); } + } + + private function textChange(event:Event):void { + notify("text", null, super.text); + } + + private function onMeasure(event:Event):void { + if(isNaN(_explicitWidth)) { + if(type == TextFieldType.INPUT) { + _measuredWidth = textWidth + 30; + } else { + _measuredWidth = textWidth; + } + } + if(isNaN(_explicitHeight)) { + _measuredHeight = textHeight + 5; + } + } + + protected function notify(property:String, oldValue:*, newValue:*):void { + var force:Boolean = false; + var instance:IEventDispatcher = this; + if(oldValue != newValue || force) { + var eventType:String = property + "Change"; + if(instance is IEventDispatcher && (instance as IEventDispatcher).hasEventListener(eventType)) { + var event:DataChangeEvent = new DataChangeEvent(eventType, oldValue, newValue); + (instance as IEventDispatcher).dispatchEvent(event); + } + } + } + + } +} \ No newline at end of file diff --git a/src/reflex/tools/flash/ISWFPreloaderSkin.as b/src/reflex/tools/flash/ISWFPreloaderSkin.as deleted file mode 100644 index d48f65df..00000000 --- a/src/reflex/tools/flash/ISWFPreloaderSkin.as +++ /dev/null @@ -1,25 +0,0 @@ -package reflex.tools.flash -{ - import flash.display.Stage; - - public interface ISWFPreloaderSkin - { - function get height():Number; - function get width():Number; - function get x():Number; - function get y():Number; - - function set label(value:String):void; - function set total(value:Number):void; - function set progress(value:Number):void; - - function set height(value:Number):void; - function set width(value:Number):void; - function set x(value:Number):void; - function set y(value:Number):void; - - // Allow the skin to position himself relative to the stage. - function position(stage:Stage):void; - function validate():void; - } -} \ No newline at end of file diff --git a/src/reflex/tools/flash/ReflexFlashLoader.as b/src/reflex/tools/flash/ReflexFlashLoader.as deleted file mode 100644 index e8cefe50..00000000 --- a/src/reflex/tools/flash/ReflexFlashLoader.as +++ /dev/null @@ -1,170 +0,0 @@ -package reflex.tools.flash -{ - import flash.display.DisplayObjectContainer; - import flash.display.MovieClip; - import flash.display.Stage; - import flash.display.StageAlign; - import flash.display.StageQuality; - import flash.display.StageScaleMode; - import flash.events.Event; - import flash.utils.setTimeout; - - public class ReflexFlashLoader extends MovieClip - { - protected var preloader:SWFPreloader; - protected var preloaderSkin:Object; - protected var loadingComplete:Boolean = false; - - public function ReflexFlashLoader(root:MovieClip, useLoader:Boolean = true, loaderSkinOrClass:Object = null) - { - if(root.stage) - initStage(root.stage); - - if(loaderSkinOrClass == null) - loaderSkinOrClass = SWFPreloaderSkin; - - preloaderSkin = loaderSkinOrClass; - - // If this is a multi-frame movie, stop on frame 1 - // so we can show progress of RSLs and the rest of - // the movie. If it's a single frame movie, stopping - // here is no problem. - root.stop(); - - if(root.totalFrames > 1 && root && root.loaderInfo) - root.loaderInfo.addEventListener(Event.INIT, initHandler); - } - - /** - * Utility method to instantiate the loader in Flash - */ - public static function init(root:MovieClip, useLoader:Boolean = true, loaderSkinClass:Class = null):void - { - root.addChild(new ReflexFlashLoader(root, useLoader, loaderSkinClass)); - } - - /** - * Utility function to set usual stage properties. - */ - public static function initStage(stage:Stage):void - { - stage.frameRate = 30; - stage.quality = StageQuality.BEST; - stage.align = StageAlign.TOP_LEFT; - stage.scaleMode = StageScaleMode.NO_SCALE; - } - - override public function get stage():Stage - { - if(root === this) - return super.stage; - - return root.stage; - } - - override public function get currentFrame():int - { - if(root === this) - return super.currentFrame; - - return MovieClip(root).currentFrame; - } - - override public function get currentFrameLabel():String - { - if(root === this) - return super.currentFrameLabel; - - return MovieClip(root).currentFrameLabel; - } - - override public function get totalFrames():int - { - if(root === this) - return super.totalFrames; - - return MovieClip(root).totalFrames; - } - - override public function get framesLoaded():int - { - if(root === this) - return super.framesLoaded; - - return MovieClip(root).framesLoaded; - } - - override public function nextFrame():void - { - if(root === this) - return super.nextFrame(); - - MovieClip(root).nextFrame(); - } - - override public function stop():void - { - if(root === this) - return super.stop(); - - MovieClip(root).stop(); - } - - protected function initHandler(event:Event):void - { - width = stage.stageWidth; - height = stage.stageHeight; - - event.target.removeEventListener(event.type, initHandler); - addEventListener(Event.ENTER_FRAME, concurrentEnterFrameHandler); - - preloader = new SWFPreloader(); - preloader.addEventListener(Event.COMPLETE, preloaderCompleteHandler); - preloader.initialize(preloaderSkin); - - DisplayObjectContainer(root).addChild(preloader); - //Allow the preloader to position his skin based on the stage. - preloader.positionLoader(stage); - } - - protected function preloaderCompleteHandler(event:Event):void - { - preloader.removeEventListener(Event.COMPLETE, preloaderCompleteHandler); - loadingComplete = true; - - DisplayObjectContainer(root).removeChild(preloader); - - moveToNextFrame(); - } - - /** - * Moves to the next frame if the next frame is baked. If not, - * waits 100 milliseconds and tries again. - */ - protected function moveToNextFrame():void - { - if(currentFrame + 1 > totalFrames) - return; - - if(currentFrame + 1 <= framesLoaded) - { - nextFrame(); - } - else - setTimeout(moveToNextFrame, 100); - } - - protected function concurrentEnterFrameHandler(event:Event):void - { - if(currentFrame >= 2 && loadingComplete) - { - removeEventListener(event.type, concurrentEnterFrameHandler); - initializeApplication(); - } - } - - protected function initializeApplication():void - { - } - } -} \ No newline at end of file diff --git a/src/reflex/tools/flash/SWFPreloader.as b/src/reflex/tools/flash/SWFPreloader.as deleted file mode 100644 index ab59f99b..00000000 --- a/src/reflex/tools/flash/SWFPreloader.as +++ /dev/null @@ -1,246 +0,0 @@ -package reflex.tools.flash -{ - import flash.display.DisplayObject; - import flash.display.LoaderInfo; - import flash.display.MovieClip; - import flash.display.Sprite; - import flash.display.Stage; - import flash.events.ErrorEvent; - import flash.events.Event; - import flash.events.ProgressEvent; - import flash.events.TimerEvent; - import flash.utils.Timer; - - import mx.core.RSLItem; - import mx.core.RSLListLoader; - import mx.events.RSLEvent; - - [Event(name="complete", type="Event")] - public class SWFPreloader extends Sprite - { - protected var timer:Timer; - protected var rslList:RSLListLoader; - protected var rslDone:Boolean = false; - protected var targetsLoaded:Boolean = false; - protected var skin:DisplayObject; - - public function SWFPreloader() - { - super(); - } - - public function initialize(skinValue:Object = null, targets:Array = null):void - { - initSkin(skinValue); - initTarget(targets); - } - - /** - * Positions the skin relative to the Stage. If the skin is an ISWFPreloaderSkin, - * allow him to position himself. If not, set the x and y of the - * ApplicationLoader to the center of the screen - */ - public function positionLoader(stage:Stage):void - { - if(skin is ISWFPreloaderSkin) - { - ISWFPreloaderSkin(skin).position(stage); - } - else - { - x = (stage.stageWidth - width) / 2; - y = (stage.stageHeight - height) / 2; - } - } - - protected function initTarget(targets:Array):void - { - if(targets && targets.length > 0) - { - rslDone = false; - rslList = new RSLListLoader(targets); - rslList.load(rslProgressHandler, rslCompleteHandler, rslErrorHandler, rslErrorHandler, rslErrorHandler); - } - else - rslDone = true; - - if(!timer) - { - timer = new Timer(10); - timer.addEventListener(TimerEvent.TIMER, checkProgressHandler); - timer.start(); - } - } - - /** - * Initializes an IApplicationLoaderSkin instance passed in from the - * ApplicationManager's info() method. If no instance is passed, uses - * the default ApplicationLoaderSkin. - * - * @param clazz Class that implements IApplicationLoaderSkin - */ - protected function initSkin(skinValue:Object = null):void - { - // Don't initialize the skin twice. - if(skin) - return; - - if(skinValue == null) - skinValue = SWFPreloaderSkin; - - if(skinValue is Class) - skin = new (skinValue as Class)(); - else if(skinValue is DisplayObject) - skin = skinValue as DisplayObject; - else - throw new ArgumentError("The skin for a SWFLoader must be a DisplayObject."); - - addChild(DisplayObject(skin)); - } - - /** - * Checks the loading progress of the SWF and RSLs every 10 milliseconds. - * Once all loading has completed, waits another 10 milliseconds to ensure - * the classes are completely loaded into this ApplicationDomain and the - * next frame is entirely baked. - * - * @param event - */ - protected function checkProgressHandler(event:TimerEvent):void - { - var bytes:Object = getByteValues(); - if(skin is ISWFPreloaderSkin) - { - ISWFPreloaderSkin(skin).progress = bytes.progress; - ISWFPreloaderSkin(skin).total = bytes.total; - ISWFPreloaderSkin(skin).validate(); - } - else if(skin is MovieClip) - { - if(MovieClip(skin).totalFrames > 1) - MovieClip(skin).gotoAndStop(Math.round((bytes.progress / bytes.total) * 100)); - else - MovieClip(skin).play(); - } - - if(rslDone && bytes.progress >= bytes.total) - { - if(targetsLoaded) - { - // Clean up - timer.stop(); - timer.removeEventListener(TimerEvent.TIMER, checkProgressHandler); - timer = null; - removeChild(DisplayObject(skin)); - targetsLoaded = false; - // Complete - dispatchEvent(new Event(Event.COMPLETE)); - } - // Wait 10 milliseconds after the loading targets are finished - // to dispatch the COMPLETE event. This ensures the classes are - // completely loaded into this ApplicationDomain, and the next - // frame is entirely baked. - else - { - targetsLoaded = true; - } - } - } - - protected function getByteValues():Object - { - var li:LoaderInfo = root.loaderInfo; - - var loaded:Number = li.bytesLoaded; - var total:Number = li.bytesTotal; - - if(rslList) - { - var i:int = 0; - var n:int = rslList.getItemCount(); - var target:Object; - for(; i < n; i++) - { - target = rslList.getItem(i); - if(target.hasOwnProperty("bytesLoaded")) - loaded += target.bytesLoaded; - else if(target.hasOwnProperty("loaded")) - loaded += target.loaded; - if(target.hasOwnProperty("bytesTotal")) - total += target.bytesTotal; - else if(target.hasOwnProperty("total")) - total += target.total; - } - } - - return{progress:loaded, total:total}; - } - - ////// - // RSL callbacks - ////// - /** - * @private - * We don't listen for the events directly - * because we don't know which RSL is sending the event. - * So we have the RSLNode listen to the events - * and then pass them along to the Preloader. - */ - protected function rslProgressHandler(event:ProgressEvent):void - { - var index:int = rslList.getIndex(); - var item:RSLItem = rslList.getItem(index); - - var rslEvent:RSLEvent = new RSLEvent(RSLEvent.RSL_PROGRESS); - rslEvent.isResourceModule = false; - rslEvent.bytesLoaded = event.bytesLoaded; - rslEvent.bytesTotal = event.bytesTotal; - rslEvent.rslIndex = index; - rslEvent.rslTotal = rslList.getItemCount(); - rslEvent.url = item.urlRequest; - dispatchEvent(rslEvent); - } - - /** - * @private - * Load the next RSL in the list and dispatch an event. - */ - protected function rslCompleteHandler(event:Event):void - { - var index:int = rslList.getIndex(); - var item:RSLItem = rslList.getItem(index); - - var rslEvent:RSLEvent = new RSLEvent(RSLEvent.RSL_COMPLETE); - rslEvent.isResourceModule = false; - rslEvent.bytesLoaded = item.total; - rslEvent.bytesTotal = item.total; - rslEvent.loaderInfo = event.target as LoaderInfo; - rslEvent.rslIndex = index; - rslEvent.rslTotal = rslList.getItemCount(); - rslEvent.url = item.urlRequest; - dispatchEvent(rslEvent); - - rslDone = Boolean(index + 1 == rslEvent.rslTotal); - } - - /** - * @private - */ - protected function rslErrorHandler(event:ErrorEvent):void - { - // send an error event - var index:int = rslList.getIndex(); - var item:RSLItem = rslList.getItem(index); - var rslEvent:RSLEvent = new RSLEvent(RSLEvent.RSL_ERROR); - - rslEvent.isResourceModule = false; - rslEvent.bytesLoaded = 0; - rslEvent.bytesTotal = 0; - rslEvent.rslIndex = index; - rslEvent.rslTotal = rslList.getItemCount(); - rslEvent.url = item.urlRequest; - rslEvent.errorText = decodeURI(event.text); - dispatchEvent(rslEvent); - } - } -} \ No newline at end of file diff --git a/src/reflex/tools/flash/SWFPreloaderSkin.as b/src/reflex/tools/flash/SWFPreloaderSkin.as deleted file mode 100644 index 6ba3292c..00000000 --- a/src/reflex/tools/flash/SWFPreloaderSkin.as +++ /dev/null @@ -1,103 +0,0 @@ -package reflex.tools.flash -{ - import flash.display.Graphics; - import flash.display.Sprite; - import flash.display.Stage; - import flash.text.TextField; - import flash.text.TextFieldAutoSize; - import flash.text.TextLineMetrics; - - public class SWFPreloaderSkin extends Sprite implements ISWFPreloaderSkin - { - protected var _progress:Number = 0; - protected var _total:Number = 1; - protected var _label:String = ""; - protected var _text:TextField; - - public function SWFPreloaderSkin() - { - super(); - - width = 210; - height = 35; - - _text = new TextField(); - _text.autoSize = TextFieldAutoSize.LEFT; - _text.text = "Loading" - addChild(_text); - } - - private var _width:Number = 0; - override public function get width():Number - { - return _width; - } - override public function set width(value:Number):void - { - _width = value; - } - - private var _height:Number = 0; - override public function get height():Number - { - return _height; - } - override public function set height(value:Number):void - { - _height = value; - } - - public function set progress(value:Number):void - { - _progress = value; - } - - public function set total(value:Number):void - { - _total = value; - } - - public function set label(value:String):void - { - _label = value; - } - - protected function get ratio():Number - { - return _progress/_total; - } - - /** - * Position the skin relative to the stage. Sets the skin - * to the center of the stage. Can be overridden to set him - * anywhere. - */ - public function position(stage:Stage):void - { - x = (stage.stageWidth - width) / 2; - y = (stage.stageHeight - height) / 2; - } - - public function validate():void - { - - var g:Graphics = graphics; - g.clear(); - - //Draw the background - g.lineStyle(1, 0x000000); - g.beginFill(0x999999, 1); - g.drawRect(0, 0, width, height); - - //Draw the progress bar - g.lineStyle(0, 0x000000, 0); - g.beginFill(0x000000, 1); - g.drawRect(5, 15, ratio * width - 10, 5); - - var tlm:TextLineMetrics = _text.getLineMetrics(0); - _text.text = _label; - _text.x = (width/tlm.width) / 2; - _text.y = 21; - } - } -} \ No newline at end of file diff --git a/src/reflex/tools/flashbuilder/ReflexApplicationLoader.as b/src/reflex/tools/flashbuilder/ReflexApplicationLoader.as deleted file mode 100644 index 38aa6d46..00000000 --- a/src/reflex/tools/flashbuilder/ReflexApplicationLoader.as +++ /dev/null @@ -1,149 +0,0 @@ -package reflex.tools.flashbuilder -{ - import flash.display.DisplayObject; - import flash.display.DisplayObjectContainer; - import flash.display.Graphics; - import flash.display.Loader; - import flash.display.MovieClip; - import flash.display.Stage; - import flash.display.StageAlign; - import flash.display.StageQuality; - import flash.display.StageScaleMode; - import flash.events.Event; - import flash.net.URLLoader; - import flash.net.URLLoaderDataFormat; - import flash.net.URLRequest; - import flash.utils.Dictionary; - import flash.utils.setTimeout; - - import mx.core.CrossDomainRSLItem; - import mx.core.IFlexModuleFactory; - import mx.core.RSLItem; - import mx.utils.LoaderUtil; - - import reflex.tools.flash.ReflexFlashLoader; - import reflex.tools.flash.SWFPreloader; - - // TODO: resolve error in Flex4: 1144: Interface method callInContext in namespace mx.core:IFlexModuleFactory is implemented with an incompatible signature in class reflex.tools.flashbuilder:ReflexApplicationLoader. - public class ReflexApplicationLoader extends ReflexFlashLoader// implements IFlexModuleFactory - { - public function ReflexApplicationLoader() - { - super(this, true); - } - - override protected function initHandler(event:Event):void - { - super.initHandler(event); - - var allRSLs:Array = []; - var info:Object = info(); - if(info) - { - //Load cdRSLs first then load RSLs - if(info["cdRsls"]) - allRSLs = allRSLs.concat(initRSLS(info["cdRsls"])); - if(info["rsls"]) - allRSLs = allRSLs.concat(initRSLS(info["rsls"])); - } - - preloader.initialize(null, allRSLs); - } - - /** - * Utility function to create RSLItem or CrossDomainRSLItem objects - * out of the objects the FB compiler generates. - * - * @return Array of CrossDomainRSLItem or RSLItem objects. - */ - protected function initRSLS(rsls:Object):Array - { - if(rsls == null) - return[]; - - var a:Array = [], r:RSLItem; - for each(var rsl:Object in rsls) - { - if(rsl.hasOwnProperty("url")) - { - r = new RSLItem(rsl.url, LoaderUtil.normalizeURL(this.loaderInfo)); - } - else if(rsl.hasOwnProperty("rsls")) - { - r = new CrossDomainRSLItem(rsl["rsls"], - rsl["policyFiles"], - rsl["digests"], - rsl["types"], - rsl["isSigned"], - LoaderUtil.normalizeURL(this.loaderInfo)); - } - - a.push(r); - } - - return a; - } - - override protected function initializeApplication():void - { - // Caste with 'as' because create() can possibly return 'null', - // and casting 'null' to a DisplayObject will throw an error - var app:DisplayObject = create() as DisplayObject; - - // If there's no app, this is probably an app made in Flash Pro, - // in which case moving to the second frame is sufficient action - // on our part. - if(!app) - { - return; - } - - //The Application should listen for its own added_to_stage - //event and initialize itself from that. - addChild(app); - - super.initializeApplication(); - - width = app.width = stage.stageWidth; - height = app.height = stage.stageHeight; - } - - public function get preloadedRSLs():Dictionary - { - return null; - } - - public function allowDomain(... parameters):void - { - } - - public function allowInsecureDomain(... parameters):void - { - } - - //callInContext(fn:Function, thisArg:*, argArray:*, returns:Boolean=true):*; - public function callInContext(fn:Function, thisArg:Object, argArray:Array, returns:Boolean = true):* - { - return null; - } - - public function create(... parameters):Object - { - return null; - } - - public function getImplementation(interfaceName:String):Object - { - return null; - } - - public function info():Object - { - return null; - } - - public function registerImplementation(interfaceName:String, impl:Object):void - { - } - } -} \ No newline at end of file diff --git a/src/spark/layouts/supportClasses/LayoutBase.as b/src/spark/layouts/supportClasses/LayoutBase.as new file mode 100644 index 00000000..0acc0b5b --- /dev/null +++ b/src/spark/layouts/supportClasses/LayoutBase.as @@ -0,0 +1,9 @@ +package spark.layouts.supportClasses +{ + public class LayoutBase + { + public function LayoutBase() + { + } + } +} \ No newline at end of file diff --git a/src/spark/skins/IHighlightBitmapCaptureClient.as b/src/spark/skins/IHighlightBitmapCaptureClient.as new file mode 100644 index 00000000..09c79d5e --- /dev/null +++ b/src/spark/skins/IHighlightBitmapCaptureClient.as @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ADOBE SYSTEMS INCORPORATED +// Copyright 2010 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +package spark.skins +{ + + +/** + * The IHighlightBitmapCaptureClient defines the interface for skins that support + * highlight bitmap capture. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ +public interface IHighlightBitmapCaptureClient +{ + /** + * Called before a bitmap capture is made for this skin. + * + * Return true if the skin needs to be updated before the bitmap is captured. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + function beginHighlightBitmapCapture():Boolean; + + /** + * Called after a bitmap capture is made for this skin. + * + * Return true if the skin needs to be updated. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + function endHighlightBitmapCapture():Boolean; + + /** + * Validate the skin. + * + * @langversion 3.0 + * @playerversion Flash 10 + * @playerversion AIR 2.5 + * @productversion Flex 4.5 + */ + function validateNow():void; +} +} \ No newline at end of file diff --git a/test/ReflexSuite.as b/test/ReflexSuite.as deleted file mode 100644 index ea4cdf8b..00000000 --- a/test/ReflexSuite.as +++ /dev/null @@ -1,21 +0,0 @@ -package -{ - import reflex.behaviors.CompositeBehaviorTest; - import reflex.behaviors.SelectableBehaviorTest; - import reflex.display.ReflexDisplayTest; - - //import reflex.utils.MetaInjectorTest; - //import reflex.utils.MetaUtilTest; - - - [Suite] - [RunWith("org.flexunit.runners.Suite")] - public class ReflexSuite - { - - public var display:ReflexDisplayTest; - public var behaviors:CompositeBehaviorTest; - public var selectabe:SelectableBehaviorTest; - - } -} \ No newline at end of file diff --git a/test/reflex/behaviors/MockBehavior.as b/test/reflex/behaviors/MockBehavior.as deleted file mode 100644 index 14546d11..00000000 --- a/test/reflex/behaviors/MockBehavior.as +++ /dev/null @@ -1,15 +0,0 @@ -package reflex.behaviors -{ - //import reflex.core.IBehavior; - - public class MockBehavior //implements IBehavior - { - - private var _target:Object; - public function get target():Object { return _target; } - public function set target(value:Object):void { - _target = value; - } - - } -} \ No newline at end of file diff --git a/test/reflex/behaviors/SelectableBehaviorTest.as b/test/reflex/behaviors/SelectableBehaviorTest.as deleted file mode 100644 index 3ba91399..00000000 --- a/test/reflex/behaviors/SelectableBehaviorTest.as +++ /dev/null @@ -1,20 +0,0 @@ -package reflex.behaviors -{ - import org.flexunit.Assert; - import flash.display.Sprite; - import flash.events.MouseEvent; - - public class SelectableBehaviorTest - { - - [Test] - public function testClick():void { - var sprite:Sprite = new Sprite(); - var selectable:SelectableBehavior = new SelectableBehavior(sprite); - selectable.selected = false; - sprite.dispatchEvent(new MouseEvent(MouseEvent.CLICK)); - Assert.assertTrue(selectable.selected); - } - - } -} \ No newline at end of file diff --git a/test/reflex/display/ReflexDisplayTest.as b/test/reflex/display/ReflexDisplayTest.as deleted file mode 100644 index c3d358b2..00000000 --- a/test/reflex/display/ReflexDisplayTest.as +++ /dev/null @@ -1,50 +0,0 @@ -package reflex.display -{ - import flash.events.Event; - import org.flexunit.Assert; - import org.flexunit.async.Async; - - public class ReflexDisplayTest - { - - [Test(async)] - public function testXChange():void { - var listener:Function = Async.asyncHandler(this, changeHandler, 500, "xChange", timeoutHandler); - var display:ReflexDisplay = new ReflexDisplay(); - display.addEventListener("xChange", listener, false, 0, false); - display.x += 100; - } - - [Test(async)] - public function testYChange():void { - var listener:Function = Async.asyncHandler(this, changeHandler, 500, "yChange", timeoutHandler); - var display:ReflexDisplay = new ReflexDisplay(); - display.addEventListener("yChange", listener, false, 0, false); - display.y += 100; - } - - [Test(async)] - public function testWidthChange():void { - var listener:Function = Async.asyncHandler(this, changeHandler, 500, "widthChange", timeoutHandler); - var display:ReflexDisplay = new ReflexDisplay(); - display.addEventListener("widthChange", listener, false, 0, false); - display.width += 100; - } - - [Test(async)] - public function testHeightChange():void { - var listener:Function = Async.asyncHandler(this, changeHandler, 500, "heightChange", timeoutHandler); - var display:ReflexDisplay = new ReflexDisplay(); - display.addEventListener("heightChange", listener, false, 0, false); - display.height += 100; - } - - private function changeHandler(event:Event, type:String):void { - Assert.assertEquals(event.type, type); - } - - private function timeoutHandler(type:String):void { - Assert.fail(type + ": timed out."); - } - } -} \ No newline at end of file diff --git a/tests/ReflexSuite.as b/tests/ReflexSuite.as new file mode 100644 index 00000000..fc7da700 --- /dev/null +++ b/tests/ReflexSuite.as @@ -0,0 +1,123 @@ +package +{ + import flash.display.Stage; + + import mx.states.AddItemsTest; + import mx.states.OverrideBaseTest; + + import reflex.behaviors.BehaviorTest; + import reflex.behaviors.ButtonBehaviorTest; + import reflex.behaviors.CompositeBehaviorTest; + import reflex.behaviors.SelectBehaviorTest; + import reflex.behaviors.SlideBehaviorTest; + import reflex.behaviors.StepBehaviorTest; + import reflex.collections.SimpleCollectionTest; + import reflex.components.ButtonTest; + import reflex.components.ComponentTest; + import reflex.components.ListItemTest; + import reflex.components.ListTest; + import reflex.components.ScrollerDefinitionTest; + import reflex.components.SliderDefinitionTest; + import reflex.containers.ContainerLifecycleTest; + import reflex.containers.ContainerMeasurementTest; + import reflex.containers.ContainerTest; + import reflex.data.PositionTest; + import reflex.data.RangeTest; + import reflex.data.ScrollPositionTest; + import reflex.data.SelectionTest; + import reflex.display.BitmapDisplayMeasurableTest; + import reflex.display.DisplayFunctionsTest; + import reflex.display.DisplayMeasurableTest; + import reflex.display.DisplayStyleableTest; + import reflex.display.DisplayTest; + import reflex.display.GroupTest; + import reflex.display.TextFieldDisplayMeasurableTest; + import reflex.graphics.GraphicBaseMeasurementTest; + import reflex.layouts.BasicLayoutTest; + import reflex.layouts.HorizontalLayoutTest; + import reflex.layouts.VerticalLayoutTest; + import reflex.layouts.XYLayoutTest; + import reflex.measurement.MeasurementFunctionsTest; + import reflex.skins.SkinContainerTest; + import reflex.skins.SkinMeasurementTest; + import reflex.styles.StyleFunctionsTest; + import reflex.text.LabelTest; + + [Suite] + [RunWith("org.flexunit.runners.Suite")] + public class ReflexSuite + { + + static public var stage:Stage; + + // components + public var component:ComponentTest; + //public var application:ApplicationTest; + public var button:ButtonTest; + public var listItem:ListItemTest; + public var list:ListTest; + public var scrollerDefinition:ScrollerDefinitionTest; + public var sliderDefinition:SliderDefinitionTest; + + // behaviors + public var behavior:BehaviorTest; + public var compositeBehavior:CompositeBehaviorTest; + public var buttonBehavior:ButtonBehaviorTest; + public var selectabeBehavior:SelectBehaviorTest; + //public var slideBehavior:SlideBehaviorTest; + public var stepBehavior:StepBehaviorTest; + + // containers + public var container:GroupTest; + public var skinContainer:SkinContainerTest; + public var displayFunctions:DisplayFunctionsTest; + public var containerMeasurement:ContainerMeasurementTest; + public var containerTest:ContainerTest; + + // data + public var position:PositionTest; + public var range:RangeTest; + public var scrollPosition:ScrollPositionTest; + public var selection:SelectionTest; + + // display + public var display:DisplayTest; + + // styling + public var styleableSprite:DisplayStyleableTest; + public var styleFunctions:StyleFunctionsTest; + + // measurement + public var measurementFunctions:MeasurementFunctionsTest; + public var skinMeasurement:SkinMeasurementTest; + public var measuredBitmap:BitmapDisplayMeasurableTest; + public var measuredTextField:TextFieldDisplayMeasurableTest; + public var measuredSprite:DisplayMeasurableTest; + + // layouts + public var xyLayout:XYLayoutTest; + public var basicLayout:BasicLayoutTest; + public var verticalLayout:VerticalLayoutTest; + public var horizontalLayout:HorizontalLayoutTest; + + // collections + public var collection:SimpleCollectionTest; + + //templating + //public var addItemAt:addItemAtTest; + + // states + public var addItems:AddItemsTest; + //public var overrideBase:OverrideBaseTest; // no tests yet + + //graphics + public var graphicMeasurement:GraphicBaseMeasurementTest; + + //text + public var label:LabelTest; + + //lifecycle + public var containerLifecycle:ContainerLifecycleTest; + + } +} \ No newline at end of file diff --git a/tests/mx/states/AddItemsTest.as b/tests/mx/states/AddItemsTest.as new file mode 100644 index 00000000..4fe4ff4d --- /dev/null +++ b/tests/mx/states/AddItemsTest.as @@ -0,0 +1,91 @@ +package mx.states +{ + import flash.display.Shape; + import flash.display.Sprite; + + import mx.core.DeferredInstanceFromClass; + import mx.core.DeferredInstanceFromFunction; + + import org.flexunit.Assert; + + import reflex.collections.SimpleCollection; + + public class AddItemsTest + { + + // need tests for applying states to Arrays and generic Objects / DisplayObjects + /* + [Test] + public function testAddItemsFirst():void + { + var override:AddItems = new AddItems(); + override.propertyName = "content"; + override.position = "first"; + + var sprite:Sprite = new Sprite(); + override.itemsFactory = new DeferredInstanceFromFunction(function():* { return sprite; }, null); + + var children:Array = [new Shape(), new Shape()]; + var container:Object = {content: new SimpleCollection(children)}; // tricky mocking + override.apply(container); + Assert.assertEquals(sprite, container.content.getItemAt(0)); + } + + [Test] + public function testAddItemsLast():void + { + var override:AddItems = new AddItems(); + override.propertyName = "content"; + override.position = "last"; + + var sprite:Sprite = new Sprite(); + override.itemsFactory = new DeferredInstanceFromFunction(function():* { return sprite; }, null); + + var children:Array = [new Shape(), new Shape()]; + //override.relativeTo = children.concat(); + + var container:Object = {content: new SimpleCollection(children)}; // tricky mocking + override.apply(container); + Assert.assertEquals(sprite, container.content.getItemAt(2)); + } + + [Test] + public function testAddItemsBefore():void + { + var override:AddItems = new AddItems(); + override.propertyName = "content"; + override.position = "before"; + + var sprite:Sprite = new Sprite(); + override.itemsFactory = new DeferredInstanceFromFunction(function():* { return sprite; }, null); + + var children:Array = [new Shape(), new Shape()]; + override.relativeTo = [children[1]]; + + var container:Object = {content: new SimpleCollection(children)}; // tricky mocking + override.apply(container); + Assert.assertEquals(sprite, container.content.getItemAt(1)); + } + */ + /* + [Test] + public function testAddItemsAfter():void + { + var override:AddItems = new AddItems(); + override.propertyName = "content"; + override.position = "after"; + + var sprite:Sprite = new Sprite(); + override.itemsFactory = new DeferredInstanceFromFunction(function():* { return sprite; }, null); + + var children:Array = [new Shape(), new Shape()]; + override.relativeTo = [children[0]]; + + var container:Object = {content: new SimpleCollection(children)}; // tricky mocking + override.apply(container); + Assert.assertEquals(sprite, container.content.getItemAt(1)); + } + */ + + } +} \ No newline at end of file diff --git a/tests/mx/states/OverrideBaseTest.as b/tests/mx/states/OverrideBaseTest.as new file mode 100644 index 00000000..9d8ee517 --- /dev/null +++ b/tests/mx/states/OverrideBaseTest.as @@ -0,0 +1,13 @@ +package mx.states +{ + public class OverrideBaseTest + { + + [Test] + public function testOverrideContext():void + { + + } + + } +} \ No newline at end of file diff --git a/tests/reflex/behaviors/BehaviorTest.as b/tests/reflex/behaviors/BehaviorTest.as new file mode 100644 index 00000000..d4e58acc --- /dev/null +++ b/tests/reflex/behaviors/BehaviorTest.as @@ -0,0 +1,26 @@ +package reflex.behaviors { + import flexunit.framework.Assert; + + import reflex.tests.BaseClass; + + public class BehaviorTest extends BaseClass { + + public var C:Class = Behavior; + + [Test] + public function testConstructor():void { + var behavior:Behavior = new Behavior(); + Assert.assertNull(behavior.target); + } + + [Test(async)] + public function testTargetChange():void { + testPropertyChange(C, "target", new MockEventDispatcher()); + } + + [Test(async)] + public function testTargetNotChanged():void { + testPropertyNotChanged(C, "target", new MockEventDispatcher()); + } + } +} \ No newline at end of file diff --git a/tests/reflex/behaviors/ButtonBehaviorTest.as b/tests/reflex/behaviors/ButtonBehaviorTest.as new file mode 100644 index 00000000..b7a8dc22 --- /dev/null +++ b/tests/reflex/behaviors/ButtonBehaviorTest.as @@ -0,0 +1,40 @@ +package reflex.behaviors +{ + import flash.display.Stage; + import flash.events.EventDispatcher; + import flash.events.MouseEvent; + + import org.flexunit.asserts.assertEquals; + + import reflex.containers.Container; + + public class ButtonBehaviorTest extends EventDispatcher + { + + [Bindable] + public var enabled:Boolean = true; + + + [Test] + public function testButtonStateUp():void { + var behavior:ButtonBehavior = new ButtonBehavior(this); + dispatchEvent(new MouseEvent(MouseEvent.ROLL_OUT)); + assertEquals("up", behavior.currentState); + } + + [Test] + public function testButtonStateOver():void { + var behavior:ButtonBehavior = new ButtonBehavior(this); + dispatchEvent(new MouseEvent(MouseEvent.ROLL_OVER)); + assertEquals("over", behavior.currentState); + } + + [Test] + public function testButtonStateDown():void { + var behavior:ButtonBehavior = new ButtonBehavior(this); + dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN)); + assertEquals("down", behavior.currentState); + } + + } +} \ No newline at end of file diff --git a/test/reflex/behaviors/CompositeBehaviorTest.as b/tests/reflex/behaviors/CompositeBehaviorTest.as similarity index 84% rename from test/reflex/behaviors/CompositeBehaviorTest.as rename to tests/reflex/behaviors/CompositeBehaviorTest.as index 1736f20f..9f8bc71a 100644 --- a/test/reflex/behaviors/CompositeBehaviorTest.as +++ b/tests/reflex/behaviors/CompositeBehaviorTest.as @@ -8,7 +8,7 @@ package reflex.behaviors public class CompositeBehaviorTest extends EventDispatcher { - + /* private var behaviors:CompositeBehavior; [Before] @@ -44,7 +44,8 @@ package reflex.behaviors [Test] public function testForLoop():void { var items:Array = [new MockBehavior(), new MockBehavior(), new MockBehavior()]; - behaviors.push(items[0], items[1], items[2]); + behaviors.add([items[0], items[1], items[2]]); + Assert.assertEquals(3, behaviors.length); for(var i:int = 0; i < behaviors.length; i++) { Assert.assertEquals(items[i], behaviors[i]) } @@ -55,7 +56,8 @@ package reflex.behaviors var items:Array = [new MockBehavior(), new MockBehavior(), new MockBehavior()]; var index:int = 0; - behaviors.push(items[0], items[1], items[2]); + behaviors.add([items[0], items[1], items[2]]); + Assert.assertEquals(3, behaviors.length); for each(var behavior:Object in behaviors) { Assert.assertEquals(items[index], behavior); index++; @@ -67,12 +69,13 @@ package reflex.behaviors var items:Array = [new MockBehavior(), new MockBehavior(), new MockBehavior()]; var index:int = 0; - behaviors.push(items[0], items[1], items[2]); + behaviors.add([items[0], items[1], items[2]]); + Assert.assertEquals(3, behaviors.length); for(var key:String in behaviors) { Assert.assertEquals(items[index], behaviors[key]); index++; } } - + */ } } \ No newline at end of file diff --git a/tests/reflex/behaviors/MockBehavior.as b/tests/reflex/behaviors/MockBehavior.as new file mode 100644 index 00000000..76c89c2c --- /dev/null +++ b/tests/reflex/behaviors/MockBehavior.as @@ -0,0 +1,15 @@ +package reflex.behaviors +{ + import flash.events.IEventDispatcher; + + public class MockBehavior implements IBehavior + { + + private var _target:IEventDispatcher; + public function get target():IEventDispatcher { return _target; } + public function set target(value:IEventDispatcher):void { + _target = value; + } + + } +} \ No newline at end of file diff --git a/tests/reflex/behaviors/MockEventDispatcher.as b/tests/reflex/behaviors/MockEventDispatcher.as new file mode 100644 index 00000000..dca3a65a --- /dev/null +++ b/tests/reflex/behaviors/MockEventDispatcher.as @@ -0,0 +1,28 @@ +package reflex.behaviors { + import flash.events.Event; + import flash.events.IEventDispatcher; + + public class MockEventDispatcher implements IEventDispatcher { + + public function MockEventDispatcher() { + } + + public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void { + } + + public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void { + } + + public function dispatchEvent(event:Event):Boolean { + return false; + } + + public function hasEventListener(type:String):Boolean { + return false; + } + + public function willTrigger(type:String):Boolean { + return false; + } + } +} \ No newline at end of file diff --git a/tests/reflex/behaviors/MockSlider.as b/tests/reflex/behaviors/MockSlider.as new file mode 100644 index 00000000..665718cb --- /dev/null +++ b/tests/reflex/behaviors/MockSlider.as @@ -0,0 +1,51 @@ +package reflex.behaviors +{ + import flash.display.Shape; + import flash.display.Sprite; + + public class MockSlider extends Sprite + { + + [Bindable] + public var thumb:Shape; + + [Bindable] + public var track:Shape; + + [Bindable] + public var incrementButton:Shape; + + [Bindable] + public var decrementButton:Shape; + + public function MockSlider() + { + super(); + + track = new Shape(); + track.graphics.beginFill(0x000000, 1); + track.graphics.drawRect(0, 0, 100, 20); + track.graphics.endFill(); + addChild(track); + + thumb = new Shape(); + thumb.graphics.beginFill(0xFFFFFF, 1); + thumb.graphics.drawRect(0, 0, 10, 20); + thumb.graphics.endFill(); + addChild(thumb); + + decrementButton = new Shape(); + decrementButton.graphics.beginFill(0xFFFFFF, 1); + decrementButton.graphics.drawRect(0, 0, 20, 20); + decrementButton.graphics.endFill(); + addChild(decrementButton); + + incrementButton = new Shape(); + incrementButton.graphics.beginFill(0xFFFFFF, 1); + incrementButton.graphics.drawRect(0, 0, 20, 20); + incrementButton.graphics.endFill(); + addChild(incrementButton); + + } + } +} \ No newline at end of file diff --git a/tests/reflex/behaviors/SelectBehaviorTest.as b/tests/reflex/behaviors/SelectBehaviorTest.as new file mode 100644 index 00000000..c1407104 --- /dev/null +++ b/tests/reflex/behaviors/SelectBehaviorTest.as @@ -0,0 +1,20 @@ +package reflex.behaviors +{ + import flash.display.Sprite; + import flash.events.EventDispatcher; + import flash.events.MouseEvent; + + import org.flexunit.Assert; + + public class SelectBehaviorTest extends EventDispatcher + { + + [Test] + public function testClick():void { + var behavior:SelectBehavior = new SelectBehavior(this); + dispatchEvent(new MouseEvent(MouseEvent.CLICK)); + Assert.assertTrue(behavior.selected); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/behaviors/SlideBehaviorTest.as b/tests/reflex/behaviors/SlideBehaviorTest.as new file mode 100644 index 00000000..a67cd28a --- /dev/null +++ b/tests/reflex/behaviors/SlideBehaviorTest.as @@ -0,0 +1,83 @@ +package reflex.behaviors +{ + import flash.display.InteractiveObject; + import flash.display.Shape; + import flash.display.Sprite; + import flash.display.Stage; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + import flash.geom.Point; + + import org.flexunit.asserts.assertEquals; + + import reflex.data.IPosition; + import reflex.data.Position; + import reflex.data.ScrollPosition; + + [Ignore("incomplete")] + public class SlideBehaviorTest extends Shape + { + + //public var stage:Stage; + + [Bindable] + public var skin:MockSlider; + + [Before] + public function setup():void { + skin = new MockSlider(); + //stage.addChild(skin); + } + + [After] + public function destroy():void { + //stage.removeChild(skin); + skin = null; + } + + /* + + // how do we test items based on mouseX/mouseY? + + [Test] + public function testTrackClick():void { // default behavior (for sliders) should jump to position + var behavior:SlideBehavior = new SlideBehavior(this); + var position:IPosition = behavior.position = new Position(0, 100, 0); + var event:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, 70, 0, null, false, false, false, true, 0); + var dispatcher:IEventDispatcher = skin.track; + dispatcher.dispatchEvent(event); + assertEquals(70, position.value); + } + + [Test] + public function testScrollTrackDownClick():void { // default behavior (for sliders) should jump to position + var behavior:SlideBehavior = new SlideBehavior(this, SlideBehavior.HORIZONTAL, true); + var position:IPosition = behavior.position = new ScrollPosition(0, 100, 0, 10); + var event:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, 100, 0, null, false, false, false, true, 0); + var dispatcher:IEventDispatcher = skin.track; + dispatcher.dispatchEvent(event); + assertEquals(10, position.value); + } + + + [Test] + public function testScrollTrackUpClick():void { // default behavior (for sliders) should jump to position + var behavior:SlideBehavior = new SlideBehavior(this, SlideBehavior.HORIZONTAL, true); + var position:IPosition = behavior.position = new ScrollPosition(0, 100, 100,1, 10); + var event:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, 0, 0, null, false, false, false, true, 0); + var dispatcher:IEventDispatcher = skin.track; + dispatcher.dispatchEvent(event); + assertEquals(90, position.value); + } + */ + /* + [Test] + public function testThumbScroll():void { // not sure how to best do this + + } + */ + + } +} \ No newline at end of file diff --git a/tests/reflex/behaviors/StepBehaviorTest.as b/tests/reflex/behaviors/StepBehaviorTest.as new file mode 100644 index 00000000..0f538652 --- /dev/null +++ b/tests/reflex/behaviors/StepBehaviorTest.as @@ -0,0 +1,61 @@ +package reflex.behaviors +{ + import flash.display.InteractiveObject; + import flash.display.Shape; + import flash.display.Sprite; + import flash.display.Stage; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + import flash.events.MouseEvent; + import flash.geom.Point; + + import org.flexunit.asserts.assertEquals; + + import reflex.data.IPosition; + import reflex.data.Position; + import reflex.data.ScrollPosition; + + public class StepBehaviorTest extends EventDispatcher + { + + //public var stage:Stage; + + [Bindable] + public var skin:MockSlider; + + [Before] + public function setup():void { + skin = new MockSlider(); + //stage.addChild(skin); + } + + [After] + public function destroy():void { + //stage.removeChild(skin); + skin = null; + } + + [Test] + public function testIncrementButtonClick():void { // default behavior (for sliders) should jump to position + var behavior:StepBehavior= new StepBehavior(this); + var position:IPosition = behavior.position = new Position(0, 100, 0, 1); + var event:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, 0, 0, null, false, false, false, true, 0); + var dispatcher:IEventDispatcher = skin.incrementButton; + dispatcher.dispatchEvent(event); + assertEquals(1, position.value); + } + + [Test] + public function testDecrementButtonClick():void { // default behavior (for sliders) should jump to position + var behavior:StepBehavior = new StepBehavior(this); + var position:IPosition = behavior.position = new Position(0, 100, 100, 1); + var event:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, 0, 0, null, false, false, false, true, 0); + var dispatcher:IEventDispatcher = skin.decrementButton; + dispatcher.dispatchEvent(event); + assertEquals(99, position.value); + } + + + } +} \ No newline at end of file diff --git a/tests/reflex/collections/SimpleCollectionTest.as b/tests/reflex/collections/SimpleCollectionTest.as new file mode 100644 index 00000000..42383195 --- /dev/null +++ b/tests/reflex/collections/SimpleCollectionTest.as @@ -0,0 +1,69 @@ +package reflex.collections +{ + import flash.events.IEventDispatcher; + + import mx.collections.IList; + import mx.events.CollectionEvent; + import mx.events.CollectionEventKind; + + import org.flexunit.Assert; + import org.flexunit.async.Async; + + public class SimpleCollectionTest + { + + private var C:Class = SimpleCollection; + + [Test(async)] + public function testAddItem():void { + var collection:IList= new C(); + collection.addItem("test1"); + collection.addItem("test2"); + var listener:Function = Async.asyncHandler(this, collectionChangeHandler, 500, "test3", timeoutHandler); + (collection as IEventDispatcher).addEventListener(CollectionEvent.COLLECTION_CHANGE, listener, false, 0, false); + collection.addItem("test3"); + } + + [Test(async)] + public function testSource():void { + var collection:SimpleCollection = new C(); + var source:Array = []; + var listener:Function = Async.asyncHandler(this, collectionChangeHandler, 500, source, timeoutHandler); + (collection as IEventDispatcher).addEventListener(CollectionEvent.COLLECTION_CHANGE, listener, false, 0, false); + collection.source = source; + } + + [Test(async)] + public function testSourceNull():void { + var collection:SimpleCollection = new C(); + var source:Array = null; + var listener:Function = Async.asyncHandler(this, collectionChangeHandler, 500, source, timeoutHandler); + (collection as IEventDispatcher).addEventListener(CollectionEvent.COLLECTION_CHANGE, listener, false, 0, false); + collection.source = source; // makes sure null doesn't throw an error + } + + private function collectionChangeHandler(event:CollectionEvent, data:Object):void { + switch(event.kind) { + case CollectionEventKind.RESET: + if(data == null) { + // blank array is given for null source + Assert.assertEquals(0, event.items.length); + } else { + for(var i:int = 0; i < event.items.length; i++) { + Assert.assertEquals(data[i], event.items[i]); + } + } + break; + case CollectionEventKind.ADD: + Assert.assertEquals(data, event.items[0]); + Assert.assertEquals(2, event.location); // tests location index + break; + } + } + + protected function timeoutHandler(type:String):void { + Assert.fail(type + ": timed out."); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/components/ButtonTest.as b/tests/reflex/components/ButtonTest.as new file mode 100644 index 00000000..384d9f24 --- /dev/null +++ b/tests/reflex/components/ButtonTest.as @@ -0,0 +1,29 @@ +package reflex.components +{ + import reflex.tests.BaseClass; + + public class ButtonTest extends BaseClass + { + + [Test(async)] + public function testLabelChange():void { + testPropertyChange(Button, "label", "test"); + } + + [Test(async)] + public function testLabelNotChanged():void { + testPropertyNotChanged(Button, "label", "test"); + } + + [Test(async)] + public function testSelectedChange():void { + testPropertyChange(Button, "selected", true); + } + + [Test(async)] + public function testSelectedNotChanged():void { + testPropertyNotChanged(Button, "selected", true); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/components/ComponentTest.as b/tests/reflex/components/ComponentTest.as new file mode 100644 index 00000000..dbd0c5fc --- /dev/null +++ b/tests/reflex/components/ComponentTest.as @@ -0,0 +1,97 @@ +package reflex.components +{ + import flash.display.MovieClip; + + import flexunit.framework.Assert; + + import mx.collections.IList; + + import org.flexunit.asserts.assertEquals; + + import reflex.behaviors.IBehavior; + import reflex.behaviors.MockBehavior; + import reflex.display.StatefulTestBase; + import reflex.tests.BaseClass; + + public class ComponentTest extends BaseClass + { + + public function ComponentTest() { + super(); + //C = Component; + } + + /* + [Test(async)] + public function testBehavoirsChange():void { + testPropertyChange(Component, "behaviors", []); + } + */ + + [Test] + public function testSetBehavoirsUsingArray():void { + var proposedBehaviors:Array = [new MockBehavior(), new MockBehavior(), new MockBehavior()]; + + var c:Component = new Component(); + + c.behaviors = proposedBehaviors; + var resultingBehaviors:IList = c.behaviors; + + Assert.assertEquals(proposedBehaviors.length, resultingBehaviors.length); + + var numberOfResultingBehaviors:int = resultingBehaviors.length; + + for(var i:int = 0; i < numberOfResultingBehaviors; i++) { + var proposedBehavior:IBehavior = proposedBehaviors[i]; + var resultBehavior:IBehavior = resultingBehaviors.getItemAt(i) as IBehavior; + Assert.assertEquals(proposedBehavior, resultBehavior); + } + } + + [Test] + public function testSetBehavoirsUsingSingleBehavior():void { + var proposedBehavior:IBehavior = new MockBehavior(); + + var c:Component = new Component(); + + c.behaviors = proposedBehavior; + var resultingBehaviors:IList = c.behaviors; + + Assert.assertEquals(1, resultingBehaviors.length); + Assert.assertEquals(proposedBehavior, resultingBehaviors.getItemAt(0)); + } + + [Test(async)] + public function testSkinChange():void { + testPropertyChange(Component, "skin", new MovieClip()); + } + + [Test(async)] + public function testSkinNotChanged():void { + testPropertyNotChanged(Component, "skin", new MovieClip()); + } + + + [Test(async)] + public function testCurrentStateChange():void { + testPropertyChange(Component, "currentState", "test"); + } + + [Test(async)] + public function testCurrentStateNotChanged():void { + testPropertyNotChanged(Component, "currentState", "test"); + } + + + [Test(async)] + public function testEnabledChange():void { + testPropertyChange(Component, "enabled", false); + } + + [Test(async)] + public function testEnabledNotChanged():void { + testPropertyNotChanged(Component, "enabled", false); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/components/ListItemTest.as b/tests/reflex/components/ListItemTest.as new file mode 100644 index 00000000..3a3b4d64 --- /dev/null +++ b/tests/reflex/components/ListItemTest.as @@ -0,0 +1,47 @@ +package reflex.components +{ + import flexunit.framework.Assert; + + import reflex.tests.BaseClass; + + public class ListItemTest extends BaseClass + { + + [Test(async)] + public function testDataChange():void { + testPropertyChange(ListItem, "data", "test"); + } + + [Test(async)] + public function testDataNotChanged():void { + testPropertyNotChanged(ListItem, "data", "test"); + } + + [Test] + public function testNewInstance():void { + var listItemDefinition:ListItem = new ListItem(); + // ListItemSkin caused test build to fail because it doesn't reference ReflexSkins.swc + listItemDefinition.skin = {}; //new ListItemSkin(); + + var newInstance:* = listItemDefinition.newInstance(); + + Assert.assertNotNull(newInstance); + Assert.assertTrue(newInstance is ListItem); + + var newInstanceOfListItem:ListItem = newInstance as ListItem; + Assert.assertNotNull(newInstanceOfListItem); + } + + [Test] + public function testNewInstanceWithoutSettingSkin():void { + var listItemDefinition:ListItem = new ListItem(); + + try { + var newInstance:* = listItemDefinition.newInstance(); + Assert.fail("Calling ListItemDefinition.newInstance() without first setting the skin should have thrown an error."); + } catch (e:Error) { + //This test should throw an error. So, if we got here, this test passed. + } + } + } +} \ No newline at end of file diff --git a/tests/reflex/components/ListTest.as b/tests/reflex/components/ListTest.as new file mode 100644 index 00000000..f56fba33 --- /dev/null +++ b/tests/reflex/components/ListTest.as @@ -0,0 +1,64 @@ +package reflex.components +{ + import reflex.collections.SimpleCollection; + import reflex.data.Position; + import reflex.data.Range; + import reflex.data.Selection; + import reflex.layouts.XYLayout; + import reflex.tests.BaseClass; + + public class ListTest extends BaseClass + { + + [Test(async)] + public function testDataProviderChange():void { + testPropertyChange(List, "dataProvider", new SimpleCollection()); + } + + [Test(async)] + public function testDataProviderNotChanged():void { + testPropertyNotChanged(List, "dataProvider", new SimpleCollection()); + } + + [Test(async)] + public function testTemplateChange():void { + testPropertyChange(List, "template", {}); + } + + [Test(async)] + public function testTemplateNotChanged():void { + testPropertyNotChanged(List, "template", {}); + } + + [Test(async)] + public function testLayoutChange():void { + testPropertyChange(List, "layout", new XYLayout()); + } + + [Test(async)] + public function testLayoutNotChanged():void { + testPropertyNotChanged(List, "layout", new XYLayout()); + } + + [Test(async)] + public function testSelectionChange():void { + testPropertyChange(List, "selection", new Selection()); + } + + [Test(async)] + public function testSelectionNotChanged():void { + testPropertyNotChanged(List, "selection", new Selection()); + } + + [Test(async)] + public function testPositionChange():void { + testPropertyChange(List, "position", new Position()); + } + + [Test(async)] + public function testPositionNotChanged():void { + testPropertyNotChanged(List, "position", new Position()); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/components/MockLayout.as b/tests/reflex/components/MockLayout.as new file mode 100644 index 00000000..8d4cab46 --- /dev/null +++ b/tests/reflex/components/MockLayout.as @@ -0,0 +1,27 @@ +package reflex.components { + import flash.events.IEventDispatcher; + import flash.geom.Point; + import flash.geom.Rectangle; + + import reflex.layouts.ILayout; + + public class MockLayout implements ILayout { + private var _target:IEventDispatcher; + + public function MockLayout() { + } + + public function get target():IEventDispatcher { return _target; } + public function set target(value:IEventDispatcher):void { + _target = value; + } + + public function measure(children:Array):Point { + return null; + } + + public function update(children:Array, tokens:Array, rectangle:Rectangle):Array { + return null; + } + } +} \ No newline at end of file diff --git a/tests/reflex/components/MockPosition.as b/tests/reflex/components/MockPosition.as new file mode 100644 index 00000000..acadef7c --- /dev/null +++ b/tests/reflex/components/MockPosition.as @@ -0,0 +1,27 @@ +package reflex.components { + import reflex.data.IPosition; + + public class MockPosition implements IPosition { + public function MockPosition() { + } + + private var _value:Number; + private var _minimum:Number; + private var _maximum:Number; + + public function get value():Number { return _value; } + public function set value(newValue:Number):void { + _value = newValue; + } + + public function get minimum():Number { return _minimum; } + public function set minimum(value:Number):void { + _minimum = value; + } + + public function get maximum():Number { return _maximum; } + public function set maximum(value:Number):void { + _maximum = value; + } + } +} \ No newline at end of file diff --git a/tests/reflex/components/ScrollerDefinitionTest.as b/tests/reflex/components/ScrollerDefinitionTest.as new file mode 100644 index 00000000..f930c60d --- /dev/null +++ b/tests/reflex/components/ScrollerDefinitionTest.as @@ -0,0 +1,39 @@ +package reflex.components +{ + import flexunit.framework.Assert; + + import mx.collections.ArrayList; + import mx.collections.IList; + + import reflex.collections.SimpleCollection; + import reflex.containers.ContainerTestBase; + import reflex.tests.BaseClass; + + public class ScrollerDefinitionTest extends ContainerTestBase + { + public function ScrollerDefinitionTest() { + super(); + C = Scroller; + } + + [Test(async)] + public function testHorizontalPositionChange():void { + testPropertyChange(Scroller, "horizontalPosition", new MockPosition()); + } + + [Test(async)] + public function testHorizontalPositionNotChanged():void { + testPropertyNotChanged(Scroller, "horizontalPosition", new MockPosition()); + } + + [Test(async)] + public function testVerticalPositionChange():void { + testPropertyChange(Scroller, "verticalPosition", new MockPosition()); + } + + [Test(async)] + public function testVerticalPositionNotChanged():void { + testPropertyNotChanged(Scroller, "verticalPosition", new MockPosition()); + } + } +} \ No newline at end of file diff --git a/tests/reflex/components/SliderDefinitionTest.as b/tests/reflex/components/SliderDefinitionTest.as new file mode 100644 index 00000000..800e1543 --- /dev/null +++ b/tests/reflex/components/SliderDefinitionTest.as @@ -0,0 +1,21 @@ +package reflex.components +{ + import reflex.tests.BaseClass; + + public class SliderDefinitionTest extends BaseClass + { + public function SliderDefinitionTest() + { + } + + [Test(async)] + public function testPositionChange():void { + testPropertyChange(SliderComponent, "position", new MockPosition()); + } + + [Test(async)] + public function testPositionNotChanged():void { + testPropertyNotChanged(SliderComponent, "position", new MockPosition()); + } + } +} \ No newline at end of file diff --git a/tests/reflex/containers/ContainerLifecycleTest.as b/tests/reflex/containers/ContainerLifecycleTest.as new file mode 100644 index 00000000..03909733 --- /dev/null +++ b/tests/reflex/containers/ContainerLifecycleTest.as @@ -0,0 +1,18 @@ +package reflex.containers { + import flexunit.framework.Assert; + + import reflex.containers.Container; + + import reflex.components.MockLayout; + import reflex.display.StatefulContainerTestBase; + import reflex.tests.BaseClass; + + public class ContainerLifecycleTest extends ContainerLifecycleTestBase { + + public function ContainerLifecycleTest() { + super(); + C = Container; + } + + } +} \ No newline at end of file diff --git a/tests/reflex/containers/ContainerLifecycleTestBase.as b/tests/reflex/containers/ContainerLifecycleTestBase.as new file mode 100644 index 00000000..4459d7a6 --- /dev/null +++ b/tests/reflex/containers/ContainerLifecycleTestBase.as @@ -0,0 +1,57 @@ +package reflex.containers { + import flash.display.DisplayObject; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + + import flexunit.framework.Assert; + + import mx.collections.ArrayList; + import mx.collections.IList; + + import org.flexunit.async.Async; + + import reflex.collections.SimpleCollection; + import reflex.components.MockLayout; + import reflex.tests.BaseClass; + + public class ContainerLifecycleTestBase extends BaseClass { + + public var C:Class; + + public function ContainerLifecycleTestBase() { + super(); + } + + [Test] + public function testSetContentWithString():void { + var instance:IContainer = new C(); + var child:DisplayObject = new DisplayObject(); + instance.content = [child]; + + var listener:Function = Async.asyncHandler(this, widthChangeHandler, 500, "measure", timeoutHandler); + (instance as IEventDispatcher).addEventListener("measure", listener, false, 0, false); + child.dispatchEvent(new Event("widthChange")); + + //Assert.assertTrue(instance.content is SimpleCollection); + + } + + private function widthChangeHandler(event:Event):void { + + } + + /* + + [Test(async)] + public function testLayoutChange():void { + testPropertyChange(C, "layout", new MockLayout()); + } + + [Test(async)] + public function testLayoutNotChanged():void { + testPropertyNotChanged(C, "layout", new MockLayout); + } + */ + } +} \ No newline at end of file diff --git a/tests/reflex/containers/ContainerMeasurementTest.as b/tests/reflex/containers/ContainerMeasurementTest.as new file mode 100644 index 00000000..f062f607 --- /dev/null +++ b/tests/reflex/containers/ContainerMeasurementTest.as @@ -0,0 +1,15 @@ +package reflex.containers +{ + import reflex.containers.Container; + + import reflex.measurement.MeasurableTestBase; + + public class ContainerMeasurementTest extends MeasurableTestBase + { + public function ContainerMeasurementTest() + { + super(); + C = Container; + } + } +} \ No newline at end of file diff --git a/tests/reflex/containers/ContainerTest.as b/tests/reflex/containers/ContainerTest.as new file mode 100644 index 00000000..6bb6d9f1 --- /dev/null +++ b/tests/reflex/containers/ContainerTest.as @@ -0,0 +1,43 @@ +package reflex.containers { + import flexunit.framework.Assert; + + import reflex.components.MockLayout; + import reflex.display.StatefulContainerTestBase; + import reflex.tests.BaseClass; + + public class ContainerTest extends StatefulContainerTestBase { + + public function ContainerTest() { + super(); + + C = Container; + } + + [Test] + public function testSetStyleDeclaration():void { + var testContainer:Container = new Container(); + testContainer.styleDeclaration = "testStyleDeclaration"; + + Assert.assertEquals("testStyleDeclaration", testContainer.styleDeclaration); + } + + [Test(async)] + public function testTemplateChange():void { + testPropertyChange(Container, "template", "testTemplate"); + } + + [Test(async)] + public function testTemplateNotChanged():void { + testPropertyNotChanged(Container, "template", "testTemplate"); + } + + [Test] + public function testSetSize():void { + var testContainer:Container = new Container(); + testContainer.setSize(20, 25); + + Assert.assertEquals(20, testContainer.width); + Assert.assertEquals(25, testContainer.height); + } + } +} \ No newline at end of file diff --git a/tests/reflex/containers/ContainerTestBase.as b/tests/reflex/containers/ContainerTestBase.as new file mode 100644 index 00000000..d8ec83a2 --- /dev/null +++ b/tests/reflex/containers/ContainerTestBase.as @@ -0,0 +1,91 @@ +package reflex.containers { + import flexunit.framework.Assert; + + import mx.collections.ArrayList; + import mx.collections.IList; + + import reflex.collections.SimpleCollection; + import reflex.components.MockLayout; + import reflex.tests.BaseClass; + + public class ContainerTestBase extends BaseClass { + + public var C:Class; + + public function ContainerTestBase() { + super(); + } + + [Test] + public function testSetContentWithString():void { + var instance:IContainer = new C(); + + instance.content = "testString"; + Assert.assertTrue(instance.content is SimpleCollection); + + var simpleCollection:SimpleCollection = instance.content as SimpleCollection; + Assert.assertEquals(1, simpleCollection.length); + Assert.assertEquals("testString", simpleCollection.getItemAt(0)); + } + + [Test] + public function testSetContentWithIList():void { + var instance:IContainer = new C(); + + var ilist:IList = new ArrayList(); + ilist.addItem("testIList"); + + instance.content = ilist; + Assert.assertTrue(instance.content is IList); + + var resultingIList:IList = instance.content as IList; + Assert.assertEquals(1, resultingIList.length); + Assert.assertEquals("testIList", resultingIList.getItemAt(0)); + } + + [Test] + public function testSetContentWithArray():void { + var instance:IContainer = new C(); + + var array:Array = ["testArray"]; + + instance.content = array; + Assert.assertTrue(instance.content is SimpleCollection); + + var simpleCollection:SimpleCollection = instance.content as SimpleCollection; + Assert.assertEquals(1, simpleCollection.length); + + var firstItemInSimpleCollection:Object = simpleCollection.getItemAt(0); + Assert.assertTrue(firstItemInSimpleCollection is String); + Assert.assertEquals("testArray", firstItemInSimpleCollection); + } + + [Test] + public function testSetContentWithVector():void { + var instance:IContainer = new C(); + + var vector:Vector. = new Vector.(); + vector.push("testVector"); + + instance.content = vector; + Assert.assertTrue(instance.content is SimpleCollection); + + var simpleCollection:SimpleCollection = instance.content as SimpleCollection; + Assert.assertEquals(1, simpleCollection.length); + + var firstItemInSimpleCollection:Object = simpleCollection.getItemAt(0); + Assert.assertTrue(firstItemInSimpleCollection is String); + Assert.assertEquals("testVector", firstItemInSimpleCollection); + } + + [Test(async)] + public function testLayoutChange():void { + testPropertyChange(C, "layout", new MockLayout()); + } + + [Test(async)] + public function testLayoutNotChanged():void { + testPropertyNotChanged(C, "layout", new MockLayout); + } + } +} \ No newline at end of file diff --git a/tests/reflex/data/PositionTest.as b/tests/reflex/data/PositionTest.as new file mode 100644 index 00000000..fbe00508 --- /dev/null +++ b/tests/reflex/data/PositionTest.as @@ -0,0 +1,51 @@ +package reflex.data { + import flexunit.framework.Assert; + + import reflex.tests.BaseClass; + + public class PositionTest extends BaseClass { + + public function PositionTest() { + } + + [Test(async)] + public function testValueChange():void { + testPropertyChange(Position, "value", 10); + } + + [Test(async)] + public function testValueNotChanged():void { + testPropertyNotChanged(Position, "value", 10); + } + + [Test(async)] + public function testStepSizeChange():void { + testPropertyChange(Position, "stepSize", 10); + } + + [Test(async)] + public function testStepSizeNotChanged():void { + testPropertyNotChanged(Position, "stepSize", 10); + } + + [Test] + public function testPositionConstructor():void { + var position:Position = new Position(10, 90, 20, 5); + + Assert.assertEquals(10, position.minimum); + Assert.assertEquals(90, position.maximum); + Assert.assertEquals(20, position.value); + Assert.assertEquals(5, position.stepSize); + } + + [Test] + public function testDefaultPositionConstructor():void { + var position:Position = new Position(); + + Assert.assertEquals(0, position.minimum); + Assert.assertEquals(100, position.maximum); + Assert.assertEquals(0, position.value); + Assert.assertEquals(1, position.stepSize); + } + } +} \ No newline at end of file diff --git a/tests/reflex/data/RangeTest.as b/tests/reflex/data/RangeTest.as new file mode 100644 index 00000000..9b08d914 --- /dev/null +++ b/tests/reflex/data/RangeTest.as @@ -0,0 +1,47 @@ +package reflex.data { + import flexunit.framework.Assert; + + import reflex.tests.BaseClass; + + public class RangeTest extends BaseClass { + + public function RangeTest() { + } + + [Test(async)] + public function testMinimumChange():void { + testPropertyChange(Range, "minimum", 10); + } + + [Test(async)] + public function testMinimumNotChanged():void { + testPropertyNotChanged(Range, "minimum", 10); + } + + [Test(async)] + public function testMaximumChange():void { + testPropertyChange(Range, "maximum", 10); + } + + [Test(async)] + public function testMaximumNotChanged():void { + testPropertyNotChanged(Range, "maximum", 10); + } + + [Test] + public function testRangeConstructor():void { + var range:Range = new Range(10, 90); + + Assert.assertEquals(10, range.minimum); + Assert.assertEquals(90, range.maximum); + } + + [Test] + public function testDefaultRangeConstructor():void { + var range:Range = new Range(); + + Assert.assertEquals(0, range.minimum); + Assert.assertEquals(100, range.maximum); + } + } +} \ No newline at end of file diff --git a/tests/reflex/data/ScrollPositionTest.as b/tests/reflex/data/ScrollPositionTest.as new file mode 100644 index 00000000..c4324ce4 --- /dev/null +++ b/tests/reflex/data/ScrollPositionTest.as @@ -0,0 +1,43 @@ +package reflex.data { + import flexunit.framework.Assert; + + import reflex.tests.BaseClass; + + public class ScrollPositionTest extends BaseClass { + + public function ScrollPositionTest() { + } + + [Test(async)] + public function testPageSizeChange():void { + testPropertyChange(ScrollPosition, "pageSize", 20); + } + + [Test(async)] + public function testPageSizeNotChanged():void { + testPropertyNotChanged(ScrollPosition, "pageSize", 20); + } + + [Test] + public function testScrollPositionConstructor():void { + var scrollPosition:ScrollPosition = new ScrollPosition(10, 90, 20, 5, 15); + + Assert.assertEquals(10, scrollPosition.minimum); + Assert.assertEquals(90, scrollPosition.maximum); + Assert.assertEquals(20, scrollPosition.value); + Assert.assertEquals(5, scrollPosition.stepSize); + Assert.assertEquals(15, scrollPosition.pageSize); + } + + [Test] + public function testDefaultScrollPositionConstructor():void { + var scrollPosition:ScrollPosition = new ScrollPosition(); + + Assert.assertEquals(0, scrollPosition.minimum); + Assert.assertEquals(100, scrollPosition.maximum); + Assert.assertEquals(0, scrollPosition.value); + Assert.assertEquals(1, scrollPosition.stepSize); + Assert.assertEquals(10, scrollPosition.pageSize); + } + } +} \ No newline at end of file diff --git a/tests/reflex/data/SelectionTest.as b/tests/reflex/data/SelectionTest.as new file mode 100644 index 00000000..90674737 --- /dev/null +++ b/tests/reflex/data/SelectionTest.as @@ -0,0 +1,35 @@ +package reflex.data { + import flexunit.framework.Assert; + + import mx.collections.IList; + + import reflex.tests.BaseClass; + + public class SelectionTest extends BaseClass { + + public function SelectionTest() { + } + + [Test(async)] + public function testSelectedItemChange():void { + testPropertyChange(Selection, "selectedItem", new Object()); + } + + [Test(async)] + public function testSelectedItemNotChanged():void { + testPropertyNotChanged(Selection, "selectedItem", "testValue"); + } + + [Test] + public function testGetSelectedItems():void { + var selection:Selection = new Selection(); + + selection.selectedItem = "testValue"; + + var resultList:IList = selection.selectedItems; + + Assert.assertEquals(1, resultList.length); + Assert.assertEquals("testValue", resultList.getItemAt(0)); + } + } +} \ No newline at end of file diff --git a/tests/reflex/display/BitmapDisplayMeasurableTest.as b/tests/reflex/display/BitmapDisplayMeasurableTest.as new file mode 100644 index 00000000..d93c875f --- /dev/null +++ b/tests/reflex/display/BitmapDisplayMeasurableTest.as @@ -0,0 +1,161 @@ +package reflex.display +{ + import flexunit.framework.Assert; + + import reflex.measurement.MeasurableTestBase; + + public class BitmapDisplayMeasurableTest extends MeasurableTestBase + { + /* + public var bitmapDisplay:BitmapDisplay; + + public function BitmapDisplayMeasurableTest() + { + super(); + C = BitmapDisplay; + } + + [Before] + public function setup():void { + bitmapDisplay = new BitmapDisplay(); + } + + [After] + public function destroy():void { + bitmapDisplay = null; + } + + [Test(async)] + public function testIdChange():void { + testPropertyChange(C, "id", "testId"); + } + + [Test(async)] + public function testIdNotChanged():void { + testPropertyNotChanged(C, "id", "testId"); + } + + [Test(async)] + public function testStyleNameChange():void { + testPropertyChange(C, "styleName", "testStyleName"); + } + + [Test(async)] + public function testStyleNameNotChanged():void { + testPropertyNotChanged(C, "styleName", "testStyleName"); + } + + [Test] + public function testSetStyleWithSingleAssignment():void { + bitmapDisplay.style = "testProperty:testValue"; + + Assert.assertTrue(bitmapDisplay.style.hasOwnProperty("testProperty")); + + var testValue:String = bitmapDisplay.style["testProperty"]; + + Assert.assertEquals("testValue", testValue); + } + + [Test] + public function testSetStyleWithMultipleAssignments():void { + bitmapDisplay.style = "testProperty1:testValue1;testProperty2:testValue2"; + + Assert.assertTrue(bitmapDisplay.style.hasOwnProperty("testProperty1")); + Assert.assertTrue(bitmapDisplay.style.hasOwnProperty("testProperty2")); + + var testValue1:String = bitmapDisplay.style["testProperty1"]; + var testValue2:String = bitmapDisplay.style["testProperty2"]; + + Assert.assertEquals("testValue1", testValue1); + Assert.assertEquals("testValue2", testValue2); + } + */ + /* + [Test] [Ignore] + public function testSetStyleWithIncorrectDelimiterUsage():void { + var testPassed:Boolean = false; + + try { + bitmapDisplay.style = "testProperty1=testValue1,testProperty2=testValue2"; + } catch (e:Error) { + testPassed = true; + } + + Assert.assertTrue("Trying to set the style when using delimiters other than \":\" and \";\" should have caused an error.", testPassed); + } + */ + /* + [Test] + public function testSetStyleWithNonStringAssignment():void { + var testPassed:Boolean = false; + + try { + bitmapDisplay.style = new Object(); + } catch (e:Error) { + testPassed = true; + } + + Assert.assertTrue("Trying to set the style with something other than a String should have caused an error.", testPassed); + } + + [Test] + public function testDirectStylePropertyAssignment():void { + bitmapDisplay.setStyle("testProperty", "testValue"); + + var resultingValue:String = bitmapDisplay.getStyle("testProperty"); + + Assert.assertEquals("testValue", resultingValue); + } + + [Test(async)] + public function testXChange():void { + testPropertyChange(C, "x", 100); + } + + [Test(async)] + public function testXNotChanged():void { + testPropertyNotChanged(C, "x", 100); + } + + [Test(async)] + public function testyChange():void { + testPropertyChange(C, "y", 100); + } + + [Test(async)] + public function testyNotChanged():void { + testPropertyNotChanged(C, "y", 100); + } + + [Test(async)] + public function testPercentWidthChange():void { + testPropertyChange(C, "percentWidth", 100); + } + + [Test(async)] + public function testPercentWidthNotChanged():void { + testPropertyNotChanged(C, "percentWidth", 100); + } + + [Test(async)] + public function testPercentHeightChange():void { + testPropertyChange(C, "percentHeight", 100); + } + + [Test(async)] + public function testPercentHeightNotChanged():void { + testPropertyNotChanged(C, "percentHeight", 100); + } + + [Test(async)] + public function testVisibleChange():void { + testPropertyChange(C, "visible", false); + } + + [Test(async)] + public function testVisibleNotChanged():void { + testPropertyNotChanged(C, "visible", false); + } + */ + } +} \ No newline at end of file diff --git a/tests/reflex/display/DisplayFunctionsTest.as b/tests/reflex/display/DisplayFunctionsTest.as new file mode 100644 index 00000000..f520b956 --- /dev/null +++ b/tests/reflex/display/DisplayFunctionsTest.as @@ -0,0 +1,70 @@ +package reflex.display +{ + + import mx.core.ClassFactory; + import mx.core.IFactory; + + import org.flexunit.Assert; + + import reflex.components.ListItem; + import reflex.templating.IDataTemplate; + import reflex.templating.getDataRenderer; + + public class DisplayFunctionsTest + { + + [Test] + public function testGetDataRendererForIDataTemplate():void { + var template:IDataTemplate = new TestTemplate(); + var instance:Object = getDataRenderer(this, "test", template); + Assert.assertTrue(instance is ListItem); + Assert.assertEquals("test", instance.data); + } + + [Test] + public function testGetDataRendererForClass():void { + var instance:Object = getDataRenderer(this, "test", ListItem); + Assert.assertTrue(instance is ListItem); + Assert.assertEquals("test", instance.data); + } + + [Test] + public function testGetDataRendererForIFactory():void { + var factory:IFactory = new ClassFactory(ListItem); + var instance:Object = getDataRenderer(this, "test", factory); + Assert.assertTrue(instance is ListItem); + Assert.assertEquals("test", instance.data); + } + + [Test] + public function testGetDataRendererForFunction():void { + var instance:Object = getDataRenderer(this, "test", templateFunction); + Assert.assertTrue(instance is ListItem); + Assert.assertEquals("test", instance.data); + } + + [Test] + public function testGetDataRendererForComponentObject():void { + var instance:Object = getDataRenderer(this, new ListItem(), null); + Assert.assertTrue(instance is ListItem); + } + + private function templateFunction(data:Object):Object { + return new ListItem(); + } + + } + +} +import reflex.components.Component; +import reflex.components.ListItem; +import reflex.templating.IDataTemplate; + +class TestTemplate implements IDataTemplate +{ + + public function createDisplayObject(data:*):Object { + return new ListItem(); + } + +} \ No newline at end of file diff --git a/tests/reflex/display/DisplayMeasurableTest.as b/tests/reflex/display/DisplayMeasurableTest.as new file mode 100644 index 00000000..ecef78f6 --- /dev/null +++ b/tests/reflex/display/DisplayMeasurableTest.as @@ -0,0 +1,35 @@ +package reflex.display +{ + import reflex.measurement.MeasurableTestBase; + + public class DisplayMeasurableTest extends MeasurableTestBase + { + + public function DisplayMeasurableTest() + { + super(); + C = MeasurableItem; + } + + [Test(async)] + public function testPercentWidthChange():void { + testPropertyChange(C, "percentWidth", 100); + } + + [Test(async)] + public function testPercentWidthNotChanged():void { + testPropertyNotChanged(C, "percentWidth", 100); + } + + [Test(async)] + public function testPercentHeightChange():void { + testPropertyChange(C, "percentHeight", 100); + } + + [Test(async)] + public function testPercentHeightNotChanged():void { + testPropertyNotChanged(C, "percentHeight", 100); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/display/DisplayStyleableTest.as b/tests/reflex/display/DisplayStyleableTest.as new file mode 100644 index 00000000..a0ae365b --- /dev/null +++ b/tests/reflex/display/DisplayStyleableTest.as @@ -0,0 +1,26 @@ +package reflex.display +{ + import org.flexunit.Assert; + + import reflex.styles.IStyleable; + import reflex.styles.StyleableTestBase; + + public class DisplayStyleableTest extends StyleableTestBase + { + + public function DisplayStyleableTest() + { + super(); + C = MeasurableItem; + } + + [Test] + public function testStyleString():void { + var instance:MeasurableItem = new C(); + instance.style = "testStyle: test;"; // more complex parsing needed later + var v:Object = instance.getStyle("testStyle"); + Assert.assertEquals("test", v); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/display/DisplayTest.as b/tests/reflex/display/DisplayTest.as new file mode 100644 index 00000000..64b866a3 --- /dev/null +++ b/tests/reflex/display/DisplayTest.as @@ -0,0 +1,106 @@ +package reflex.display { + import flexunit.framework.Assert; + + import reflex.styles.StyleableMeasurableMeasurablePercentTestBase; + + public class DisplayTest extends StyleableMeasurableMeasurablePercentTestBase { + public var item:MeasurableItem; + + public function DisplayTest() { + super(); + } + + [Before] + public function setup():void { + item = new MeasurableItem(); + } + + [After] + public function destroy():void { + item = null; + } + + [Test] + public function testSetStyleWithSingleAssignment():void { + item.style = "testProperty:testValue"; + + Assert.assertTrue(item.style.hasOwnProperty("testProperty")); + + var testValue:String = item.style["testProperty"]; + + Assert.assertEquals("testValue", testValue); + } + + [Test] + public function testSetStyleWithMultipleAssignments():void { + item.style = "testProperty1:testValue1;testProperty2:testValue2"; + + Assert.assertTrue(item.style.hasOwnProperty("testProperty1")); + Assert.assertTrue(item.style.hasOwnProperty("testProperty2")); + + var testValue1:String = item.style["testProperty1"]; + var testValue2:String = item.style["testProperty2"]; + + Assert.assertEquals("testValue1", testValue1); + Assert.assertEquals("testValue2", testValue2); + } + + /* + [Test] [Ignore] + public function testSetStyleWithIncorrectDelimiterUsage():void { + var testPassed:Boolean = false; + + try { + bitmapDisplay.style = "testProperty1=testValue1,testProperty2=testValue2"; + } catch (e:Error) { + testPassed = true; + } + + Assert.assertTrue("Trying to set the style when using delimiters other than \":\" and \";\" should have caused an error.", testPassed); + } + */ + + [Test] + public function testSetStyleWithNonStringAssignment():void { + var testPassed:Boolean = false; + + try { + item.style = new Object(); + } catch (e:Error) { + testPassed = true; + } + + Assert.assertTrue("Trying to set the style with something other than a String should have caused an error.", testPassed); + } + + [Test(async)] + public function testXChange():void { + testPropertyChange(MeasurableItem, "x", 100); + } + + [Test(async)] + public function testXNotChanged():void { + testPropertyNotChanged(MeasurableItem, "x", 100); + } + + [Test(async)] + public function testyChange():void { + testPropertyChange(MeasurableItem, "y", 100); + } + + [Test(async)] + public function testyNotChanged():void { + testPropertyNotChanged(MeasurableItem, "y", 100); + } + + [Test(async)] + public function testVisibleChange():void { + testPropertyChange(MeasurableItem, "visible", false); + } + + [Test(async)] + public function testVisibleNotChanged():void { + testPropertyNotChanged(MeasurableItem, "visible", false); + } + } +} \ No newline at end of file diff --git a/tests/reflex/display/GroupTest.as b/tests/reflex/display/GroupTest.as new file mode 100644 index 00000000..fc0dd62b --- /dev/null +++ b/tests/reflex/display/GroupTest.as @@ -0,0 +1,24 @@ +package reflex.display +{ + import reflex.containers.Container; + + public class GroupTest extends StatefulContainerTestBase + { + + public function GroupTest() { + super(); + C = Container; + } + + [Test(async)] + public function testTemplateChange():void { + testPropertyChange(C, "template", {}); + } + + [Test(async)] + public function testTemplateNotChanged():void { + testPropertyChange(C, "template", {}); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/display/LifecycleTest.as b/tests/reflex/display/LifecycleTest.as new file mode 100644 index 00000000..05a6c4bd --- /dev/null +++ b/tests/reflex/display/LifecycleTest.as @@ -0,0 +1,36 @@ +package reflex.containers { + import flash.display.DisplayObject; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + + import flexunit.framework.Assert; + + import mx.collections.ArrayList; + import mx.collections.IList; + + import org.flexunit.async.Async; + + import reflex.collections.SimpleCollection; + import reflex.components.MockLayout; + import reflex.invalidation.LifeCycle; + import reflex.tests.BaseClass; + + public class LifecycleTest extends BaseClass { + + public var C:Class; + + public function LifecycleTest() { + super(); + } + + [Test] + public function testInitialization():void { + var instance:IEventDispatcher = new C(); + //instance.addEventListener(LifeCycle.CREATE, ; + //Assert.assertTrue(instance.content is SimpleCollection); + + } + + } +} \ No newline at end of file diff --git a/tests/reflex/display/StatefulContainerTestBase.as b/tests/reflex/display/StatefulContainerTestBase.as new file mode 100644 index 00000000..3ab58e8c --- /dev/null +++ b/tests/reflex/display/StatefulContainerTestBase.as @@ -0,0 +1,67 @@ +package reflex.display +{ + + import flash.display.Bitmap; + import flash.display.DisplayObject; + import flash.display.Shape; + import flash.display.Sprite; + import flash.events.IEventDispatcher; + + import mx.collections.IList; + + import org.flexunit.Assert; + + import reflex.collections.SimpleCollection; + import reflex.containers.IContainer; + import reflex.layouts.XYLayout; + + public class StatefulContainerTestBase extends StatefulTestBase + { + + //public var C:Class; + + [Test(order=-1)] + public function testIContainer():void { + var instance:Object = new C(); + Assert.assertTrue(instance is IContainer); + Assert.assertTrue(instance is IEventDispatcher); + } + + [Test(async)] + public function testChildrenChange():void { + testPropertyChange(C, "content", new SimpleCollection()); + } + + [Test(async)] + public function testChildrenNotChanged():void { + testPropertyNotChanged(C, "content", new SimpleCollection()); + } + + [Test(async)] + public function testLayoutChange():void { + testPropertyChange(C, "layout", new XYLayout()); + } + + [Test(async)] + public function testLayoutNotChanged():void { + testPropertyNotChanged(C, "layout", new XYLayout()); + } + + [Test] + public function testChildrenArrayConversion():void { + // containers may be assigned vanilla Arrays in MXML and will be required to convert them to an IList + var container:IContainer = new C(); + var test1:DisplayObject = new Sprite(); + var test2:DisplayObject = new Shape(); + var test3:DisplayObject = new Bitmap(); + container.content = [test1, test2, test3]; + var list:IList = container.content; + Assert.assertNotNull(list); + Assert.assertTrue(list is IList); + Assert.assertEquals(test1, list.getItemAt(0)); + Assert.assertEquals(test2, list.getItemAt(1)); + Assert.assertEquals(test3, list.getItemAt(2)); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/display/StatefulTestBase.as b/tests/reflex/display/StatefulTestBase.as new file mode 100644 index 00000000..b7fd933f --- /dev/null +++ b/tests/reflex/display/StatefulTestBase.as @@ -0,0 +1,67 @@ +package reflex.display +{ + import flexunit.framework.Assert; + + import reflex.components.IStateful; + import reflex.tests.BaseClass; + + public class StatefulTestBase extends BaseClass + { + + public var C:Class; + + [Test(async)] + public function testStatesChange():void { + testPropertyChange(C, "states", []); + } + + [Test(async)] + public function testStatesNotChanged():void { + testPropertyNotChanged(C, "states", []); + } + + [Test(async)] + public function testCurrentStateChange():void { + testPropertyChange(C, "currentState", "test"); + } + + [Test(async)] + public function testCurrentStateNotChanged():void { + testPropertyNotChanged(C, "currentState", "test"); + } + + [Test(async)] + public function testTransitionsChange():void { + testPropertyChange(C, "transitions", new Array()); + } + + [Test(async)] + public function testTransitionsNotChanged():void { + testPropertyNotChanged(C, "transitions", new Array()); + } + + [Test] + public function testHasState():void { + var instance:Object = new C(); + var statefulObject:IStateful = instance as IStateful; + statefulObject.states = createTestStates(); + + Assert.assertTrue(statefulObject.hasState("name1")); + Assert.assertFalse(statefulObject.hasState("nonExistingStateName")); + } + + private function createTestStates():Array { + var state1:Object = new Object(); + var state2:Object = new Object(); + var state3:Object = new Object(); + + state1["name"] = "name1"; + state2["name"] = "name2"; + state3["name"] = "name3"; + + var testStates:Array = [state1, state2, state3]; + + return testStates; + } + } +} \ No newline at end of file diff --git a/tests/reflex/display/TextFieldDisplayMeasurableTest.as b/tests/reflex/display/TextFieldDisplayMeasurableTest.as new file mode 100644 index 00000000..123735b3 --- /dev/null +++ b/tests/reflex/display/TextFieldDisplayMeasurableTest.as @@ -0,0 +1,35 @@ +package reflex.display +{ + import reflex.measurement.MeasurableTestBase; + import reflex.text.TextFieldDisplay; + + public class TextFieldDisplayMeasurableTest extends MeasurableTestBase + { + public function TextFieldDisplayMeasurableTest() + { + super(); + C = TextFieldDisplay; + } + + [Test(async)] + public function testPercentWidthChange():void { + testPropertyChange(C, "percentWidth", 100); + } + + [Test(async)] + public function testPercentWidthNotChanged():void { + testPropertyNotChanged(C, "percentWidth", 100); + } + + [Test(async)] + public function testPercentHeightChange():void { + testPropertyChange(C, "percentHeight", 100); + } + + [Test(async)] + public function testPercentHeightNotChanged():void { + testPropertyNotChanged(C, "percentHeight", 100); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/graphics/GraphicBaseMeasurementTest.as b/tests/reflex/graphics/GraphicBaseMeasurementTest.as new file mode 100644 index 00000000..eb2b6e63 --- /dev/null +++ b/tests/reflex/graphics/GraphicBaseMeasurementTest.as @@ -0,0 +1,13 @@ +package reflex.graphics +{ + import reflex.measurement.MeasurableTestBase; + + public class GraphicBaseMeasurementTest extends MeasurableTestBase + { + public function GraphicBaseMeasurementTest() + { + super(); + C = GraphicBase; + } + } +} \ No newline at end of file diff --git a/tests/reflex/layouts/BasicLayoutTest.as b/tests/reflex/layouts/BasicLayoutTest.as new file mode 100644 index 00000000..5b550c86 --- /dev/null +++ b/tests/reflex/layouts/BasicLayoutTest.as @@ -0,0 +1,257 @@ +package reflex.layouts +{ + import flash.events.EventDispatcher; + import flash.geom.Point; + import flash.geom.Rectangle; + + import org.flexunit.Assert; + + import reflex.display.MeasurableItem; + + public class BasicLayoutTest extends EventDispatcher + { + + // still playing around with how to best test these + + // measurement tests + + [Test] + public function testMeasurementXY():void { + var child1:MeasurableItem = new MeasurableItem(); + var child2:MeasurableItem = new MeasurableItem(); + + child1.x = 5; + child1.y = 5; + child1.width = 20; + child1.height = 20; + + child2.x = 10; + child2.y = 10; + child2.width = 20; + child2.height = 20; + + var layout:BasicLayout = new BasicLayout(); + var point:Point = layout.measure([child1, child2]); + Assert.assertEquals(30, point.x); + Assert.assertEquals(30, point.y); + } + + [Test] + public function testMeasurementLeft():void { + var layout:BasicLayout = new BasicLayout(); + var child:MeasurableItem = new MeasurableItem(); + + child.width = 20; + child.setStyle("left", 5); + + var point:Point = layout.measure([child]); + Assert.assertEquals(25, point.x) + } + + [Test] + public function testMeasurementRight():void { + var layout:BasicLayout = new BasicLayout(); + var child:MeasurableItem = new MeasurableItem(); + + child.width = 20; + child.setStyle("right", 5); + + var point:Point = layout.measure([child]); + Assert.assertEquals(25, point.x) + } + + [Test] + public function testMeasurementLeftRight():void { + var layout:BasicLayout = new BasicLayout(); + var child:MeasurableItem = new MeasurableItem(); + + child.width = 20; + child.setStyle("left", 5); + child.setStyle("right", 5); + + var point:Point = layout.measure([child]); + Assert.assertEquals(30, point.x) + } + + [Test] + public function testMeasurementTop():void { + var layout:BasicLayout = new BasicLayout(); + var child:MeasurableItem = new MeasurableItem(); + + child.height = 20; + child.setStyle("top", 5); + + var point:Point = layout.measure([child]); + Assert.assertEquals(25, point.y) + } + + [Test] + public function testMeasurementBottom():void { + var layout:BasicLayout = new BasicLayout(); + var child:MeasurableItem = new MeasurableItem(); + + child.height = 20; + child.setStyle("bottom", 5); + + var point:Point = layout.measure([child]); + Assert.assertEquals(25, point.y) + } + + [Test] + public function testMeasurementTopBottom():void { + var layout:BasicLayout = new BasicLayout(); + var child:MeasurableItem = new MeasurableItem(); + + child.height = 20; + child.setStyle("top", 5); + child.setStyle("bottom", 5); + + var point:Point = layout.measure([child]); + Assert.assertEquals(30, point.y) + } + + [Test] + public function testMeasurementHorizontalCenter():void { + var layout:BasicLayout = new BasicLayout(); + var child:MeasurableItem = new MeasurableItem(); + + child.width = 20; + child.setStyle("horizontalCenter", -5); + + var point:Point = layout.measure([child]); + Assert.assertEquals(25, point.x) + } + + [Test] + public function testMeasurementVerticalCenter():void { + var layout:BasicLayout = new BasicLayout(); + var child:MeasurableItem = new MeasurableItem(); + + child.height = 20; + child.setStyle("verticalCenter", -5); + + var point:Point = layout.measure([child]); + Assert.assertEquals(25, point.y) + } + + + // layout tests + + [Test] + public function testLeft():void { + var child:MeasurableItem = new MeasurableItem(); + var layout:BasicLayout = new BasicLayout(); + layout.target = this; + child.setStyle("left", 5); + layout.update([child], null, new Rectangle(0, 0, 100, 100)); + Assert.assertEquals(5, child.x); + } + + [Test] + public function testRight():void { + var child:MeasurableItem = new MeasurableItem(); + var layout:BasicLayout = new BasicLayout(); + layout.target = this; + child.width = 20; + child.setStyle("right", 5); + layout.update([child], null, new Rectangle(0, 0, 100, 100)); + Assert.assertEquals(75, child.x); + } + + [Test] + public function testLeftRight():void { + var child:MeasurableItem = new MeasurableItem(); + var layout:BasicLayout = new BasicLayout(); + layout.target = this; + child.setStyle("left", 5); + child.setStyle("right", 5); + layout.update([child], null, new Rectangle(0, 0, 100, 100)); + Assert.assertEquals(5, child.x); + Assert.assertEquals(90, child.width); + } + + [Test] + public function testTop():void { + var child:MeasurableItem = new MeasurableItem(); + var layout:BasicLayout = new BasicLayout(); + layout.target = this; + child.setStyle("top", 5); + layout.update([child], null, new Rectangle(0, 0, 100, 100)); + Assert.assertEquals(5, child.y); + } + + [Test] + public function testBottom():void { + var child:MeasurableItem = new MeasurableItem(); + var layout:BasicLayout = new BasicLayout(); + layout.target = this; + child.height = 20; + child.setStyle("bottom", 5); + layout.update([child], null, new Rectangle(0, 0, 100, 100)); + Assert.assertEquals(75, child.y); + } + + [Test] + public function testTopBottom():void { + var child:MeasurableItem = new MeasurableItem(); + var layout:BasicLayout = new BasicLayout(); + layout.target = this; + child.setStyle("top", 5); + child.setStyle("bottom", 5); + layout.update([child], null, new Rectangle(0, 0, 100, 100)); + Assert.assertEquals(5, child.y); + Assert.assertEquals(90, child.height); + } + + [Test] + public function testHorizontalCenter():void { + var child1:MeasurableItem = new MeasurableItem(); + var child2:MeasurableItem = new MeasurableItem(); + var layout:BasicLayout = new BasicLayout(); + layout.target = this; + child1.width = 20; + child2.width = 20; + child1.setStyle("horizontalCenter", 0); + child2.setStyle("horizontalCenter", -5); + layout.update([child1, child2], null, new Rectangle(0, 0, 100, 100)); + Assert.assertEquals(40, child1.x); + Assert.assertEquals(35, child2.x); + } + + [Test] + public function testVerticalCenter():void { + var child1:MeasurableItem = new MeasurableItem(); + var child2:MeasurableItem = new MeasurableItem(); + var layout:BasicLayout = new BasicLayout(); + layout.target = this; + child1.height = 20; + child2.height = 20; + child1.setStyle("verticalCenter", 0); + child2.setStyle("verticalCenter", -5); + layout.update([child1, child2], null, new Rectangle(0, 0, 100, 100)); + Assert.assertEquals(40, child1.y); + Assert.assertEquals(35, child2.y); + } + + [Test] + // percent-based layout test + public function testPercentLayout():void { + var child1:MeasurableItem = new MeasurableItem(); + child1.percentWidth = 50; + child1.percentHeight = 60; + + var child2:MeasurableItem = new MeasurableItem(); + child2.percentWidth = 70; + child2.percentHeight = 80; + + var layout:BasicLayout= new BasicLayout(); + layout.update([child1, child2], null, new Rectangle(0, 0, 100, 100)); + + Assert.assertEquals(50, child1.width); + Assert.assertEquals(60, child1.height); + Assert.assertEquals(70, child2.width); + Assert.assertEquals(80, child2.height); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/layouts/HorizontalLayoutTest.as b/tests/reflex/layouts/HorizontalLayoutTest.as new file mode 100644 index 00000000..504b2fad --- /dev/null +++ b/tests/reflex/layouts/HorizontalLayoutTest.as @@ -0,0 +1,102 @@ +package reflex.layouts +{ + import flash.events.EventDispatcher; + import flash.geom.Point; + import flash.geom.Rectangle; + + import org.flexunit.Assert; + + import reflex.display.MeasurableItem; + + + public class HorizontalLayoutTest extends EventDispatcher + { + + [Test] + // strait-forward measurement test + public function testMeasurement():void { + var child1:MeasurableItem = new MeasurableItem(); + var child2:MeasurableItem = new MeasurableItem(); + + child1.width = 20; + child1.height = 20; + + child2.width = 20; + child2.height = 20; + + var layout:HorizontalLayout = new HorizontalLayout(); + layout.gap = 10; + var point:Point = layout.measure([child1, child2]); + Assert.assertEquals(50, point.x); + Assert.assertEquals(20, point.y); + } + + // need to test percent-based measurement + + [Test] + // strait-forward layout test + public function testLayout():void { + var child1:MeasurableItem = new MeasurableItem(); + var child2:MeasurableItem = new MeasurableItem(); + + child1.width = 20; + child1.height = 20; + + child2.width = 20; + child2.height = 20; + + var rectangle:Rectangle = new Rectangle(0, 0, 100, 100); + var layout:HorizontalLayout = new HorizontalLayout(); + layout.gap = 10; + layout.update([child1, child2], null, rectangle); + Assert.assertEquals(0, child1.x); + Assert.assertEquals(30, child2.x); + } + + [Test] + // percent-based layout test + // width percentages are normalized but height percentages are not + public function testPercentLayout():void { + var child1:MeasurableItem = new MeasurableItem(); + child1.percentWidth = 100; + child1.percentHeight = 100; + + var child2:MeasurableItem = new MeasurableItem(); + child2.percentWidth = 100; + child2.percentHeight = 100; + + var layout:HorizontalLayout = new HorizontalLayout(); + layout.gap = 5; // gap spacing is removed from available width + layout.update([child1, child2], null, new Rectangle(0, 0, 100, 100)); + + Assert.assertEquals(48, child1.width); + Assert.assertEquals(100, child1.height); + Assert.assertEquals(48, child2.width); + Assert.assertEquals(100, child2.height); + } + + [Test] + // percent-based layout test + // width percentages are based on available space but height percentages are not + public function testAdjustedPercentLayout():void { + // adjust relative to 100% space + var child1:MeasurableItem = new MeasurableItem(); + child1.width = 20; + child1.height = 20; + + var child2:MeasurableItem = new MeasurableItem(); + child2.percentWidth = 100; + child2.percentHeight = 100; + + var layout:HorizontalLayout = new HorizontalLayout(); + layout.gap = 5; // gap spacing is removed from available width + layout.update([child1, child2], null, new Rectangle(0, 0, 100, 100)); + + Assert.assertEquals(20, child1.width); + Assert.assertEquals(20, child1.height); + Assert.assertEquals(75, child2.width); + Assert.assertEquals(100, child2.height); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/layouts/VerticalLayoutTest.as b/tests/reflex/layouts/VerticalLayoutTest.as new file mode 100644 index 00000000..efc940d5 --- /dev/null +++ b/tests/reflex/layouts/VerticalLayoutTest.as @@ -0,0 +1,101 @@ +package reflex.layouts +{ + import flash.events.EventDispatcher; + import flash.geom.Point; + import flash.geom.Rectangle; + + import org.flexunit.Assert; + + import reflex.display.MeasurableItem; + + public class VerticalLayoutTest extends EventDispatcher + { + + [Test] + // strait-forward measurement test + public function testMeasurement():void { + var child1:MeasurableItem = new MeasurableItem(); + var child2:MeasurableItem = new MeasurableItem(); + + child1.width = 20; + child1.height = 20; + + child2.width = 20; + child2.height = 20; + + var layout:VerticalLayout = new VerticalLayout(); + layout.gap = 10; + var point:Point = layout.measure([child1, child2]); + Assert.assertEquals(20, point.x); + Assert.assertEquals(50, point.y); + } + + // need to test percent-based measurement + + [Test] + // strait-forward layout test + public function testLayout():void { + var child1:MeasurableItem = new MeasurableItem(); + var child2:MeasurableItem = new MeasurableItem(); + + child1.width = 20; + child1.height = 20; + + child2.width = 20; + child2.height = 20; + + var rectangle:Rectangle = new Rectangle(0, 0, 100, 100); + var layout:VerticalLayout = new VerticalLayout(); + layout.gap = 10; + layout.update([child1, child2], null, rectangle); + Assert.assertEquals(0, child1.y); + Assert.assertEquals(30, child2.y); + } + + [Test] + // percent-based layout test + // height percentages are normalized but width percentages are not + public function testPercentLayout():void { + var child1:MeasurableItem = new MeasurableItem(); + child1.percentWidth = 100; + child1.percentHeight = 100; + + var child2:MeasurableItem = new MeasurableItem(); + child2.percentWidth = 100; + child2.percentHeight = 100; + + var layout:VerticalLayout = new VerticalLayout(); + layout.gap = 5; // gap spacing is removed from available height + layout.update([child1, child2], null, new Rectangle(0, 0, 100, 100)); + + Assert.assertEquals(100, child1.width); + Assert.assertEquals(48, child1.height); + Assert.assertEquals(100, child2.width); + Assert.assertEquals(48, child2.height); + } + + [Test] + // percent-based layout test + // height percentages are based on available space but width percentages are not + public function testAdjustedPercentLayout():void { + // adjust relative to 100% space + var child1:MeasurableItem = new MeasurableItem(); + child1.width = 20; + child1.height = 20; + + var child2:MeasurableItem = new MeasurableItem(); + child2.percentWidth = 100; + child2.percentHeight = 100; + + var layout:VerticalLayout = new VerticalLayout(); + layout.gap = 5; // gap spacing is removed from available height + layout.update([child1, child2], null, new Rectangle(0, 0, 100, 100)); + + Assert.assertEquals(20, child1.width); + Assert.assertEquals(20, child1.height); + Assert.assertEquals(100, child2.width); + Assert.assertEquals(75, child2.height); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/layouts/XYLayoutTest.as b/tests/reflex/layouts/XYLayoutTest.as new file mode 100644 index 00000000..dba1bafb --- /dev/null +++ b/tests/reflex/layouts/XYLayoutTest.as @@ -0,0 +1,36 @@ +package reflex.layouts +{ + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + import flash.geom.Point; + + import org.flexunit.Assert; + + import reflex.display.MeasurableItem; + + public class XYLayoutTest extends EventDispatcher + { + + [Test] + public function testMeasurement():void { + var child1:MeasurableItem = new MeasurableItem(); + var child2:MeasurableItem = new MeasurableItem(); + + child1.x = 5; + child1.y = 5; + child1.width = 20; + child1.height = 20; + + child2.x = 10; + child2.y = 10; + child2.width = 20; + child2.height = 20; + + var layout:XYLayout = new XYLayout(); + var point:Point = layout.measure([child1, child2]); + Assert.assertEquals(30, point.x); + Assert.assertEquals(30, point.y); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/measurement/MeasurableTestBase.as b/tests/reflex/measurement/MeasurableTestBase.as new file mode 100644 index 00000000..1af6141b --- /dev/null +++ b/tests/reflex/measurement/MeasurableTestBase.as @@ -0,0 +1,143 @@ +package reflex.measurement +{ + + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + + import org.flexunit.Assert; + import org.flexunit.async.Async; + + import reflex.tests.BaseClass; + + + public class MeasurableTestBase extends BaseClass + { + + public var C:Class; + + [Test(order=-1)] + public function testIMeasurable():void { + var instance:Object = new C(); + Assert.assertTrue(instance is IMeasurable); + Assert.assertTrue(instance is IEventDispatcher); + } + + [Test] + public function testDefaultSize():void { + var instance:IMeasurable = new C(); + //Assert.assertNotNull(instance.explicit); + //Assert.assertNotNull(instance.measured); + Assert.assertFalse(isNaN(instance.measuredWidth)); + Assert.assertFalse(isNaN(instance.measuredHeight)); + Assert.assertTrue(isNaN(instance.explicitWidth)); + Assert.assertTrue(isNaN(instance.explicitHeight)); + } + + [Test(async)] + public function testWidthChange():void { + testPropertyChange(C, "width", 100); + } + + [Test(async)] + public function testWidthNotChanged():void { + testPropertyNotChanged(C, "width", 100); + } + + [Test(async)] + public function testHeightChange():void { + testPropertyChange(C, "height", 100); + } + + [Test(async)] + public function testHeightNotChanged():void { + testPropertyNotChanged(C, "height", 100); + } + + [Test] + public function testexplicitWidth():void { + var instance:IMeasurable = new C() as IMeasurable; + instance.width = 100; + Assert.assertEquals(100, instance.width); + Assert.assertEquals(100, instance.explicitWidth); + } + + [Test] + public function testexplicitHeight():void { + var instance:IMeasurable = new C() as IMeasurable; + instance.height = 100; + Assert.assertEquals(100, instance.height); + Assert.assertEquals(100, instance.explicitHeight); + } + /* + [Test(async)] + public function testMeasuredWidth():void { + var instance:IMeasurable = new C() as IMeasurable; + var listener:Function = Async.asyncHandler(this, changeHandler, 500, "widthChange", timeoutHandler); + (instance as IEventDispatcher).addEventListener("widthChange", listener, false, 0, false); + + instance.measuredWidth = 100; + Assert.assertEquals(100, instance.width); + Assert.assertEquals(100, instance.measuredWidth); + } + + [Test(async)] + public function testMeasuredHeight():void { + var instance:IMeasurable = new C() as IMeasurable; + var listener:Function = Async.asyncHandler(this, changeHandler, 500, "heightChange", timeoutHandler); + (instance as IEventDispatcher).addEventListener("heightChange", listener, false, 0, false); + + instance.measuredHeight = 100; + Assert.assertEquals(100, instance.height); + Assert.assertEquals(100, instance.measuredHeight); + } + + [Test(async)] + public function testMeasuredWidthEvent():void { + var instance:IMeasurable = new C() as IMeasurable; + instance.width = 100; // there should be no change event for measured changes now + Async.failOnEvent(this, instance as IEventDispatcher, "widthChange", 500); + + instance.measuredWidth = 5; + Assert.assertEquals(100, instance.width); + Assert.assertEquals(100, instance.explicitWidth); + } + + [Test(async)] + public function testMeasuredHeightEvent():void { + var instance:IMeasurable = new C() as IMeasurable; + instance.height = 100; // there should be no change event for measured changes now + Async.failOnEvent(this, instance as IEventDispatcher, "heightChange", 500); + + instance.measured.height = 5; + Assert.assertEquals(100, instance.height); + Assert.assertEquals(100, instance.explicitHeight); + } + */ + [Test(async)] // binding events should fire for width/height changes, but explicit/measured should not be updated + public function testSetSize():void { + var instance:IMeasurable = new C() as IMeasurable; + var widthListener:Function = Async.asyncHandler(this, changeHandler, 500, "widthChange", timeoutHandler); + var heightListener:Function = Async.asyncHandler(this, changeHandler, 500, "heightChange", timeoutHandler); + (instance as IEventDispatcher).addEventListener("widthChange", widthListener, false, 0, false); + (instance as IEventDispatcher).addEventListener("heightChange", heightListener, false, 0, false); + + instance.setSize(100, 100); + Assert.assertEquals(100, instance.width); + Assert.assertEquals(100, instance.height); + Assert.assertFalse(instance.explicitWidth == 100); + Assert.assertFalse(instance.explicitHeight == 100); + } + + [Test(async)] // no events should fire if size hasn't changed + public function testSetSizeNotChanged():void { + var instance:IMeasurable = new C() as IMeasurable; + instance.setSize(100, 100); + Async.failOnEvent(this, instance as IEventDispatcher, "widthChange", 500, timeoutHandler); + Async.failOnEvent(this, instance as IEventDispatcher, "heightChange", 500, timeoutHandler); + instance.setSize(100, 100); + } + + + } +} \ No newline at end of file diff --git a/tests/reflex/measurement/MeasurementFunctionsTest.as b/tests/reflex/measurement/MeasurementFunctionsTest.as new file mode 100644 index 00000000..9a042fd7 --- /dev/null +++ b/tests/reflex/measurement/MeasurementFunctionsTest.as @@ -0,0 +1,86 @@ +package reflex.measurement +{ + import mx.core.UIComponent; + + import org.flexunit.Assert; + + import reflex.display.MeasurableItem; + + public class MeasurementFunctionsTest + { + + [Test] + public function testResolveWidthObject():void { + var object:Object = {width: 100}; + var v:Number = reflex.measurement.resolveWidth(object); + Assert.assertEquals(100, v); + } + /* + [Test] + public function testResolveWidthMeasured():void { + var instance:IMeasurable = new MeasurableItem(); + instance.measuredWidth = 100; + var v:Number = reflex.measurement.resolveWidth(instance); + Assert.assertEquals(100, v); + new UIComponent + } + + [Test] + public function testResolveWidthexplicit():void { + var instance:IMeasurable = new MeasurableItem(); + instance.measuredWidth = 5; + instance.explicitWidth = 100; + var v:Number = reflex.measurement.resolveWidth(instance); + Assert.assertEquals(100, v); + } + + [Test] + public function testResolveHeightObject():void { + var object:Object = {height: 100}; + var v:Number = reflex.measurement.resolveHeight(object); + Assert.assertEquals(100, v); + } + + [Test] + public function testResolveHeightMeasured():void { + var instance:IMeasurable = new MeasurableItem(); + instance.measuredHeight = 100; + var v:Number = reflex.measurement.resolveHeight(instance); + Assert.assertEquals(100, v); + } + + [Test] + public function testResolveHeightexplicit():void { + var instance:IMeasurable = new MeasurableItem(); + instance.measuredHeight= 5; + instance.explicitHeight = 100; + var v:Number = reflex.measurement.resolveHeight(instance); + Assert.assertEquals(100, v); + } + + [Test] + public function testSetSizeObject():void { + var object:Object = {}; + reflex.measurement.setSize(object, 100, 100); + Assert.assertEquals(100, object.width); + Assert.assertEquals(100, object.height); + } + + [Test] + public function testSetSizeMeasurable():void { + var instance:IMeasurable = new MeasurableItem(); + instance.measuredWidth = 5; + instance.measuredHeight = 5; + instance.explicitWidth = 5; + instance.explicitHeight = 5; + reflex.measurement.setSize(instance, 100, 100); + Assert.assertEquals(100, instance.width); + Assert.assertEquals(100, instance.height); + Assert.assertEquals(5, instance.measuredWidth); + Assert.assertEquals(5, instance.measuredHeight); + Assert.assertEquals(5, instance.explicitWidth); + Assert.assertEquals(5, instance.explicitHeight); + } + */ + } +} \ No newline at end of file diff --git a/tests/reflex/skins/SkinContainerTest.as b/tests/reflex/skins/SkinContainerTest.as new file mode 100644 index 00000000..32936046 --- /dev/null +++ b/tests/reflex/skins/SkinContainerTest.as @@ -0,0 +1,24 @@ +package reflex.skins +{ + import reflex.display.StatefulContainerTestBase; + + public class SkinContainerTest extends StatefulContainerTestBase + { + public function SkinContainerTest() + { + super(); + C = Skin; + } + + [Test(async)] + public function testTemplateChange():void { + testPropertyChange(C, "template", {}); + } + + [Test(async)] + public function testTemplateNotChanged():void { + testPropertyChange(C, "template", {}); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/skins/SkinMeasurementTest.as b/tests/reflex/skins/SkinMeasurementTest.as new file mode 100644 index 00000000..0cda1b1d --- /dev/null +++ b/tests/reflex/skins/SkinMeasurementTest.as @@ -0,0 +1,15 @@ +package reflex.skins +{ + import reflex.measurement.MeasurableTestBase; + + public class SkinMeasurementTest extends MeasurableTestBase + { + + public function SkinMeasurementTest() + { + super(); + C = Skin; + } + + } +} \ No newline at end of file diff --git a/tests/reflex/styles/StyleFunctionsTest.as b/tests/reflex/styles/StyleFunctionsTest.as new file mode 100644 index 00000000..25b22a50 --- /dev/null +++ b/tests/reflex/styles/StyleFunctionsTest.as @@ -0,0 +1,33 @@ +package reflex.styles +{ + import org.flexunit.Assert; + + import reflex.display.MeasurableItem; + + public class StyleFunctionsTest + { + + [Test] + public function testHasStyle():void { + var instance:IStyleable = new MeasurableItem(); + instance.setStyle("testStyle", "test"); + Assert.assertTrue(reflex.styles.hasStyle(instance, "testStyle")); + } + + [Test] + public function testResolveStyle():void { + var instance:IStyleable = new MeasurableItem(); + instance.setStyle("testStyle", "test"); + var v:* = reflex.styles.resolveStyle(instance, "testStyle"); + Assert.assertEquals("test", v); + } + + [Test] + public function testResolveStyleStandard():void { + var instance:IStyleable = new MeasurableItem(); + var v:* = reflex.styles.resolveStyle(instance, "testStyle", null, "test"); + Assert.assertEquals("test", v); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/styles/StyleableMeasurableMeasurablePercentTestBase.as b/tests/reflex/styles/StyleableMeasurableMeasurablePercentTestBase.as new file mode 100644 index 00000000..a960492b --- /dev/null +++ b/tests/reflex/styles/StyleableMeasurableMeasurablePercentTestBase.as @@ -0,0 +1,57 @@ +package reflex.styles { + import flash.events.IEventDispatcher; + + import flexunit.framework.Assert; + + import reflex.display.DisplayMeasurableTest; + + public class StyleableMeasurableMeasurablePercentTestBase extends DisplayMeasurableTest { + + public function StyleableMeasurableMeasurablePercentTestBase() { + super(); + } + + [Test(order=-1)] + public function testIStyleable():void { + var instance:Object = new C(); + Assert.assertTrue(instance is IStyleable); + Assert.assertTrue(instance is IEventDispatcher); + } + + [Test(async)] + public function testIdChange():void { + testPropertyChange(C, "id", "test"); + } + + [Test(async)] + public function testIdNotChanged():void { + testPropertyNotChanged(C, "id", "test"); + } + + [Test(async)] + public function testStyleNameChange():void { + testPropertyChange(C, "styleName", "test"); + } + + [Test(async)] + public function testStyleNameNotChanged():void { + testPropertyChange(C, "styleName", "test"); + } + + [Test] + public function testGetStyle():void { + var instance:IStyleable = new C(); + instance.setStyle("testStyle", "test"); + var v:Object = instance.getStyle("testStyle"); + Assert.assertEquals("test", v); + } + + [Test] + public function testSetStyle():void { + var instance:IStyleable = new C(); + instance.setStyle("testStyle", "test"); + var v:Object = instance.getStyle("testStyle"); + Assert.assertEquals("test", v); + } + } +} \ No newline at end of file diff --git a/tests/reflex/styles/StyleableTestBase.as b/tests/reflex/styles/StyleableTestBase.as new file mode 100644 index 00000000..4db3424f --- /dev/null +++ b/tests/reflex/styles/StyleableTestBase.as @@ -0,0 +1,60 @@ +package reflex.styles +{ + import flash.events.Event; + import flash.events.IEventDispatcher; + + import org.flexunit.Assert; + import org.flexunit.async.Async; + + import reflex.tests.BaseClass; + + public class StyleableTestBase extends BaseClass + { + + public var C:Class; + + [Test(order=-1)] + public function testIStyleable():void { + var instance:Object = new C(); + Assert.assertTrue(instance is IStyleable); + Assert.assertTrue(instance is IEventDispatcher); + } + + [Test(async)] + public function testIdChange():void { + testPropertyChange(C, "id", "test"); + } + + [Test(async)] + public function testIdNotChanged():void { + testPropertyNotChanged(C, "id", "test"); + } + + [Test(async)] + public function testStyleNameChange():void { + testPropertyChange(C, "styleName", "test"); + } + + [Test(async)] + public function testStyleNameNotChanged():void { + testPropertyChange(C, "styleName", "test"); + } + + [Test] + public function testGetStyle():void { + var instance:IStyleable = new C(); + instance.setStyle("testStyle", "test"); + var v:Object = instance.getStyle("testStyle"); + Assert.assertEquals("test", v); + } + + [Test] + public function testSetStyle():void { + var instance:IStyleable = new C(); + instance.setStyle("testStyle", "test"); + var v:Object = instance.getStyle("testStyle"); + Assert.assertEquals("test", v); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/tests/BaseClass.as b/tests/reflex/tests/BaseClass.as new file mode 100644 index 00000000..cb7fb4b0 --- /dev/null +++ b/tests/reflex/tests/BaseClass.as @@ -0,0 +1,40 @@ +package reflex.tests +{ + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + + import org.flexunit.Assert; + import org.flexunit.async.Async; + + public class BaseClass extends EventDispatcher + { + + // tests that the appropriate PropertyChange + protected function testPropertyChange(C:Class, property:String, value:*):Object { + var instance:Object = new C(); + var listener:Function = Async.asyncHandler(this, changeHandler, 500, property + "Change", timeoutHandler); + (instance as IEventDispatcher).addEventListener(property + "Change", listener, false, 0, false); + instance[property] = value; + Assert.assertEquals(value, instance[property]); + return instance; + } + + protected function testPropertyNotChanged(C:Class, property:String, value:*):Object { + var instance:Object = new C(); + instance[property] = value; + Async.failOnEvent(this, instance as IEventDispatcher, property + "Change", 500, timeoutHandler); + instance[property] = value; + return instance; + } + + protected function changeHandler(event:Event, type:String):void { + Assert.assertEquals(event.type, type); + } + + protected function timeoutHandler(type:String):void { + Assert.fail(type + ": timed out."); + } + + } +} \ No newline at end of file diff --git a/tests/reflex/text/LabelTest.as b/tests/reflex/text/LabelTest.as new file mode 100644 index 00000000..70877f5c --- /dev/null +++ b/tests/reflex/text/LabelTest.as @@ -0,0 +1,23 @@ +package reflex.text +{ + + import org.flexunit.async.Async; + import reflex.tests.BaseClass; + + public class LabelTest extends BaseClass + { + + // validation doesn't occur off the DisplayList + // we'll need to fix Invalidation first + + [Test(async)] [Ignore]// text changes should trigger measurement + public function testMeasurementInvalidation():void { + var label:Label = new Label("x"); + var listener:Function = Async.asyncHandler(this, changeHandler, 500, "widthChange", timeoutHandler); + label.addEventListener("widthChange", listener, false, 0, false); + label.text = "test"; + } + + + } +} \ No newline at end of file