| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.styles |
| { |
| |
| import flash.display.DisplayObject; |
| import flash.display.DisplayObjectContainer; |
| import flash.system.ApplicationDomain; |
| import flash.utils.getQualifiedClassName; |
| import flash.utils.getQualifiedSuperclassName; |
| |
| 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, styleManager, 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:Object = 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, inheritPopUpStylesFromOwner:Boolean=true):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; |
| else if (object is IAdvancedStyleClient) |
| p = IAdvancedStyleClient(object).styleParent 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 (inheritPopUpStylesFromOwner && 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, styleManager, 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:Object = styleManager.getStyleDeclarations("*"); |
| styleDeclarations = matchStyleDeclarations(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, styleManager:IStyleManager2, qualified:Boolean=true):OrderedObject |
| { |
| 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:Object, |
| object:IAdvancedStyleClient):Array // of CSSStyleDeclaration |
| { |
| var matchingDecls:Array = []; |
| var pseudos:Array = declarations["pseudo"]; |
| var classes:Array = declarations["class"]; |
| var ids:Array = declarations["id"]; |
| var unconditionals:Array = declarations["unconditional"]; |
| |
| var decl:CSSStyleDeclaration; |
| |
| // Find the subset of declarations that match this component |
| for each (decl in unconditionals) |
| { |
| if (decl.matchesStyleClient(object)) |
| matchingDecls.push(decl); |
| } |
| |
| if (object.styleName is String) |
| { |
| // Find the subset of declarations that match this component |
| for each (decl in classes) |
| { |
| if (decl.matchesStyleClient(object)) |
| matchingDecls.push(decl); |
| } |
| } |
| |
| if (object.hasCSSState()) |
| { |
| // Find the subset of declarations that match this component |
| for each (decl in pseudos) |
| { |
| if (decl.matchesStyleClient(object)) |
| matchingDecls.push(decl); |
| } |
| } |
| |
| if (object.id) |
| { |
| // Find the subset of declarations that match this component |
| for each (decl in ids) |
| { |
| if (decl.matchesStyleClient(object)) |
| matchingDecls.push(decl); |
| } |
| } |
| |
| if (matchingDecls.length > 1) |
| matchingDecls.sortOn("selectorIndex", Array.NUMERIC); |
| |
| // if there are declarations from the parent StyleManager, match them in their own |
| // order, then prepend them. Parent styles go on the chain before child styles. |
| if (declarations.parent) |
| matchingDecls = matchStyleDeclarations(declarations.parent, object).concat(matchingDecls); |
| |
| return matchingDecls; |
| } |
| |
| /** |
| * @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); |
| } |
| } |
| |
| } |