CSS parsing and the use of Styles in Flash

Posted on May 11, 2009

0


Building the Flash RAD framework, I am constantly looking for the best practices around. For styling, I like CSS over creating some XML-structure. Here are the reasons:

  1. CSS is a well known standard
  2. It is clean and simple
  3. CSS is dedicated to styling

Flash and Style Sheets
Flash currently allows you to use a limited set of styles and Style Sheets on text fields. And that is where it stops. If I want to manipulate objects as I can do in HTML I need to do this via code in ActionScript. There is no default “applyStyleSheet” to objects. I decided to do something about this using CSS as my main starting point.

Reason
Styling by using External Definitions is immense powerful.
CSS is a clear cut concept to separate your styling from your data and your visual assets and make this styling easy to switch and change. For the Flash Rapid Application Development Framework it is a logical and (as it turns out) powerful choice to implement.

Proof of Concept: CSS casting with the Flash RAD framework
In this demo (which shows only the basic set of possibilities), parsed CSS styles are used to mark the state of active and inactive items. I use Event Casting and Style Casting from the input field “Type name here” to change the styles of the Select Lists. Apart from the RAD framework, no code is used to change styles or add glow or shadow. As styles are easy to link to the state of an object (using the “_<state name>” suffix) it is easy to reflect those states later. Automated State Based Styles always refer to the base style you assigned to the object.

Scope in Flash: surpassing HTML styles
The scope of styling possibilities in Flash go much further than those currently in HTML. Styling Flash objects can include:

  • Drop Shadow
  • Glow
  • Blur
  • X/Y/Z movement and rotation
  • Animations
  • Binding Behavioral classes to objects

Changes created by the styles can be tweened, so when the x/y position, the alpha, blur or whatever is changed to something else using a new style, it will use a transition to move to that new state.

Possible usage in Flash
You can use and cast CSS like stylesheets in Flash to i.e.:

  1. Change the X/Y/Z position and rotation of any object in your Flash movie
  2. Change background colors, objects (like switching the movie clips you use as backgrounds for your objects) or background images
  3. Tell your renderer to organize your screen assets using relative or absolute positioning
  4. Tell your renderer which (custom) Flash Classes to use to handle specific behaviors
  5. Set animation paths and follow those through when another object changes state

Basics
To map CSS settings to Flash objects, you need to translate them. “padding-left” for instance means nothing when addressing a Flash object directly unless you define this in your project. The same goes for “background” using either a color or image. Also, the parameters given in the CSS file or CSS block in the HTML are given in clear text, made readable for humans, not for machines.

To use the CSS styles we need to translate the clear text to objects with readable parameters. The hierarchy in doing this is as follows:

  1. Clear all unwanted characters
  2. Break down the CSS into separate Style Classes (by using the end bracket “}”)
  3. Break each style down to the Class Name and the Style Settings (by using the start bracket “{” separating the two)
  4. Separate each Style Setting (by using the semicolon “end of line” indicator “;”)
  5. Break each Style Setting down to the Variable and the Value. (by using the “is” indicator “:”)

Furthermore, we need to map the CSS settings to variables and cast them to objects that Flash understands.

Tabs, spaces, line feeds and carriage returns
As said before, CSS is made for humans. So it will contain characters we do not need and can not use.

Parsing the CSS
Taking this all in account here is the first version of the CSS parser.
The code is written to accept anything the user puts into the CSS, so we use Objects to put the data in.

The input consists of a variable (styleSheetContent) containing the Style Sheet text and the object (“CSS”) that will contain the parsed Style Sheet (including the parsed Flash Filters if they are defined)

function parseCSS(styleSheetContent:String,CSS:Object)
{
   // BETA 0.2.
   /*
      BRIEF:
      parseCSS translates the Style Classes in a flat CSS text
      to objects with variables and settings.

      A basic Flash Style Sheet can look like this:

      .fieldstyle
      {
         font-size:10;
         color:#ff0000;
      }
      .buttonstyle
      {
         width:200px;
         filter.DropShadowFilter:alpha=0.5, blurX=3, blurY=3;
      }
      After parsing, a setting in a style class (like fieldstyle - font-size) can be approached like this:

      var fontSize=CSS.fieldstyle.fontsize;

   */

   // MAIN VARIABLES
   var myStyleItems:Array;
   var myStyleClass:Array;
   var myStyleClassCount:Number;
   var myStyleItemsCount:Number;
   var myStyleSetting:Array;
   var myStyleClassName:String;

   // FOR EACH SETTING
   var myValue:String;
   var myTag:String;

   // STEP 1: CLEAN THE CSS FROM UNWANTED CHARACTERS
   styleSheetContent=replace(styleSheetContent,"\t","");
   styleSheetContent=replace(styleSheetContent,"\n","");
   styleSheetContent=replace(styleSheetContent,"\r","");

   // STEP 2: BREAK IT DOWN TO SEPARATE STYLE CLASSES
   var myStyleTags:Array=styleSheetContent.split("}")
   var myStyleTagCount:Number=myStyleTags.length

   // GO THROUGH EACH STYLE CLASS
   for (var i=0;i<myStyleTagCount;i++)
   {
      // STEP 3: SEPARATE CLASS NAME FROM SETTINGS
      myStyleClass=myStyleTags[i].split("{")

      // GET THE NAME OF THE STYLE CLASS
      myStyleClassName=replace(myStyleClass[0]," ","")
      // We do not need the "dot".
      myStyleClassName=replace(myStyleClass[0],".","")

      // OUTPUT TO TRACER
      trace("STYLECLASS= '" + myStyleClassName + "'")

      try{
         if(myStyleClassName=="")
         {
            // DO NOTHING/SKIP
         }
         else
         {
            // CREATE AN OBJECT FOR THE STYLE CLASS
            CSS[myStyleClassName]=new Object();
            CSS[myStyleClassName].filters=new Array();

            // STEP 4: SPLIT STRING AND GET EACH STYLE ITEM
            myStyleItems=myStyleClass[1].split(";")
            var myStyleItemsCount=myStyleItems.length

            // GET THE STYLE ITEMS
            for (var j=0;j<myStyleItemsCount-1;j++)
            {
               // STEP 5: SEPARATE THE VARIABLE AND THE VALUE PER SETTING
               myStyleSetting=myStyleItems[j].split(":");

               // NAME OF VARIABLE
               myTag=replace(myStyleSetting[0]," ","");

               // IF THE STYLE CLASS CONTAINS A FLASH FILTER, TREAT IT HERE
               if(myTag.indexOf("filter")>=0)
               {
                  // FILTERS HAVE AN INDICATOR OF THE TYPE IN THE NAME
                  // E.G: filter.DropShadowFilter:<parameters>;
                  var filter:Array=myTag.split(".")
                  // createFlashFilter(CSS[myStyleClassName].filters,filter[1],myStyleSetting[1])
               }
               else
               {
                  // MAKE SURE WE DO NOT HAVE THE "-" IN OUR VARIABLE NAMES
                  myTag=replace(myTag,"-","").toLowerCase();

                  // GET THE VALUE
                  // WEED OUT ALL OTHER CHARACTERS WE DO NOT WANT
                  // This could be done using Regular Expressions I guess.
                  myValue=replace(myStyleSetting[1]," ","");
                  myValue=replace(myValue,"#","0x");
                  myValue=replace(myValue,"px",""); // As we like the "width:9px;"

                  // ADD THE VARIABLE AND VALUE TO THE CSS STYLE OBJECT
                  CSS[myStyleClassName][myTag]=myValue;

                  // OUTPUT TO TRACER
                  trace("         '" + myTag + ":"+myValue+"'")
               }
            }
         }
      }
      catch(evt)
      {
         trace("error in CSS parsing: check structure")
      }
   }
}
function replace(_var,_a,_b):String
{
  _var=_var.split(_a).join(_b);
  return _var;
}

function insertCSS(CSSobj:Object,className:String,textField:TextField,container:Sprite)
{
 var CSSclass=CSS[className]
 var textStyle:TextFormat= new TextFormat();
 textStyle.font = CSSclass.fontfamily;
 textStyle.color = CSSclass.color;
 textStyle.size = CSSclass.fontsize;
 textField.setTextFormat(textStyle);

 // APPLY FLASH FILTERS
 container.filters=CSSclass.filters;   
}
var CSS:Object=new Object()
parseCSS(myCSStext,CSS)

insertCSS(CSS,"formfield",myTextField,this)
The Flash Filters are parsed separately.

Using the CSS Objects
Using the CSS Objects with their settings in Flash, you call the CSS object with the name of a specific Style Class as is shown in the code above with “insertCSS”. In the example, two things are done:

  1. Setting the font name, font size and the color of the text field
  2. Setting the filters of the movieclip containing the text field.

This might seem contrive, but using this basis you can start casting classes to any object, changing the visual state based on different stylesheets.

Advertisements
Posted in: Styling and CSS