One of the coolest new features in Flash MX 2004 is the Cascading Style Sheet (CSS) support for HTML text areas.
I recently used the new CSS feature to style an HTML TextField in an application I built.
Surrounding that HTML field are a number of other TextFields and UI Components. To format those other TextFields I had to use a different formatting approach involving the TextFormat class. To format the MX 2004 UI Components required a third approach, involving the CSSStyleDeclaration class.
There really should be a common way to style any type of visual component. Three different approaches is too many. It means there are three different sets of style declaration properties to learn, and three different ways to code them in ActionScript.
The good news is that Macromedia provides enough functionality in the current version of Flash to let us simplify the stylizing process ourselves.
In this article I’ll show how to stylize non-HTML TextFields using external CSS stylesheets. Next time I’ll discuss how to do the same thing for MX 2004 UI Components.
When it’s all done we’ll have a relatively easy way to style all the components in our movie at runtime using one common interface.
Think of the advantages: you can manage all your styles externally; you only have to learn one style declaration syntax (standard CSS syntax); and you can change the look of your movie on-the-fly to conform to different website style guidelines, or for improved user accessibility.
Extending TextField.StyleSheet
The TextField.StyleSheet class does a great job loading and parsing CSS files. It only handles a subset of CSS properties, but what it handles it handles well.
You enter code like this to load an external stylesheet and apply it to a TextField component (this sample is from the Flash MX 2004 help files):
// Create a new style sheet object var style_sheet = new TextField.StyleSheet(); // // Location of CSS file that defines styles var css_url = "html_styles.css"; // // Create some HTML text to display var storyText:String = "<p class='headline'>Flash Player now supports Cascading Style Sheets!</p><p><span class='byline'>San Francisco, CA</span>--Macromedia Inc. announced today a new version of Flash Player that supports Cascading Style Sheet (CSS) text styles. For more information, visit the <a href='http://www.macromedia.com'>Macromedia Flash web site.</a></p>"; // // Load CSS file and define onLoad handler: style_sheet.load(css_url); style_sheet.onLoad = function(ok) { if (ok) { // // If the style sheet loaded without error, // then assign it to the text object, // and assign the HTML text to the text field. news_txt.styleSheet = style_sheet; news_txt.text = storyText; } };
That gives you a nicely formatted TextField showing multiple text styles defined by the external stylesheet.
The TextField.StyleSheet class also lets you add and define styles at runtime using standard CSS syntax. For example in a CSS file the HTML
style might be formatted like this:
p { color: #000000; font-family: Arial,Helvetica,sans-serif; font-size: 12px; display: inline; }
You can add the same style from within your ActionScript using this syntax:
style_sheet.setStyle(“p”, { color: “#000000”, font-family: “Arial,Helvetica,sans-serif”, font-size: “12px”, display: “inline” } );
So on the surface the TextField.StyleSheet class is very useful if you want to read in external CSS files, or programmatically create new styles, and apply them as a whole stylesheet to an HTML TextField.
Under the surface TextField.StyleSheet class has some undocumented features that make it much more powerful.
The StyleSheet stores its list of styles in two separate internal arrays. One array is named _styles and it is an array of TextFormat objects.
The other array, named _css, seems to store the styles as instances of the mx.styles.CSSStyleDeclaration class. This class is normally used when stylizing MX 2004 UI Components. I’ll get into the CSSStyleDeclaration format in more detail in the next article.
Unfortunately both of these are private arrays, meaning you can’t access them directly from within your own ActionScript code (There are ways to get around this restriction, but it’s not considered good programming practice!).
It would have been pretty easy to just grab a TextFormat or CSSStyleDeclaration from one of these arrays when we needed one. Instead we’ll have to use some of StyleSheet’s public methods to achieve the same goals.
The StyleSheet.getStyle() method returns the style properties for a given style name in the form of a “style object”. This is an anonymous object that contains the properties specified in the original CSS style definition. The property names are the ActionScript style property names like “fontFamily” and “fontSize”..
We’ll also use StyleSheet.transform(), which is an undocumented method that accepts a “style object” and converts it to a TextFormat object.
So if you want to convert a CSS style to a TextFormat just do this:
var styleObj:Object = style_sheet.getStyle(“.myStyle”); var tf:TextFormat = style_sheet.transform(styleObj);
That’s not bad, but it could be easier. One way to simplify is to create a new subclass of TextField.StyleSheet that does more of the work for you.
I call this new subclass the StyleFormatter. It extends the TextField.StyleSheet class and adds a getTextFormat() method that returns a TextFormat object for a given style name. Here’s an excerpt:
class com.appcentral.styles.StyleFormatter extends TextField.StyleSheet { // // … clipped for brevity // public function getTextFormat(styleName:String):TextFormat { // // first retrieve the “style object” var style = getStyle(styleName); // // next convert the “style object” to a TextFormat object. // transform is an undocumented method that returns // a TextFormat version of a CSS style return transform(style); } }
Now we can grab a TextFormat object for any style in the external .css file.
To apply a style to your TextField (after loading the .css into a StyleFormatter object called styleFormatter):
var tf = styleFormatter.getTextFormat(styleName); if (tf != undefined) { myTextField_txt.setTextFormat(tf); myTextField_txt.setNewTextFormat(tf); }
Simple enough, but it can be easier still. Let’s add another method to the StyleFormatter class that formats a passed-in TextField using a named style:
public function applyTextFormat(txt:TextField, styleName:String):Void { var tf = getTextFormat(styleName); if (tf != undefined) { txt.setTextFormat(tf); txt.setNewTextFormat(tf); } }
I have posted a test movie showing the StyleFormatter class in action. It lets you load in a .css file, view the properties of each style, and apply each one to a set of TextFields. Try applying the styles in a different order to see the additive effect upon the TextFields.
This simple enhancement to the StyleSheet class offers some real advantages:
- Your Dynamic and Input Text fields can be made to match text styles in your HTML TextFields without a lot of extra programming.
- You can manage all your styles for your movie externally if you wish, and you only have to learn one set of property names (the standard CSS names).
- You can change your TextField styles at runtime by loading different .css files. This means you can publish a movie once, and change its appearance later without doing additional editing in Flash. If you have one movie that needs to conform to different style guidelines on different websites, or you want to give users some style customization options, this is a good way to go.
Here are links to the fully commented source code for the StyleFormatter class and a .zip file containing the source code plus a test movie and sample .css file.
In the next article I’ll improve this class further to handle CSSStyleDeclarations and MX 2004 UI Components.
Until then, here are a few other observations and tips for applying StyleSheets to more than just HTML TextFields.
Collecting Styles from Multiple CSS Files
When you load a second .css file into an existing StyleSheet, it completely overwrites any previous styles of the same name. However it does not erase all the previous styles. So one StyleSheet can be used to aggregate styles from many different .css files.
If you want to completely erase all the previous styles, instantiate a new StyleSheet object before loading. You can also use another undocumented TextField.StyleSheet method, clear(), to empty out the style arrays.
TextFormat Style Properties are Additive
If you apply different TextFormat properties to a TextField in succession, the later TextFormats will only replace previous properties if they have a new property value specified. For example, if the fontWeight in a field’s current TextFormat is set to “bold”, and you apply a new TextFormat that does not specify a value for fontWeight, your field will continue to display as bold.
To minimize unexpected “piling on” of style properties, try to include default values for properties. For example, if you have a “body” style that should always be normal weight and not underlined, make sure you include values like font-weight:normal; and text-decoration:none;
TextFields with Embedded Font Outlines
Changing the fontFamily property on-the-fly seems to work best on TextFields that have “Embed font outlines” set to “No characters”. In that case, the person watching the movie must have at least one of the requested fonts on installed on his or her machine. It seems that you can change the fontFamily property successfully for TextFields with embedded font outlines, but only if you embed all of the possible fontFamily fonts in your Flash movie.
Aliased Text
The Aliased text fields in the sample applications will only appear aliased (i.e. showing jagged edges) if you are using a Version 7 Flash Player.
Does anyone have a list of the subset of CSS properties TextField.StyleSheet handles?
Posted by: Nadine | 31 March 2005 at 12:53 AM
This may help.
Test a movie with an imported stylesheet, and select Debug->List Variables. You'll get something like the following for each stylesheet class, even if you've actually got the class setting only one property:
h1:[object #17, class 'TextFormat'] {
getTextExtent:[function],
font:[getter/setter] "URWEgyptienneT",
size:[getter/setter] 18,
color:[getter/setter] 0x000000,
url:[getter/setter] null,
target:[getter/setter] null,
bold:[getter/setter] null,
italic:[getter/setter] null,
underline:[getter/setter] null,
align:[getter/setter] null,
leftMargin:[getter/setter] null,
rightMargin:[getter/setter] null,
indent:[getter/setter] null,
leading:[getter/setter] null,
blockIndent:[getter/setter] null,
tabStops:[getter/setter] null,
bullet:[getter/setter] null,
display:[getter/setter] "block"
}
Of course, the names of the properties in Actionscript are different both from the list above and from the css properties you've used to set them. (Want to set the font? Both "font" and "font-family" will silently fail. Flash will cooperate if you use "fontFamily")
But that's intellectual property for you.
Posted by: rob | 20 May 2005 at 01:17 PM
I can't get your code example... seems to be very interesting but no file at all :(
Posted by: stef | 08 November 2005 at 03:11 AM
I want to have different style sheets for diffrent texts of same text filed.Is it possible?
Posted by: kishor | 27 November 2005 at 04:31 AM
Ah thank you. A big fat cigar for you. You've just spared me three hours of headaches!
Posted by: daan | 27 July 2008 at 02:19 PM