This monkey patches StyleProtoChain to make it MUCH faster in Flex 4. Learn more: http://taytay.com/?p=169
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//////////////////////////////////////////////////////////////////////////////// | |
// | |
// 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.styles | |
{ | |
import flash.display.DisplayObject; | |
import flash.display.DisplayObjectContainer; | |
import flash.system.ApplicationDomain; | |
import flash.utils.Dictionary; | |
import flash.utils.getQualifiedClassName; | |
import flash.utils.getQualifiedSuperclassName; | |
import flash.utils.getTimer; | |
import mx.core.FlexGlobals; | |
import mx.core.IFlexDisplayObject; | |
import mx.core.IFlexModule; | |
import mx.core.IFlexModuleFactory; | |
import mx.core.IFontContextComponent; | |
import mx.core.IInvalidating; | |
import mx.core.IUITextField; | |
import mx.core.IVisualElement; | |
import mx.core.UIComponent; | |
import mx.core.mx_internal; | |
import mx.effects.EffectManager; | |
import mx.managers.SystemManager; | |
import mx.modules.IModule; | |
import mx.modules.ModuleManager; | |
import mx.utils.NameUtil; | |
import mx.utils.OrderedObject; | |
import mx.utils.object_proxy; | |
use namespace mx_internal; | |
use namespace object_proxy; | |
[ExcludeClass] | |
/** | |
* @private | |
* This is an all-static class with methods for building the protochains | |
* that Flex uses to look up CSS style properties. | |
*/ | |
public class StyleProtoChain | |
{ | |
include "../core/Version.as"; | |
//-------------------------------------------------------------------------- | |
// | |
// Class constants | |
// | |
//-------------------------------------------------------------------------- | |
/** | |
* @private | |
* The inheritingStyles and nonInheritingStyles properties | |
* are initialized to this empty Object. | |
* This allows the getStyle() and getStyle() | |
* methods to simply access inheritingStyles[] and nonInheritingStyles[] | |
* without needing to first check whether those objects exist. | |
* If they were simply initialized to {}, we couldn't determine | |
* whether the style chain has already been built or not. | |
*/ | |
public static var STYLE_UNINITIALIZED:Object = {}; | |
//-------------------------------------------------------------------------- | |
// | |
// Class methods | |
// | |
//-------------------------------------------------------------------------- | |
/** | |
* @private | |
* Implements the getClassStyleDeclarations() logic | |
* for UIComponent and TextBase. | |
* The 'object' parameter will be one or the other. | |
*/ | |
public static function getClassStyleDeclarations(object:IStyleClient):Array | |
{ | |
var styleManager:IStyleManager2 = getStyleManager(object); | |
var qualified:Boolean = styleManager.qualifiedTypeSelectors; | |
var className:String = qualified ? getQualifiedClassName(object) : object.className; | |
var advancedObject:IAdvancedStyleClient = object as IAdvancedStyleClient; | |
var typeHierarchy:OrderedObject = getTypeHierarchy(object, qualified); | |
var types:Array = typeHierarchy.propertyList; | |
var typeCount:int = types.length; | |
var classDecls:Array = null; | |
if (!styleManager.hasAdvancedSelectors()) | |
{ | |
classDecls = styleManager.typeSelectorCache[className]; | |
if (classDecls) | |
return classDecls; | |
} | |
classDecls = []; | |
// Loop over the type hierarhcy starting at the base type and work | |
// down the chain of subclasses. | |
for (var i:int = typeCount - 1; i >= 0; i--) | |
{ | |
var type:String = types[i].toString(); | |
if (styleManager.hasAdvancedSelectors() && advancedObject != null) | |
{ | |
var decls:Array = styleManager.getStyleDeclarations(type); | |
if (decls) | |
{ | |
var matchingDecls:Array = matchStyleDeclarations(decls, advancedObject); | |
classDecls = classDecls.concat(matchingDecls); | |
} | |
} | |
else | |
{ | |
var decl:CSSStyleDeclaration = styleManager.getMergedStyleDeclaration(type); | |
if (decl) | |
classDecls.push(decl); | |
} | |
} | |
if (styleManager.hasAdvancedSelectors() && advancedObject != null) | |
{ | |
// Advanced selectors may result in more than one match per type so | |
// we sort based on specificity, but we preserve the declaration | |
// order for equal selectors. | |
classDecls = sortOnSpecificity(classDecls); | |
} | |
else | |
{ | |
// Cache the simple type declarations for this class | |
styleManager.typeSelectorCache[className] = classDecls; | |
} | |
return classDecls; | |
} | |
/** | |
* @private | |
* Implements the initProtoChain() logic for UIComponent and TextBase. | |
* The 'object' parameter will be one or the other. | |
*/ | |
public static function initProtoChain(object:IStyleClient):void | |
{ | |
var styleManager:IStyleManager2 = getStyleManager(object); | |
var n:int; | |
var i:int; | |
var uicObject:UIComponent = object as UIComponent; | |
var advancedObject:IAdvancedStyleClient = object as IAdvancedStyleClient; | |
var styleDeclaration:CSSStyleDeclaration = null; | |
var universalSelectors:Array = []; | |
var hasStyleName:Boolean = false; | |
var styleName:Object = object.styleName; | |
if (styleName) | |
{ | |
if (styleName is CSSStyleDeclaration) | |
{ | |
// Get the styles referenced by the styleName property. | |
universalSelectors.push(CSSStyleDeclaration(styleName)); | |
} | |
else if (styleName is IFlexDisplayObject || styleName is IStyleClient) | |
{ | |
// If the styleName property is a UIComponent, then there's a | |
// special search path for that case. | |
StyleProtoChain.initProtoChainForUIComponentStyleName(object); | |
return; | |
} | |
else if (styleName is String) | |
{ | |
hasStyleName = true; | |
} | |
} | |
// To build the proto chain, we start at the end and work forward. | |
// Referring to the list at the top of this function, we'll start | |
// by getting the tail of the proto chain, which is: | |
// - for non-inheriting styles, the global style sheet | |
// - for inheriting styles, my parent's style object | |
var nonInheritChain:Object = styleManager.stylesRoot; | |
if (nonInheritChain && nonInheritChain.effects) | |
object.registerEffects(nonInheritChain.effects); | |
var p:IStyleClient = null; | |
if (object is IVisualElement) | |
p = IVisualElement(object).parent as IStyleClient; | |
if (p) | |
{ | |
var inheritChain:Object = p.inheritingStyles; | |
if (inheritChain == StyleProtoChain.STYLE_UNINITIALIZED) | |
inheritChain = nonInheritChain; | |
// If this object is a module then add its global styles to the | |
// inheritChain. If we don't have global styles in this style manager | |
// then the user didn't declare a global style in the module and the | |
// compiler didn't add a duplicate default style. In that case don't | |
// add global styles to the chain because the parent style manager's | |
// global styles are already on the chain. | |
if (object is IModule) | |
{ | |
styleDeclaration = styleManager.getStyleDeclaration("global"); | |
if (styleDeclaration) | |
inheritChain = styleDeclaration.addStyleToProtoChain(inheritChain, DisplayObject(object)); | |
} | |
} | |
else | |
{ | |
// Pop ups inheriting chain starts at Application instead of global. | |
// This allows popups to grab styles like themeColor that are | |
// set on Application. | |
if (uicObject && uicObject.isPopUp) | |
{ | |
var owner:DisplayObjectContainer = uicObject._owner; | |
if (owner && owner is IStyleClient) | |
{ | |
inheritChain = IStyleClient(owner).inheritingStyles; | |
} | |
else | |
{ | |
inheritChain = FlexGlobals.topLevelApplication.inheritingStyles; | |
} | |
} | |
else | |
{ | |
inheritChain = styleManager.stylesRoot; | |
} | |
} | |
var styleDeclarations:Array = null; | |
// If we have an advanced style client, we handle this separately | |
// because of the considerably more complex selector matches... | |
if (styleManager.hasAdvancedSelectors() && advancedObject != null) | |
{ | |
styleDeclarations = getMatchingStyleDeclarations(advancedObject, universalSelectors); | |
n = styleDeclarations != null ? styleDeclarations.length : 0; | |
for (i = 0; i < n; i++) | |
{ | |
styleDeclaration = styleDeclarations[i]; | |
inheritChain = styleDeclaration.addStyleToProtoChain(inheritChain, uicObject); | |
nonInheritChain = styleDeclaration.addStyleToProtoChain(nonInheritChain, uicObject); | |
if (styleDeclaration.effects) | |
advancedObject.registerEffects(styleDeclaration.effects); | |
} | |
} | |
// Otherwise we use the legacy Flex 3 logic for simple selectors. | |
else | |
{ | |
// Get the styles referenced by the styleName property | |
if (hasStyleName) | |
{ | |
var styleNames:Array = styleName.split(/\s+/); | |
n = styleNames.length; | |
for (i = 0; i < n; i++) | |
{ | |
if (styleNames[i].length) | |
{ | |
styleDeclaration = styleManager.getMergedStyleDeclaration("." + styleNames[i]); | |
if (styleDeclaration) | |
universalSelectors.push(styleDeclaration); | |
} | |
} | |
} | |
// Working backwards up the list, the next element in the | |
// search path is the type selector | |
styleDeclarations = object.getClassStyleDeclarations(); | |
n = styleDeclarations != null ? styleDeclarations.length : 0; | |
for (i = 0; i < n; i++) | |
{ | |
styleDeclaration = styleDeclarations[i]; | |
inheritChain = styleDeclaration.addStyleToProtoChain(inheritChain, uicObject); | |
nonInheritChain = styleDeclaration.addStyleToProtoChain(nonInheritChain, uicObject); | |
if (styleDeclaration.effects) | |
object.registerEffects(styleDeclaration.effects); | |
} | |
// Next are the class selectors | |
n = universalSelectors.length; | |
for (i = 0; i < n; i++) | |
{ | |
styleDeclaration = universalSelectors[i]; | |
if (styleDeclaration) | |
{ | |
inheritChain = | |
styleDeclaration.addStyleToProtoChain(inheritChain, uicObject); | |
nonInheritChain = | |
styleDeclaration.addStyleToProtoChain(nonInheritChain, uicObject); | |
if (styleDeclaration.effects) | |
object.registerEffects(styleDeclaration.effects); | |
} | |
} | |
} | |
// Finally, we'll add the in-line styles | |
// to the head of the proto chain. | |
styleDeclaration = object.styleDeclaration; | |
object.inheritingStyles = | |
styleDeclaration ? | |
styleDeclaration.addStyleToProtoChain(inheritChain, uicObject) : | |
inheritChain; | |
object.nonInheritingStyles = | |
styleDeclaration ? | |
styleDeclaration.addStyleToProtoChain(nonInheritChain, uicObject) : | |
nonInheritChain; | |
} | |
/** | |
* @private | |
* If the styleName property points to a UIComponent, then we search | |
* for stylable properties in the following order: | |
* | |
* 1) Look for inline styles on this object | |
* 2) Look for inline styles on the styleName object | |
* 3) Look for class selectors on the styleName object | |
* 4) Look for type selectors on the styleName object | |
* 5) Look for type selectors on this object | |
* 6) Follow the usual search path for the styleName object | |
* | |
* If this object doesn't have any type selectors, then the | |
* search path can be simplified to two steps: | |
* | |
* 1) Look for inline styles on this object | |
* 2) Follow the usual search path for the styleName object | |
*/ | |
public static function initProtoChainForUIComponentStyleName( | |
obj:IStyleClient):void | |
{ | |
var styleManager:IStyleManager2 = getStyleManager(obj); | |
var styleName:IStyleClient = IStyleClient(obj.styleName); | |
var target:DisplayObject = obj as DisplayObject; | |
// Push items onto the proto chain in reverse order, beginning with | |
// 6) Follow the usual search path for the styleName object | |
var nonInheritChain:Object = styleName.nonInheritingStyles; | |
if (!nonInheritChain || | |
nonInheritChain == StyleProtoChain.STYLE_UNINITIALIZED) | |
{ | |
nonInheritChain = styleManager.stylesRoot; | |
if (nonInheritChain.effects) | |
obj.registerEffects(nonInheritChain.effects); | |
} | |
var inheritChain:Object = styleName.inheritingStyles; | |
if (!inheritChain || | |
inheritChain == StyleProtoChain.STYLE_UNINITIALIZED) | |
{ | |
inheritChain = styleManager.stylesRoot; | |
} | |
// If there's no type selector on this object, then we can collapse | |
// 6 steps to 2 (see above) | |
var typeSelectors:Array = obj.getClassStyleDeclarations(); | |
var n:int = typeSelectors.length; | |
// If we are a StyleProxy and we aren't building the protochain from | |
// our type selectors, then we need to build the protochain from | |
// the styleName since styleName.nonInheritingStyles is always null. | |
if (styleName is StyleProxy) | |
{ | |
if (n == 0) | |
{ | |
// 4) Look for type selectors on the styleName object | |
// 3) Look for class selectors on the styleName object | |
// 2) Look for inline styles on the styleName object | |
nonInheritChain = addProperties(nonInheritChain, styleName, false); | |
} | |
target = StyleProxy(styleName).source as DisplayObject; | |
} | |
for (var i:int = 0; i < n; i++) | |
{ | |
var typeSelector:CSSStyleDeclaration = typeSelectors[i]; | |
// If there's no *inheriting* type selector on this object, then we | |
// can still collapse 6 steps to 2 for the inheriting properties. | |
// 5) Look for type selectors on this object | |
inheritChain = typeSelector.addStyleToProtoChain(inheritChain, target); | |
// 4) Look for type selectors on the styleName object | |
// 3) Look for class selectors on the styleName object | |
// 2) Look for inline styles on the styleName object | |
inheritChain = addProperties(inheritChain, styleName, true); | |
// 5) Look for type selectors on this object | |
nonInheritChain = typeSelector.addStyleToProtoChain(nonInheritChain, target); | |
// 4) Look for type selectors on the styleName object | |
// 3) Look for class selectors on the styleName object | |
// 2) Look for inline styles on the styleName object | |
nonInheritChain = addProperties(nonInheritChain, styleName, false); | |
if (typeSelector.effects) | |
obj.registerEffects(typeSelector.effects); | |
} | |
// 1) Look for inline styles on this object | |
obj.inheritingStyles = | |
obj.styleDeclaration ? | |
obj.styleDeclaration.addStyleToProtoChain(inheritChain, target) : | |
inheritChain; | |
obj.nonInheritingStyles = | |
obj.styleDeclaration ? | |
obj.styleDeclaration.addStyleToProtoChain(nonInheritChain, target) : | |
nonInheritChain; | |
} | |
/** | |
* See the comment for the initProtoChainForUIComponentStyleName | |
* function. The comment for that function includes a six-step | |
* sequence. This sub-function implements the following pieces | |
* of that sequence: | |
* | |
* 2) Look for inline styles on the styleName object | |
* 3) Look for class selectors on the styleName object | |
* 4) Look for type selectors on the styleName object | |
* | |
* This piece is broken out as a separate function so that it | |
* can be called recursively when the styleName object has a | |
* styleName property is itself another UIComponent. | |
* | |
* @langversion 3.0 | |
* @playerversion Flash 9 | |
* @playerversion AIR 1.1 | |
* @productversion Flex 3 | |
*/ | |
private static function addProperties(chain:Object, obj:IStyleClient, | |
bInheriting:Boolean):Object | |
{ | |
// Only use a filter map if styleName is a StyleProxy and we are building the nonInheritingStyles chain | |
var filterMap:Object = obj is StyleProxy && !bInheriting ? StyleProxy(obj).filterMap : null; | |
// StyleProxy's usually have sources that are DisplayObject's, but a StyleProxy can also have | |
// another StyleProxy as it's source (Example: CalendarLayout's source is a StyleProxy for DateChooser, | |
// whose style is a StyleProxy for DateField) | |
// The way we use target is a bit hacky, but we always assume that styles (if pointed to DisplayObjects) | |
// are the parent (or atleast an ancestor), and we rely on this down the line (such as in | |
// DataGridColumn.addStyleToProtoChain) | |
var curObj:IStyleClient = obj; | |
while (curObj is StyleProxy) | |
{ | |
curObj = StyleProxy(curObj).source; | |
} | |
var target:DisplayObject = curObj as DisplayObject; | |
var advancedObject:IAdvancedStyleClient = obj as IAdvancedStyleClient; | |
var styleName:Object = obj.styleName; | |
var styleDeclarations:Array; | |
var decl:CSSStyleDeclaration; | |
var styleManager:IStyleManager2 = getStyleManager(target); | |
// If we have an advanced style client, we handle this separately | |
// because of the considerably more complex selector matches... | |
if (advancedObject != null && styleManager.hasAdvancedSelectors()) | |
{ | |
// Handle special case of styleName as a CSSStyleDeclaration | |
if (styleName is CSSStyleDeclaration) | |
{ | |
styleDeclarations = [CSSStyleDeclaration(styleName)]; | |
} | |
// Find matching style declarations, sorted by specificity | |
styleDeclarations = getMatchingStyleDeclarations(advancedObject, styleDeclarations); | |
// Then apply matching selectors to the proto chain | |
for (i = 0; i < styleDeclarations.length; i++) | |
{ | |
decl = styleDeclarations[i]; | |
if (decl) | |
{ | |
chain = decl.addStyleToProtoChain(chain, target, filterMap); | |
if (decl.effects) | |
obj.registerEffects(decl.effects); | |
} | |
} | |
// Finally, handle special case of styleName as an IStyleClient | |
// which overrides any of the selectors above | |
if (styleName is IStyleClient) | |
{ | |
// If the styleName property is another UIComponent, then | |
// recursively add type selectors, class selectors, and | |
// inline styles for that UIComponent | |
chain = addProperties(chain, IStyleClient(styleName), | |
bInheriting); | |
} | |
} | |
else | |
{ | |
// 4) Add type selectors | |
styleDeclarations = obj.getClassStyleDeclarations(); | |
var n:int = styleDeclarations.length; | |
for (var i:int = 0; i < n; i++) | |
{ | |
decl = styleDeclarations[i]; | |
chain = decl.addStyleToProtoChain(chain, target, filterMap); | |
if (decl.effects) | |
obj.registerEffects(decl.effects); | |
} | |
// 3) Add class selectors | |
if (styleName) | |
{ | |
styleDeclarations = []; | |
if (typeof(styleName) == "object") | |
{ | |
if (styleName is CSSStyleDeclaration) | |
{ | |
// Get the style sheet referenced by the styleName property. | |
styleDeclarations.push(CSSStyleDeclaration(styleName)); | |
} | |
else | |
{ | |
// If the styleName property is another UIComponent, then | |
// recursively add type selectors, class selectors, and | |
// inline styles for that UIComponent | |
chain = addProperties(chain, IStyleClient(styleName), | |
bInheriting); | |
} | |
} | |
else | |
{ | |
// Get the style sheets referenced by the styleName property | |
var styleNames:Array = styleName.split(/\s+/); | |
for (var c:int=0; c < styleNames.length; c++) | |
{ | |
if (styleNames[c].length) | |
{ | |
styleDeclarations.push(styleManager.getMergedStyleDeclaration("." + styleNames[c])); | |
} | |
} | |
} | |
for (i = 0; i < styleDeclarations.length; i++) | |
{ | |
decl = styleDeclarations[i]; | |
if (decl) | |
{ | |
chain = decl.addStyleToProtoChain(chain, target, filterMap); | |
if (decl.effects) | |
obj.registerEffects(decl.effects); | |
} | |
} | |
} | |
} | |
// 2) Add inline styles | |
if (obj.styleDeclaration) | |
chain = obj.styleDeclaration.addStyleToProtoChain(chain, target, filterMap); | |
return chain; | |
} | |
/** | |
* @private | |
*/ | |
public static function initTextField(obj:IUITextField):void | |
{ | |
// TextFields never have any inline styles or type selector, so | |
// this is an optimized version of the initObject function (above) | |
var styleManager:IStyleManager2 = StyleManager.getStyleManager(obj.moduleFactory); | |
var styleName:Object = obj.styleName; | |
var classSelectors:Array = []; | |
if (styleName) | |
{ | |
if (typeof(styleName) == "object") | |
{ | |
if (styleName is CSSStyleDeclaration) | |
{ | |
// Get the style sheet referenced by the styleName property. | |
classSelectors.push(CSSStyleDeclaration(styleName)); | |
} | |
else if (styleName is StyleProxy) | |
{ | |
obj.inheritingStyles = | |
IStyleClient(styleName).inheritingStyles; | |
obj.nonInheritingStyles = addProperties(styleManager.stylesRoot, IStyleClient(styleName), false); | |
return; | |
} | |
else | |
{ | |
// styleName points to a UIComponent, so just set | |
// this TextField's proto chains to be the same | |
// as that UIComponent's proto chains. | |
obj.inheritingStyles = | |
IStyleClient(styleName).inheritingStyles; | |
obj.nonInheritingStyles = | |
IStyleClient(styleName).nonInheritingStyles; | |
return; | |
} | |
} | |
else | |
{ | |
// Get the style sheets referenced by the styleName property | |
var styleNames:Array = styleName.split(/\s+/); | |
for (var c:int=0; c < styleNames.length; c++) | |
{ | |
if (styleNames[c].length) { | |
classSelectors.push(styleManager.getMergedStyleDeclaration("." + | |
styleNames[c])); | |
} | |
} | |
} | |
} | |
// To build the proto chain, we start at the end and work forward. | |
// We'll start by getting the tail of the proto chain, which is: | |
// - for non-inheriting styles, the global style sheet | |
// - for inheriting styles, my parent's style object | |
var inheritChain:Object = IStyleClient(obj.parent).inheritingStyles; | |
var nonInheritChain:Object = styleManager.stylesRoot; | |
if (!inheritChain) | |
inheritChain = styleManager.stylesRoot; | |
// Next are the class selectors | |
for (var i:int = 0; i < classSelectors.length; i++) | |
{ | |
var classSelector:CSSStyleDeclaration = classSelectors[i]; | |
if (classSelector) | |
{ | |
inheritChain = | |
classSelector.addStyleToProtoChain(inheritChain, DisplayObject(obj)); | |
nonInheritChain = | |
classSelector.addStyleToProtoChain(nonInheritChain, DisplayObject(obj)); | |
} | |
} | |
obj.inheritingStyles = inheritChain; | |
obj.nonInheritingStyles = nonInheritChain; | |
} | |
/** | |
* @private | |
* Implements the setStyle() logic for UIComponent and TextBase. | |
* The 'object' parameter will be one or the other. | |
*/ | |
public static function setStyle(object:IStyleClient, styleProp:String, | |
newValue:*):void | |
{ | |
var styleManager:IStyleManager2 = getStyleManager(object); | |
if (styleProp == "styleName") | |
{ | |
// Let the setter handle this one, see UIComponent. | |
object.styleName = newValue; | |
// Short circuit, because styleName isn't really a style. | |
return; | |
} | |
if (EffectManager.getEventForEffectTrigger(styleProp) != "") | |
EffectManager.setStyle(styleProp, object); | |
// If this object didn't previously have any inline styles, | |
// then regenerate its proto chain | |
// (and the proto chains of its descendants). | |
var isInheritingStyle:Boolean = | |
styleManager.isInheritingStyle(styleProp); | |
var isProtoChainInitialized:Boolean = | |
object.inheritingStyles != StyleProtoChain.STYLE_UNINITIALIZED; | |
var valueChanged:Boolean = object.getStyle(styleProp) != newValue; | |
if (!object.styleDeclaration) | |
{ | |
object.styleDeclaration = new CSSStyleDeclaration(null, styleManager); | |
object.styleDeclaration.setLocalStyle(styleProp, newValue); | |
// If inheritingStyles is undefined, then this object is being | |
// initialized and we haven't yet generated the proto chain. To | |
// avoid redundant work, don't bother to create the proto chain here. | |
if (isProtoChainInitialized) | |
object.regenerateStyleCache(isInheritingStyle); | |
} | |
else | |
{ | |
object.styleDeclaration.setLocalStyle(styleProp, newValue); | |
} | |
if (isProtoChainInitialized && valueChanged) | |
{ | |
object.styleChanged(styleProp); | |
object.notifyStyleChangeInChildren(styleProp, isInheritingStyle); | |
} | |
} | |
/** | |
* @private | |
* Implements the styleChanged() logic for UIComponent and TextBase. | |
* The 'object' parameter will be one or the other. | |
*/ | |
public static function styleChanged(object:IInvalidating, styleProp:String):void | |
{ | |
var styleManager:IStyleManager2 = getStyleManager(object); | |
// If font changed, then invalidateProperties so | |
// we can re-create the text field in commitProperties | |
// TODO (gosmith): Should hasFontContextChanged() be added to IFontContextComponent? | |
if (object is IFontContextComponent && | |
"hasFontContextChanged" in object && | |
object["hasFontContextChanged"]()) | |
{ | |
object.invalidateProperties(); | |
} | |
if (!styleProp || | |
styleProp == "styleName" || | |
styleProp == "layoutDirection") | |
{ | |
object.invalidateProperties(); | |
} | |
// Check to see if this is one of the style properties | |
// that is known to affect layout. | |
if (!styleProp || | |
styleProp == "styleName" || | |
styleManager.isSizeInvalidatingStyle(styleProp)) | |
{ | |
// This style property change may affect the layout of this | |
// object. Signal the LayoutManager to re-measure the object. | |
object.invalidateSize(); | |
} | |
// TODO (gosmith): Should initThemeColor() be in some interface? | |
if (!styleProp || | |
styleProp == "styleName" || | |
styleProp == "themeColor") | |
{ | |
if (object is UIComponent) | |
object["initThemeColor"](); | |
} | |
object.invalidateDisplayList(); | |
var parent:IInvalidating; | |
if (object is IVisualElement) | |
parent = IVisualElement(object).parent as IInvalidating; | |
if (parent) | |
{ | |
if (styleProp == "styleName" || styleManager.isParentSizeInvalidatingStyle(styleProp)) | |
parent.invalidateSize(); | |
if (styleProp == "styleName" || styleManager.isParentDisplayListInvalidatingStyle(styleProp)) | |
parent.invalidateDisplayList(); | |
} | |
} | |
/** | |
* @private | |
*/ | |
public static function matchesCSSType(object:IAdvancedStyleClient, cssType:String):Boolean | |
{ | |
var styleManager:IStyleManager2 = getStyleManager(object); | |
var qualified:Boolean = styleManager.qualifiedTypeSelectors; | |
var typeHierarchy:OrderedObject = getTypeHierarchy(object, qualified); | |
return typeHierarchy.object_proxy::getObjectProperty(cssType) != null; | |
} | |
/** | |
* @private | |
* Find all matching style declarations for an IAdvancedStyleClient | |
* component. The result is sorted in terms of specificity, but the | |
* declaration order is preserved. | |
* | |
* @param object - an IAdvancedStyleClient instance of the component to | |
* match. | |
* @param styleDeclarations - an optional Array of additional | |
* CSSStyleDeclarations to be included in the sorted matches. | |
* | |
* @return An Array of matching style declarations sorted by specificity. | |
*/ | |
public static function getMatchingStyleDeclarations(object:IAdvancedStyleClient, | |
styleDeclarations:Array=null):Array // of CSSStyleDeclaration | |
{ | |
var styleManager:IStyleManager2 = getStyleManager(object); | |
if (styleDeclarations == null) | |
styleDeclarations = []; | |
// First, look for universal selectors | |
var universalDecls:Array = styleManager.getStyleDeclarations("*"); | |
styleDeclarations = matchUniversalStyleDeclarations(universalDecls, object).concat(styleDeclarations); | |
// Next, look for type selectors (includes ActionScript supertype matches) | |
// If we also had universal selectors, concatenate them with our type | |
// selectors and then resort by specificity... | |
if (styleDeclarations.length > 0) | |
{ | |
styleDeclarations = object.getClassStyleDeclarations().concat(styleDeclarations); | |
styleDeclarations = sortOnSpecificity(styleDeclarations); | |
} | |
else | |
{ | |
// Otherwise, we only have type selectors (which are already sorted) | |
styleDeclarations = object.getClassStyleDeclarations(); | |
} | |
return styleDeclarations; | |
} | |
/** | |
* @private | |
* @param object - the IStyleClient to be introspected | |
* @param qualified - whether qualified type names should be used | |
* @return an ordered map of class names, starting with the object's class | |
* name and then each super class name until we hit a stop class, such as | |
* mx.core::UIComponent. | |
*/ | |
private static function getTypeHierarchy(object:IStyleClient, qualified:Boolean=true):OrderedObject | |
{ | |
var styleManager:IStyleManager2 = getStyleManager(object); | |
var className:String = getQualifiedClassName(object); | |
var hierarchy:OrderedObject = styleManager.typeHierarchyCache[className] as OrderedObject; | |
if (hierarchy == null) | |
{ | |
hierarchy = new OrderedObject(); | |
var myApplicationDomain:ApplicationDomain; | |
var factory:IFlexModuleFactory = ModuleManager.getAssociatedFactory(object); | |
if (factory != null) | |
{ | |
myApplicationDomain = ApplicationDomain(factory.info()["currentDomain"]); | |
} | |
else | |
{ | |
var myRoot:DisplayObject = SystemManager.getSWFRoot(object); | |
if (!myRoot) | |
return hierarchy; | |
myApplicationDomain = myRoot.loaderInfo.applicationDomain; | |
} | |
styleManager.typeHierarchyCache[className] = hierarchy; | |
while (!isStopClass(className)) | |
{ | |
try | |
{ | |
var type:String; | |
if (qualified) | |
type = className.replace("::", "."); | |
else | |
type = NameUtil.getUnqualifiedClassName(className); | |
hierarchy.object_proxy::setObjectProperty(type, true); | |
className = getQualifiedSuperclassName( | |
myApplicationDomain.getDefinition(className)); | |
} | |
catch(e:ReferenceError) | |
{ | |
className = null; | |
} | |
} | |
} | |
return hierarchy; | |
} | |
/** | |
* @private | |
* Our style type hierarhcy stops at UIComponent, UITextField or | |
* GraphicElement, not Object. | |
*/ | |
private static function isStopClass(value:String):Boolean | |
{ | |
return value == null || | |
value == "mx.core::UIComponent" || | |
value == "mx.core::UITextField" || | |
value == "mx.graphics.baseClasses::GraphicElement"; | |
} | |
/** | |
* @private | |
* Find all matching style declarations for an IAdvancedStyleClient | |
* component. The result is unsorted in terms of specificity, but the | |
* declaration order is preserved. | |
* | |
* @param declarations - a map of declarations to be searched for matches. | |
* @param object - an instance of the component to match. | |
* | |
* @return An unsorted Array of matching style declarations for the given | |
* subject. | |
*/ | |
private static function matchStyleDeclarations(declarations:Array, | |
object:IAdvancedStyleClient):Array // of CSSStyleDeclaration | |
{ | |
var matchingDecls:Array = []; | |
// Find the subset of declarations that match this component | |
for each (var decl:CSSStyleDeclaration in declarations) | |
{ | |
if (decl.matchesStyleClient(object)) | |
matchingDecls.push(decl); | |
} | |
return matchingDecls; | |
} | |
//TB: Here to improve the speed of this function | |
private static var nonSimpleUniversalStyles : Array; | |
private static var lengthDuringLastCache : int = 0; | |
private static var cachedSimpleUniversalStyles : Dictionary; | |
private static const whitespaceRegex : RegExp = /\s+/; | |
//TB: I added this function because the normal matchStyleDeclarations above just did a | |
//linear search through all 280+ CSS declarations we have | |
//It was SLOW - this could still be sped up, but it is MUCH faster | |
private static function matchUniversalStyleDeclarations(declarations:Array, | |
object:IAdvancedStyleClient):Array // of CSSStyleDeclaration | |
{ | |
var matchingDecls:Array = []; | |
if (declarations.length != lengthDuringLastCache) | |
{ | |
lengthDuringLastCache = declarations.length; | |
//trace("Rebuilding cache: "+lengthDuringLastCache + " - "+declarations.length); | |
var begin : int = getTimer(); | |
buildStyleNameToDeclarationCache(declarations); | |
var end : int = getTimer(); | |
//trace("Done : "+(end - begin).toString()); | |
} | |
var styleName : String = object.styleName as String; | |
if ((styleName != null) && (styleName != "")) | |
{ | |
var styleNames:Array = styleName.split(whitespaceRegex); | |
var length : int = styleNames.length; | |
for (var i:uint = 0; i < length; i++) | |
{ | |
var currentStyleName : String = styleNames[i]; | |
var match : CSSStyleDeclaration = cachedSimpleUniversalStyles[currentStyleName]; | |
if (match != null) | |
{ | |
matchingDecls.push(match); | |
} | |
} | |
} | |
// TB: Now we can do a linear search of the non-"simple" styles | |
//Find the subset of declarations that match this component | |
for each (var decl:CSSStyleDeclaration in nonSimpleUniversalStyles) | |
{ | |
if (decl.matchesStyleClient(object)) | |
matchingDecls.push(decl); | |
} | |
return matchingDecls; | |
} | |
private static function buildStyleNameToDeclarationCache(declarations : Array) : void | |
{ | |
cachedSimpleUniversalStyles = new Dictionary(true); | |
nonSimpleUniversalStyles = new Array(); | |
for each (var decl:CSSStyleDeclaration in declarations) | |
{ | |
var isSimple : Boolean; | |
if ((decl.selector.ancestor == null) && | |
((decl.selector.subject == "") || (decl.selector.subject == "*")) && | |
(decl.selector.conditions.length == 1)) | |
{ | |
var condition : CSSCondition = CSSCondition(decl.selector.conditions[0]); | |
if (condition.kind == CSSConditionKind.CLASS) | |
{ | |
cachedSimpleUniversalStyles[condition.value] = decl; | |
isSimple = true; | |
} | |
} | |
if (!isSimple) | |
{ | |
nonSimpleUniversalStyles.push(decl); | |
} | |
} | |
} | |
/** | |
* @private | |
* Sort algorithm to order style declarations by specificity. Note that | |
* Array.sort() is not used as it does not employ a stable algorithm and | |
* CSS requires the order of equal style declaration to be preserved. | |
*/ | |
private static function sortOnSpecificity(decls:Array):Array // of CSSStyleDeclaration | |
{ | |
// TODO (pfarland): Copied algorithm from Group.sortOnLayer as the | |
// number of declarations to be sorted is usually small. We may consider | |
// replacing this insertion sort with an efficient but stable merge sort | |
// or the like if many style declarations need to sorted. | |
var len:Number = decls.length; | |
var tmp:CSSStyleDeclaration; | |
if (len <= 1) | |
return decls; | |
for (var i:int = 1; i < len; i++) | |
{ | |
for (var j:int = i; j > 0; j--) | |
{ | |
if (decls[j].specificity < decls[j-1].specificity) | |
{ | |
tmp = decls[j]; | |
decls[j] = decls[j-1]; | |
decls[j-1] = tmp; | |
} | |
else | |
{ | |
break; | |
} | |
} | |
} | |
return decls; | |
} | |
/** | |
* @private | |
* Get the style manager of any object. If the object does not implement IFlexModule or | |
* is not of type StyleProxy, then the top-level style manager will be returned. | |
* | |
* @param object - Typed as Object because various interfaces are passed here. | |
* @return a style manager, will not be null. | |
*/ | |
private static function getStyleManager(object:Object):IStyleManager2 | |
{ | |
if (object is IFlexModule) | |
return StyleManager.getStyleManager(IFlexModule(object).moduleFactory); | |
else if (object is StyleProxy) | |
return getStyleManagerFromStyleProxy(StyleProxy(object)); | |
else | |
return StyleManager.getStyleManager(null); | |
} | |
/** | |
* @private | |
* Get the style manager for a given StyleProxy object. | |
* | |
* @return a style manager, will not be null. | |
*/ | |
private static function getStyleManagerFromStyleProxy(obj:StyleProxy):IStyleManager2 | |
{ | |
// StyleProxy's usually have sources that are DisplayObject's, but a StyleProxy can also have | |
// another StyleProxy as it's source (Example: CalendarLayout's source is a StyleProxy for DateChooser, | |
// whose style is a StyleProxy for DateField) | |
var curObj:IStyleClient = obj; | |
while (curObj is StyleProxy) | |
{ | |
curObj = StyleProxy(curObj).source; | |
} | |
if (curObj is IFlexModule) | |
return StyleManager.getStyleManager(IFlexModule(curObj).moduleFactory); | |
return StyleManager.getStyleManager(null); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment