Developing Widgets for Chumby: Liquid layout

From Chumby Wiki
Jump to: navigation, search

This document describe the development of widgets for chumby with liquid layout. The purpose of using liquid layout is to develop only one version of widget that is able to scale to various resolutions.


These are resolutions Chumby widgets run on.
Chumby One: 320x240
Insignia 3.5": 320x240
Insignia 8": 800x600
Sony Dash: 800x480
Android: 800x480 (most popular), 854x480, 1024x600 (Archos 10.1), 1200 x 800 (XOOM)

Understand scaling

Normally Flash scales everything to best fit into its windows. This works and probably looks good too.
For example a widget is developed to display 1 paragraph of text on 320x240.
When it is displayed on a 800x600 device, font size scaled 2.5 times, and the widget is still showing only 1 paragraph of text.
In this case, there is no real benefit of having higher resolution display.

What we want to have is the font size of text generally stays the same while we have more pixels to display more text at once.
This is where liquid layout comes in.
So, the first thing we need to do is to disable Flash's default scaling.
It is best to do this in the first frame.

stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;

Liquid layout class

See Sample RSS Widget in AS3.
com.chumby.widgets.RSSTemplatesAS3.RSSFlexibleLayoutManager were written to help implementing liquid layout easily.

public class SplashScreen extends MovieClip
    //UI objects placed in Flash stage
    public var splash_logo:MovieClip;
    public var splash_title:TextField;
    public var splash_background:MovieClip;

    //Liquid layout
    var myStage:Stage;
    var myRoot:Object;
    var layoutManager:RSSFlexibleLayoutManager;

    public function SplashScreen()
         myStage.addEventListener(Event.RESIZE, onResize);

         //Liquid layout rules
         layoutManager.addDisplayObject(splash_background, false, {height_relative: [1, RSSFlexibleLayoutManager.heightOf(myStage)], width_relative: [1, RSSFlexibleLayoutManager.widthOf(myStage)]  });
         layoutManager.addDisplayObject(splash_logo, true, {height_relative: [(splash_logo.height/RSSFlexibleLayoutManager.yDesignSize), RSSFlexibleLayoutManager.heightOf(myStage)], width_relative: [ (splash_logo.width/RSSFlexibleLayoutManager.xDesignSize), RSSFlexibleLayoutManager.widthOf(myStage)], width_max_relative: [0.8, RSSFlexibleLayoutManager.widthOf(splash_background)], height_max_relative:  [0.8, RSSFlexibleLayoutManager.heightOf(splash_background)], center_y_relative: [0.9,  RSSFlexibleLayoutManager.yCenterOf(myStage)], center_x_relative: [1,  RSSFlexibleLayoutManager.xCenterOf(myStage)]   });
         layoutManager.addDisplayObject(splash_title,false,{top_relative: [1, RSSFlexibleLayoutManager.bottomOf(splash_logo), 10], left: 0, width_span: [0, RSSFlexibleLayoutManager.widthOf(myStage)], text_size_relative: [ (Number)(splash_title.getTextFormat().size) / RSSFlexibleLayoutManager.yDesignSize, RSSFlexibleLayoutManager.heightOf(myStage)], text_size_min: (Number)(splash_title.getTextFormat().size) / 1.5, text_size_max: (Number)(splash_title.getTextFormat().size) * 2});

    public function onResize(evt:Event)

This is an AS3 example extracted from class of Sample RSS Widget in AS3 template.
See [Developing Widgets for Chumby: ActionScript 3] for information about myStage & myRoot.

Position & size of the UI elements in this class are setup with relative constraints to the stage or one another.
This is done by passing the layout manager the UI elements and a set of rules.
Once we have the constraints established, everytime there is a need to change their layout, we just simply let the layout manager re-apply those rules.


Liquid layout rules

The syntax for adding rules is

layoutManager.addDisplayObject(display_object, keep_aspect_ratio true/false, {rule1, rule2, rule3...});
display_object - UI element to be added to layout. This object must be a DisplayObject or derived from DisplayObject. Current dimensions of the object is saved for scaling calculations when it is added.
keep_aspect_ratio - Set to true to keep aspect ratio of the element such as logo, photos. Certain graphics such as gradient background bars for title should use 'false'. This flag is not applied to TextField elements.
rules - an array of rules using predefined syntax.

Rules are applied in the same order they were added.
General syntax for a rule is

type_of_constraint: [parameter1, parameter2, ...]

Constraints fall in 2 types: size constraints & position constraints. Size contraints is always applied before position constraints.
Different constraint has different number of parameters. Normally there are 1,2 and 3 parameters.

Examples below are not exhaustive, please check source code in One parameter:

text_size_min: 12  //minimum size of the text (TextField)
width_max: 345     //maximum width of the element in pixels
width: 123         //fixed width of the element in pixels
left: 123          //fixed left position or .x coordinate of the element in pixel in its coordinate system

Two parameter:
param0: percentage of; param1: an expression to be evaluate.
The expression can be a number of special syntax used in RSSFlexibleLayoutManager class

//100% of the bottom position of splash_logo
top_relative: [1, RSSFlexibleLayoutManager.bottomOf(splash_logo)]

//whatever percentage of the font size currently compared to stage height currently
//when the stage changes dimensions, the font size is scaled to a fixed percentage as compared to stage height
text_size_relative: [ (Number)(splash_title.getTextFormat().size) / RSSFlexibleLayoutManager.yDesignSize, RSSFlexibleLayoutManager.heightOf(myStage)]

Three parameter:
Most rules with 2 parameters can also accept an additional offer parameter.
Layout manager will take whatever value evaluated from the first 2 parameters and add it with the offset parameter.

//100% of the bottom position of splash_logo, minus 7 pixels.
top_relative: [1, RSSFlexibleLayoutManager.bottomOf(splash_logo), -7]

Note: TextField must have 'multiline' property enabled for liquid layout to work properly.

someTextField.multiline = true;