Share 'The Jakute Styling Engine “Hello World” Tutorial' on Delicious Share 'The Jakute Styling Engine “Hello World” Tutorial' on Facebook Share 'The Jakute Styling Engine “Hello World” Tutorial' on Google Bookmarks Share 'The Jakute Styling Engine “Hello World” Tutorial' on Twitter

Article | Published: 12. Januar 2011 | Changed: 15. Dezember 2014 | Category: Jakute
  1. About Jakute Styling Engine (JCSS)
  2. About this tutorial
  3. Creating a simple Flash component
  4. Installing JCSS
  5. Setting up the application
  6. Configuring the component
    1. The component adapter JCSS_Adapter
    2. Creating a JCSS_Adapter instance
    3. Setting component name, ID, class and states
    4. Defining component styles
    5. Implementing the style-to-property binding
    6. Registering the component adapter in JCSS
  7. Creating and assigning a global style sheet
  8. The final code and the compiled swf
  9. Leave a Comment

About Jakute Styling Engine (JCSS)

The Jakute Styling Engine (JCSS) is a CSS framework for Flash. Its features are comparable to those of HTML/CSS while being focused on the specifics of Flash and custom applications.

About this tutorial

In this tutorial we are going to create a small movie containing just a rectangular box that is configured by an external style sheet. Here is the swf that we will engineer. Note, the controls are not part of this tutorial.

HelloWorld4.swf

If you want to see the final code first, jump to the last section of this tutorial.

The tutorial covers the following techniques:

  1. Installation of JCSS
  2. General set up of a JCSS application
  3. Implementation of a simple styleable component
    1. Configuration of a JCSS component adapter
    2. Definition of component styles
  4. Creation and assignment of a global style sheet

The tutorial covers the following concepts:

  1. JCSS service and JCSS component adapter
  2. Component name, ID, class and states
  3. Default and fix styles
  4. Style value formatters

Jakute Styling Engine is designed to style independent components that do not need to know the framework at all. (With JCSS it is even possible to apply styles to finalized components or to a black box application. But this is not subject of this tutorial.)

We will therefore start the tutorial with the implementation of a simple self-contained component before we come to JCSS configuration and the actual style handling.

Note, all examples and example steps are committed to the Jakute Git repository.

Creating a simple Flash component

Our component shall be a filled rectangle with a colored border. We would like to externally specify both colors, the border size as well as the component dimensions. The component shall use default values for its properties. The component should immediately update when a property changes.

Here the list of the component properties and default values:

  1. Width (300), height (100)
  2. Background color (light grey)
  3. Border color (darker grey)
  4. Border size (2)

The component then could look like as follows. There is a public property for each style value and a public draw method that needs to be triggered externally for performance reasons. The background is drawn with a gradient.

Component.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package helloworld {

  import common.ColorUtil;
  import flash.display.GradientType;
  import flash.display.Sprite;
  import flash.geom.Matrix;

  public class Component extends Sprite {
    public var w : uint = 300;
    public var h : uint = 100;
    public var backgroundColor : uint = 0xEEEEEE;
    public var borderColor : uint = 0xAAAAAA;
    public var borderSize : uint = 2;

    public function draw() : void {
      // gradient
      var matrix : Matrix = new Matrix();
      matrix.createGradientBox(w, h, Math.PI / 180 * 45, 0, 0);
      var gradient : Array = gradient = ColorUtil.getGradient(backgroundColor, 20, "bright_to_dark");

      with (graphics) {
        clear();
        // border
        if (borderSize) lineStyle(borderSize, borderColor);
        // background
        beginGradientFill(GradientType.LINEAR, gradient, [1, 1], [0, 255], matrix);
        drawRect(0, 0, w, h);
      }
    }
  }
}

And the component in action:

HelloWorld1.as
1
2
3
4
5
6
7
8
9
10
11
12
package helloworld {

  import flash.display.Sprite;

  public class HelloWorld1 extends Sprite {
    public function HelloWorld1() {
      var box : Component = new Component();
      box.draw();
      addChild(box);
    }
  }
}
HelloWorld1.swf

To test our runtime modification capabilities, I have added a control panel that let’s test how our properties work.

HelloWorld1b.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package helloworld {

  import helloworld.panel.ControlPanelProperties;
  import flash.display.Sprite;

  public class HelloWorld1b extends Sprite {
    public function HelloWorld1b() {
      var box : Component = new Component();
      box.w = 150;
      box.h = 150;
      box.backgroundColor = 0xBCCC7A;
      box.borderColor = 0x81991F;
      box.borderSize = 1;
      box.draw();
      addChild(box);

      // box control panel
      addChild(new ControlPanelProperties(box));
    }
  }
}
HelloWorld1b.swf

Now we have a nice component for that we can specify properties programmatically. In the next chapters we will configure JCSS to enable the component properties to be set using an external style sheet. Let’s go :-)

Installing JCSS

  1. Create a new Flash/Flex/ActionScript project.
  2. Download the most recent jakute.swc file from GitHub.
  3. Add the swc to your project. That’s all.

Setting up the application

To setup an application for JCSS there is nothing to do but to import the JCSS library. The library provides three top level classes you work with. All the other stuff is internal code that you kindly may ignore.

JCSS.as This is the JCSS service. It provides general operations, settings, constants and maintains the global style sheet. You need to work with this class at least once in order to register your component.
JCSS_Adapter.as You register a component in JCSS providing a custom adapter (described later). You can directly instantiate JCSS_Adapter or create a sub class of it.
JCSS_Sprite.as This is an optional class you may engage for convenience. JCSS_Sprite extends the Flash Sprite by including all the adapter functionality. To no disturb the actual component implementation, all methods of this method are prefixed with a short jcss_. It is possible and encouraged to copy and rename the methods from this class into a base class of your choice.

Configuring the component

This is a 2 step procedure. First we need to create and set up a component adapter. Then we register this adapter together with the component in JCSS.

The component adapter JCSS_Adapter

A component adapter realizes the connection between the JCSS framework on the one hand and the particular component implementation on the other hand. This way both parties can be developed completely independently.

Each display list component has its own adapter in JCSS. Click to enlarge this diagram.

The role of the component adapter is to store additional styling related information as well as to provide a style-to-property binding which is executed whenever a style should be set to a component.

The developer creates, configures and registers the adapter. Click to enlarge this diagram.

The following list shows the easy way a JCSS_Adapter gets configured and registered.

  1. Create an instance of JCSS_Adapter
  2. Think out and set a nice component name
  3. Set css attributes such as class, ID or states (optional)
  4. Define a number of styles that to component shall support
  5. Implement the style-to-property binding
  6. Register the adapter in JCSS

Creating a JCSS_Adapter instance

There are 3 different ways to create an adapter instance. The most simple is to directly instantiate JCSS_Adapter and afterwards configure this instance externally. Another way is to create a subclass of JCSS_Adapter and configure this custom adapter inline, so the main code does not need to carry configuration code.

The third possibility is probably the most preferred. Here we create a base display object class in which we already embed the adapter. A reference implementation of such a base class is included in the JCSS project (JCSS_Sprite.as).

The following listing shows the 3 ways of creating an adapter.

CreatingAnAdapter.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package configuration {

  import com.sibirjak.jakute.JCSS;
  import com.sibirjak.jakute.JCSS_Adapter;
  import com.sibirjak.jakute.JCSS_Sprite;
  import com.sibirjak.jakute.constants.JCSS_StyleValueFormat;

  import flash.display.Sprite;

  public class CreatingAnAdapter extends Sprite {
    public function CreatingAnAdapter() {
      var jcss : JCSS = new JCSS();
     
      // Version 1 - Default adapter configuration

      var box1 : Sprite = new Sprite();
      var adapter1 : JCSS_Adapter = new JCSS_Adapter();
      adapter1.cssName = "Box";
      adapter1.defineStyle("color", "#CCFF99", JCSS_StyleValueFormat.FORMAT_HTML_COLOR);
      adapter1.stylesInitializedHandler = function(adapter : JCSS_Adapter) : void {
        draw(adapter.component as Sprite, adapter.getStyle("color"));
      };
      jcss.registerComponent(box1, adapter1);
      addChild(box1);

      // Version 2 - Custom adapter configuration

      var box2 : Sprite = new Sprite();
      box2.x = 100;
      var adapter2 : JCSS_Adapter = new BoxAdapter();
      jcss.registerComponent(box2, adapter2);
      addChild(box2);

      // Version 3 - UI base class configuration

      JCSS_Sprite.jcss = jcss;
      var box3 : Box = new Box();
      box3.x = 200;
      addChild(box3);
    }
  }
}

import com.sibirjak.jakute.JCSS_Adapter;
import com.sibirjak.jakute.JCSS_Sprite;
import com.sibirjak.jakute.constants.JCSS_StyleValueFormat;

import flash.display.Sprite;

internal class BoxAdapter extends JCSS_Adapter {
  public function BoxAdapter() {
    cssName = "Box";
    defineStyle("color", "#CC99FF", JCSS_StyleValueFormat.FORMAT_HTML_COLOR);
  }

  override protected function onStylesInitialized() : void {
    draw(component as Sprite, getStyle("color"));
  }
}

internal class Box extends JCSS_Sprite {
  public function Box() {
    jcss_cssName = "Box";
    jcss_defineStyle("color", "#99CCFF", JCSS_StyleValueFormat.FORMAT_HTML_COLOR);
    // automatically registered in JCSS by super constructor
  }

  override protected function jcss_onStylesInitialized() : void {
    draw(this, jcss_getStyle("color"));
  }
}

internal function draw(shape : Sprite, color : uint) : void {
  with (shape.graphics) {
    beginFill(color);
    drawRect(0, 0, 80, 80);
  }
}
CreatingAnAdapter.swf

We will create our tutorial adapter using the 1 approach, by externally configuring a fresh new JCSS_Adapter instance.

Setting component name, ID, class and states

The component name is later used to address components in style sheets or style declaration. To illustrate, you can think of your application as an HTML page, and the particular HTML tags are the component names. ID and class are tag attributes in HTML, in JCSS they are set to the adapter. States do not exist in HTML. A JCSS component can have an unlimited number of states for that we can declare specific style rules. However, ID, class and states are not part of this tutorial :-)

HTML

1
<Box id="top" class="big" />

JCSS

1
2
3
adapter.cssName = "Box";
adapter.cssID = "top";
adapter.cssClass = "big";

There are a few interesting details to consider:

  1. It is possible to use different component names for instances of the same class.
  2. It is also possible to the same component name for instances of the different classes.
  3. The component name cannot be changed after the initialize event (explained later).
  4. Only alphanumeric chars plus “_” are allowed for the component name.

Defining component styles

Each style a component shall support must be defined in the component adapter beforehand. If not defined initially, a style never applies to the component. Styles can be defined only initially (before the initialize event). To define a style we invoke the JCSS_Adapter.defineStyle() method. It has the following signature:

1
public function defineStyle(styleName : String, styleValue : *, format : String, priority : uint = 0) : void;
styleName The name of the style such as “backgroundColor” or “w”.
styleValue The initial value of the style such as “#FF0000″ or 0xFF0000.
format The key of a globally registered style formatter.
priority A flag that indicates if a style can be overridden in style declarations.

While styleName and styleValue are obvious, a note to format and priority is appropriate.

Style value formatter

Since style rules may be declared in style sheets in text form, we need a possibility to convert a given style value into the type the component expects. For this purpose we can register a style value formatter in JCSS using:

1
JCSS.getInstance().registerStyleValueFormatter(formatterKey : String, formatter : Function)

An example of a custom style formatter can be found at the Jakute examples page.

There are a few predefined formatter we can take use of. The key constants for those formatters are available in JCSS.as. Using the color formatter, we can declare a color value in HTML form (#FF0000), and to the component is still passed the according numeric value. Creating a custom formatter is pretty easy.

Priority

There are 3 priorities: JCSS.PRIORITY_DEFAULT = 0, JCSS.PRIORITY_FIX = 1, JCSS.PRIORITY_IMPORTANT = 2
If set to important, it is not possible to override the initial style value. If set to fix, only important styles may override this style. This is a very useful feature when developers hand over applications to customers who are only allowed to modify a subset of the available styles.

These are our styles:

1
2
3
4
5
adapter.defineStyle("width", 150, JCSS.FORMAT_NUMBER);
adapter.defineStyle("height", 150, JCSS.FORMAT_NUMBER);
adapter.defineStyle("background", "#BCCC7A", JCSS.FORMAT_COLOR);
adapter.defineStyle("border", "#81991F", JCSS.FORMAT_COLOR);
adapter.defineStyle("thickness", 1, JCSS.FORMAT_NUMBER);

Implementing the style-to-property binding

As mentioned above, we need to implement some kind of mapping between the styles managed by JCSS and the properties of the component. Lets say, we have a component with the public setter setSize(w, h) and we would like to offer the styles “width” and “height”. Then our mapping should invoke the setSize() method of the component with the style values for “width” and “height” as arguments. The style-property-binding should be executed in the following 2 cases:

  1. After all styles of the component have been calculated initially. All style values then need to be passed to the component in order to give the component an initial state. This is the already mentioned component initialized event, which happens only once for the entire component life cycle.
  2. After styles have changed at runtime due to style rule settings or updates or display list modifications. In such a case a component needs to be updated. This event is called styles changed event and may occur multiple times for a component. (We do not discuss the event in the Hello World tutorial.)

A component adapter should always specify a handler for the initialized event. If the component is allowed to change at runtime, a handler for the style changed event must be specified. Here is the initialized handler for our component. We take the values of the given style object and invoke the appropriate setter of the component instance. Its obvious that the setters do not need to have the same name as the styles.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
adapter.defineStyle("thickness", 1, JCSS.FORMAT_NUMBER);
adapter.stylesInitializedHandler = onStylesInitialized;
...

private function onStylesInitialized(styles : Object, adapter : JCSS_Adapter) : void {
  var box : Component = adapter.component as Component;
  box.w = styles["width"];
  box.h = styles["height"];
  box.backgroundColor = styles["background"];
  box.borderColor = styles["border"];
  box.borderSize = styles["thickness"];
  box.draw();
}

Registering the component adapter in JCSS

This is the last step to make our component CSS capable. There are again some interesting considerations:

  1. A component must not be in the display list at the point of registration.
  2. A component cannot be registered twice.
  3. A component adapter cannot be registered again.

Altogether, this is now our main class code:

HelloWorld3.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package helloworld {
  import com.sibirjak.jakute.constants.JCSS_StyleValueFormat;
  import com.sibirjak.jakute.JCSS;
  import com.sibirjak.jakute.JCSS_Adapter;
  import flash.display.Sprite;

  public class HelloWorld3 extends Sprite {
    public function HelloWorld3() {
      var adapter : JCSS_Adapter = new JCSS_Adapter();
      adapter.cssName = "Box";
      adapter.defineStyle("width", 150, JCSS_StyleValueFormat.FORMAT_NUMBER);
      adapter.defineStyle("height", 150, JCSS_StyleValueFormat.FORMAT_NUMBER);
      adapter.defineStyle("background", "#BCCC7A", JCSS_StyleValueFormat.FORMAT_HTML_COLOR);
      adapter.defineStyle("border", "#81991F", JCSS_StyleValueFormat.FORMAT_HTML_COLOR);
      adapter.defineStyle("thickness", 1, JCSS_StyleValueFormat.FORMAT_NUMBER);
      adapter.stylesInitializedHandler = onStylesInitialized;

      var box : Component = new Component();
      new JCSS().registerComponent(box, adapter);
      addChild(box);
    }
   
    private function onStylesInitialized(adapter : JCSS_Adapter) : void {
      var box : Component = adapter.component as Component;
      box.w = adapter.getStyle("width");
      box.h = adapter.getStyle("height");
      box.backgroundColor = adapter.getStyle("background");
      box.borderColor = adapter.getStyle("border");
      box.borderSize = adapter.getStyle("thickness");
      box.draw();
    }
  }
}

And it compiles to:

HelloWorld3.swf

Since we do not have added a handler for the style changed event, we cannot modify our styles once set. To enable runtime setting, we simply extend the main class by that handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
adapter.defineStyle("thickness", 1, JCSS.FORMAT_NUMBER);
adapter.stylesInitializedHandler = onStylesInitializedOrChanged;
adapter.stylesChangedHandler = onStylesInitializedOrChanged;
...

private function onStylesInitializedOrChanged(styles : Object, adapter : JCSS_Adapter) : void {
  var box : Component = adapter.component as Component;
  box.w = adapter.getStyle("width");
  box.h = adapter.getStyle("height");
  box.backgroundColor = adapter.getStyle("background");
  box.borderColor = adapter.getStyle("border");
  box.borderSize = adapter.getStyle("thickness");
  box.draw();
  ...
}
HelloWorld3b.swf

Creating and assigning a global style sheet

Finally, its time to create a style sheet for our application.

1
2
3
4
5
6
Box {
  width: 200;
  height: 200;
  background: #99CCFF;
  border: #779999
}

In a real world we perhaps load the style sheet into the application. For the tutorial we pretend that the file is already loaded and put it simply in a variable. And that’s all.

The final code and the compiled swf

HelloWorld4.swf
HelloWorld4.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package helloworld {

  import helloworld.panel.ControlPanelStyles;

  import com.sibirjak.jakute.JCSS;
  import com.sibirjak.jakute.JCSS_Adapter;
  import com.sibirjak.jakute.constants.JCSS_StyleValueFormat;

  import flash.display.Sprite;

  public class HelloWorld4 extends Sprite {
    public function HelloWorld4() {
      var jcss : JCSS = new JCSS();
      jcss.setStyleSheet(CSS.styles);

      var adapter : JCSS_Adapter = new JCSS_Adapter();
      adapter.cssName = "Box";
      adapter.defineStyle("width", 150, JCSS_StyleValueFormat.FORMAT_NUMBER);
      adapter.defineStyle("height", 150, JCSS_StyleValueFormat.FORMAT_NUMBER);
      adapter.defineStyle("background", "#BCCC7A", JCSS_StyleValueFormat.FORMAT_HTML_COLOR);
      adapter.defineStyle("border", "#81991F", JCSS_StyleValueFormat.FORMAT_HTML_COLOR);
      adapter.defineStyle("thickness", 1, JCSS_StyleValueFormat.FORMAT_NUMBER);
      adapter.stylesInitializedHandler = onStylesInitialized;
      adapter.stylesChangedHandler = onStylesChanged;

      var box : Component = new Component();
      jcss.registerComponent(box, adapter);
      addChild(box);
     
      addChild(new ControlPanelStyles(jcss, box));
    }

    private function onStylesInitialized(adapter : JCSS_Adapter) : void {
      onStylesChanged(null, adapter);
    }

    private function onStylesChanged(styles : Object, adapter : JCSS_Adapter) : void {
      var box : Component = adapter.component as Component;
      box.w = adapter.getStyle("width");
      box.h = adapter.getStyle("height");
      box.backgroundColor = adapter.getStyle("background");
      box.borderColor = adapter.getStyle("border");
      box.borderSize = adapter.getStyle("thickness");
      box.draw();
    }
  }
}

internal class CSS {
  public static var styles : String = <styles><![CDATA[
    Box {
      width: 200;
      height: 200;
      background: #99CCFF;
      border: #779999
    }
  ]]></styles>.toString();
}
Component.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package helloworld {

  import common.ColorUtil;
  import flash.display.GradientType;
  import flash.display.Sprite;
  import flash.geom.Matrix;

  public class Component extends Sprite {
    public var w : uint = 300;
    public var h : uint = 100;
    public var backgroundColor : uint = 0xEEEEEE;
    public var borderColor : uint = 0xAAAAAA;
    public var borderSize : uint = 2;

    public function draw() : void {
      // gradient
      var matrix : Matrix = new Matrix();
      matrix.createGradientBox(w, h, Math.PI / 180 * 45, 0, 0);
      var gradient : Array = gradient = ColorUtil.getGradient(backgroundColor, 20, "bright_to_dark");

      with (graphics) {
        clear();
        // border
        if (borderSize) lineStyle(borderSize, borderColor);
        // background
        beginGradientFill(GradientType.LINEAR, gradient, [1, 1], [0, 255], matrix);
        drawRect(0, 0, w, h);
      }
    }
  }
}

Two different ways to create a component adapter are described at the example page of the Jakute Styling Engine project:

  1. HelloWorld2 (Custom adapter configuration)
  2. HelloWorld3 (UI base class configuration)

If you have any question or recommendation, feel free to write a reply or to contact me.



Leave a Comment

You have a question or have experienced an issue? Please post it in the forum: http://sibirjak.tenderapp.com/ in order to make the discussion available at a more central place.