////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.states
{
import mx.core.UIComponent;
import mx.core.IDeferredInstance;
import mx.styles.IStyleClient;
import mx.styles.StyleManager;
import mx.core.IFlexModule;
import mx.styles.IStyleManager2;
/**
* The SetStyle class specifies a style that is in effect only during the parent view state.
* You use this class in the overrides
property of the State class.
*
* @mxml
*
*
The <mx:SetStyle>
tag
* has the following attributes:
* <mx:SetStyle * Properties * name="null" * target="null" * value"null" * /> ** * @see mx.states.State * @see mx.states.SetEventHandler * @see mx.states.SetProperty * @see mx.effects.SetStyleAction * * @includeExample examples/StatesExample.mxml * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class SetStyle extends OverrideBase { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class constants // //-------------------------------------------------------------------------- /** * @private * This is a table of related properties. * Whenever the property being overridden is found in this table, * the related property is also saved and restored. */ private static const RELATED_PROPERTIES:Object = { left: [ "x" ], top: [ "y" ], right: [ "x" ], bottom: [ "y" ] }; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @param target The object whose style is being set. * By default, Flex uses the immediate parent of the State object. * * @param name The style to set. * * @param value The value of the style in the view state. * * @param valueFactory An optional write-only property from which to obtain * a shared value. This is primarily used when this override's value is * shared by multiple states or state groups. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function SetStyle( target:IStyleClient = null, name:String = null, value:Object = null, valueFactory:IDeferredInstance = null ) { super(); this.target = target; this.name = name; this.value = value; this.valueFactory = valueFactory; } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * Storage for the old style value. */ private var oldValue:Object; /** * @private * True if old value was set as an inline style. */ private var wasInline:Boolean; /** * @private * Storage for the old related property values, if used. */ private var oldRelatedValues:Array; //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // name //---------------------------------- [Inspectable(category="General")] /** * * The name of the style to change. * You must set this property, either in * the SetStyle constructor or by setting * the property value directly. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var name:String; //---------------------------------- // target //---------------------------------- [Inspectable(category="General")] /** * * The object whose style is being changed. * If the property value is
null
, Flex uses the
* immediate parent of the State object.
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var target:Object;
/**
* The cached target for which we applied our override.
* We keep track of the applied target while applied since
* our target may be swapped out in the owning document and
* we want to make sure we roll back the correct (original)
* element.
*
* @private
*/
private var appliedTarget:Object;
//----------------------------------
// value
//----------------------------------
[Inspectable(category="General")]
/**
* @private
* Storage for the style value.
*/
public var _value:Object;
/**
* The new value for the style.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get value():Object
{
return _value;
}
/**
* @private
*/
public function set value(val:Object):void
{
_value = val;
// Reapply if necessary.
if (applied)
{
apply(parentContext);
}
}
//----------------------------------
// valueFactory
//----------------------------------
/**
* An optional write-only property from which to obtain a shared value. This
* is primarily used when this override's value is shared by multiple states
* or state groups.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 4
*/
public function set valueFactory(factory:IDeferredInstance):void
{
// We instantiate immediately in order to retain the instantiation
// behavior of a typical (unshared) value. We may later enhance to
// allow for deferred instantiation.
if (factory)
value = factory.getInstance();
}
//--------------------------------------------------------------------------
//
// IOverride methods
//
//--------------------------------------------------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function apply(parent:UIComponent):void
{
parentContext = parent;
var context:Object = getOverrideContext(target, parent);
if (context != null)
{
appliedTarget = context;
var obj:IStyleClient = IStyleClient(appliedTarget);
var relatedProps:Array = RELATED_PROPERTIES[name] ?
RELATED_PROPERTIES[name] :
null;
// Remember the original value so it can be restored later
// after we are asked to remove our override (and only if we
// aren't being asked to re-apply a value).
if (!applied)
{
wasInline = obj.styleDeclaration &&
obj.styleDeclaration.getStyle(name) !== undefined;
oldValue = wasInline ? obj.getStyle(name) : null;
}
if (relatedProps)
{
oldRelatedValues = [];
for (var i:int = 0; i < relatedProps.length; i++)
oldRelatedValues[i] = obj[relatedProps[i]];
}
// Set new value
if (value === null)
{
obj.clearStyle(name);
}
else if (oldValue is Number)
{
// The "value" for colors can be several different formats:
// 0xNNNNNN, #NNNNNN or "red". We can't use
// StyleManager.isColorStyle() because that only returns true
// for inheriting color styles and misses non-inheriting styles like
// backgroundColor.
if (name.toLowerCase().indexOf("color") != -1)
{
var styleManager:IStyleManager2;
if (obj is UIComponent)
styleManager = UIComponent(obj).styleManager;
else
styleManager = parent.styleManager;
obj.setStyle(name, styleManager.getColorName(value));
}
else if (value is String &&
String(value).lastIndexOf("%") ==
String(value).length - 1)
{
obj.setStyle(name, value);
}
else
{
obj.setStyle(name, Number(value));
}
}
else if (oldValue is Boolean)
{
obj.setStyle(name, toBoolean(value));
}
else
{
obj.setStyle(name, value);
}
// Disable bindings for the base style if appropriate. If the binding
// fires while our override is applied, the correct value will automatically
// be applied when the binding is later enabled.
enableBindings(obj, parent, name, false);
}
else if (!applied)
{
// Our target context is unavailable so we attempt to register
// a listener on our parent document to detect when/if it becomes
// valid.
addContextListener(target);
}
// Save state in case our value or target is changed while applied. This
// can occur when our value property is databound or when a target is
// deferred instantiated.
applied = true;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function remove(parent:UIComponent):void
{
var obj:IStyleClient = IStyleClient(getOverrideContext(appliedTarget, parent));
if (obj != null && appliedTarget)
{
if (wasInline)
{
// Restore the old value
if (oldValue is Number)
obj.setStyle(name, Number(oldValue));
else if (oldValue is Boolean)
obj.setStyle(name, toBoolean(oldValue));
else if (oldValue === null)
obj.clearStyle(name);
else
obj.setStyle(name, oldValue);
}
else
{
obj.clearStyle(name);
}
// Re-enable bindings for the base style if appropriate. If the binding
// fired while our override was applied, the current value will automatically
// be applied once enabled.
enableBindings(obj, parent, name);
var relatedProps:Array = RELATED_PROPERTIES[name] ?
RELATED_PROPERTIES[name] :
null;
// Restore related property values, if needed
if (relatedProps)
{
for (var i:int = 0; i < relatedProps.length; i++)
{
obj[relatedProps[i]] = oldRelatedValues[i];
}
}
}
else
{
// It seems our override is no longer active, but we were never
// able to successfully apply ourselves, so remove our context
// listener if applicable.
removeContextListener();
}
// Clear our flags and override context.
applied = false;
parentContext = null;
appliedTarget = null;
}
/**
* @private
* Converts a value to a Boolean true/false.
*/
private function toBoolean(value:Object):Boolean
{
if (value is String)
return value.toLowerCase() == "true";
return value != false;
}
}
}