Finally, BaseUI version 4 is released!
If you don’t know what is BaseUI, it is a lightweight layout manager written in pure AS3 that is helping you to handle the position and size of your elements in a Flash layout (a site, an application and or whatever you can build with AS3).
More specifically, it is calculating positions and size when you resize your browser to update your elements. BaseUI makes you able to use layout properties (such as top, bottom, horizontalCenter, and so on) on any DisplayObject instance, much like you would do in a Flex environment.
You can use BaseUI with a single DisplayOBject instance, with tons of DisplayObject instances or in a more complex system with layout classes (such as canvas, horizontal and vertical box, and tile).
Before showing you how it works with some simple examples, I’d like to mention that BaseUI has been completely rebuilt from scratch. I did that because I wasn’t happy at all with a lot of things in the previous version. I wanted a very clean and effective library, with no hacks to make things works, which would make it very reliable.
The library is now contained in a com.soma.ui package, it will be part of the next Soma MVC Framework version.
I removed two things from the previous version:
– the width and height ElementUI properties, as it wasn’t making sense, you can now just set the width and height on your own DisplayObject instances.
– the percentage values for width and height, that is not possible anymore, I might add specific properties in the future if requested
Enough talk, you will get everything you need on the BaseUI page:
Concept and the need of a reference
The important concept to understand is that you need to specify a reference to a BaseUI instance, or to the class that will handle the DisplayObject (which is the ElementUI class). A reference is what will be used to calculate size and position. For example, if you want your DisplayObject to be at 10 pixels from the bottom, you need to tell the library from what object. That’s what a reference is.
The reference can be any DisplayObjectContainer, such as the stage, a sprite, a layout, etc. Once you understand completely the need of having a reference, it is good to know that is doesn’t have to be a parent of your DisplayObject, it can be anything, anywhere. The only important things is that the reference must have a proper width and height, the stage or a layout class will always have it, but an empty Sprite instance won’t, the values will be 0.
All the following example will use the stage as a reference, but again, you can use anything you want.
Create a BaseUI instance
var baseUI:BaseUI = new BaseUI(stage);
Adding and removing DisplayObject instances
var baseUI:BaseUI = new BaseUI(stage); var mySprite:Sprite = new Sprite(); var element:ElementUI = baseUI.add(mySprite); element.refresh(); addChild(mySprite);
baseUI.remove(mySprite); baseUI.removeAll();
Getting ElementUI instances
var element:ElementUI = baseUI.getElement(mySprite); var arrayOfElements:Array = baseUI.getElements(); // note that this is a copy of the real array var dictionaryOfElements:Dictionary = baseUI.getElementsAsDictionary(); // note that this is a copy of the real dictionary
Setting ElementUI properties
var element:ElementUI = baseUI.getElement(mySprite); element.bottom = 10; element.right = 10; element.refresh();
var element:ElementUI = baseUI.getElement(mySprite); element.horizontalCenter = 0; element.verticalCenter = 0; element.refresh();
var element:ElementUI = baseUI.getElement(mySprite); element.ratio = ElementUI.RATIO_IN; element.refresh();
Reset ElementUI properties
You can reset the Number properties of the ElementUI instance by using NaN or null for the others:
element.top = 10; element.top = NaN; element.ratio = ElementUI.RATIO_IN; element.ratio = null; // or Element.RATIO_NONE element.rect = new Rectangle(0, 0, 50, 50); element.rect = null;
Or reset all of them:
element.reset();
Using ratio
The ratio is a kind of mode. You can use with a DisplayObject instance that keeps its proportion (Aspect Ratio) when it gets resized. Especially useful with pictures or backgrounds for example.
There is two ratio mode, a mode “in” and a mode “out”.
The mode “in” will always display all the surface of the DisplayObject instance in its reference, you might get empty area if the reference has a different ratio.
The mode “out” will always cover the surface of the reference, you might miss some part of the DisplayObject if the reference has a different ratio.
var element:ElementUI = baseUI.add(mySprite); element.ratio = ElementUI.RATIO_IN; element.alignX = ElementUI.ALIGN_RIGHT; element.alignY = ElementUI.ALIGN_BOTTOM;
DisplayObject boundaries (optional)
When you use some ElementUI properties (such as right, bottom, horizontalCenter, etc), BaseUI will use the width and height of your DisplayObject instance to make its position calculation. For example, if you want the object to be at 10 pixels from the bottom, BaseUI needs to use the height.
This might prove difficult in some cases, for example if you have a specific “center point” or if the size of the DisplayObject instance changes all the time (if it is animated).
To solve this kind of problem, you can specify the boundaries of your DisplayObject to be used as a size in BaseUI instead of its real size. You can do that by using the “rect” and a Rectangle instance.
For example, if you draw a square in a sprite width 100 and height 100, but starting at x -50 and y -50 so it is centered. You can specify the boundaries like this:
var element:ElementUI = baseUI.add(myCenteredSprite); element.rect = new Rectangle(-50, -50, 100, 100);
And you can remove it this way:
element.rect = null;
ElementUI Listeners
An ElementUI instance can dispatch three events, in this order:
1. EventUI.WILL_CALCULATE (dispatched before any calculation)
2. EventUI.WILL_UPDATE (dispatched after calculation and before updating the DisplayObject)
3. EventUI.UPDATED (dispatched after that the DisplayObject has been updated)
You can stop the ElementUI process in the EventUI.WILL_CALCULATE and EventUI.WILL_UPDATE handlers using “event.preventDefault()”.
var baseUI:BaseUI = new BaseUI(stage); var element:ElementUI = baseUI.add(mySprite); element.addEventListener(EventUI.WILL_CALCULATE, willCalculateHandler); element.addEventListener(EventUI.WILL_UPDATE, willUpdateHandler); element.addEventListener(EventUI.UPDATED, updatedHandler); element.right = 10; element.bottom = 10; element.refresh(); addChild(mySprite); private function willCalculateHandler(event:EventUI):void { //event.preventDefault(); // stop the process before the calculation trace(event.element); // trace the ElementUI instance trace(event.element.object); // trace the DisplayObject instance trace(event.element.baseUI); // trace the BaseUI instance } private function willUpdateHandler(event:EventUI):void { //event.preventDefault(); // stop the process before new properties are applied to the DisplayObject trace(event.element); // trace the ElementUI instance trace(event.element.object); // trace the DisplayObject instance trace(event.element.baseUI); // trace the BaseUI instance trace(event.properties); // trace the properties that will be applied to the DisplayObject } private function updatedHandler(event:EventUI):void { trace(event.element); // trace the ElementUI instance trace(event.element.object); // trace the DisplayObject instance trace(event.element.baseUI); // trace the BaseUI instance trace(event.properties); // trace the properties that have been applied to the DisplayObject }
Refreshing an element
When you create an element, the position and size will be calculated when an Event.RESIZE occurs, which might not happen unless you resize your browser. For a performance matter, the calculation is not done when you change a property. To make the calculation and update your element, you might need to use the refresh method of your ElementUI instance (or BaseUI instance for all the ElementUI).
element.refresh(); baseUI.refresh();
Disposing and garbage collection
Both BaseUI and ElementUI classes have a dispose method to destroy variables and make the instance eligible for the garbage collection. The only one you have to care about is the BaseUI one, as the ElementUI will be properly destroyed when you use the remove and removeAll BaseUI methods.
baseUI.dispose(); baseUI = null;
Using layouts
Five layouts classes are built in BaseUI, all of them have ElementUI properties directly accessible.
– The LayoutUI class: the super class of all the other layouts classes, only a container, children stay un-touched
– The CanvasUI class: handle the children with ElementUI properties
– The HBoxUI class: display the children, one after the other, in a horizontal direction
– The VBoxUI class: display the children, one after the other, in a vertical direction
– The TileUI class: display the children, one after the other, and is in both direction and in a “multiline” way
All the layouts classes will have some common properties, such as background color, background transparency, rounded values, and if the content that goes outside of the boundaries of the layouts should be displayed or hidden.
You can also use layouts in other layouts, like adding canvas in tiles that are added in vertical boxes, etc. Be aware that we’re still using flash here, and even if the library is efficient, it might get slower if you add thousands of tiles in thousands of other tiles with thousands of bitmaps. Calculation are made and everything has limits, especially Flash. Beside that, I got good performance with the library, you will be fine unless you do crazy things π
The LayoutUI class
The LayoutUI class extends the MovieClip class and is nothing else that a container that will always have a size (width and height), even if there’s nothing inside. Another difference with another built-in flash container (like Sprite, MovieClip, etc), is that the width and height will always be the one of the container regardless of what it contains.
For example, a layout instance (width 100 and height 100), that contains another DisplayObject (width 200 and height 200). The width and height values returned by a normal Sprite or MovieClip would be 200, while the layouts classes will return 100 (see getRealWidth and getRealHeight methods).
And that is true for all the layouts in the library, it is something to be aware of.
var layout:LayoutUI = new LayoutUI(stage, 400, 300); layout.backgroundColor = 0xFF0000; layout.backgroundAlpha = 0.2; layout.bottom = 10; layout.right = 10; layout.refresh(); addChild(layout);
The CanvasUI class
The behavior of the CanvasUI class is very close to its super class (LayoutUI), with the different that an ElementUI instance is created (or removed) when you add children to its display list. Makes you able to handle the children of the CanvasUI instance with ElementUI properties.
var canvas:CanvasUI = new CanvasUI(stage, 400, 300); canvas.backgroundColor = 0xFF0000; canvas.backgroundAlpha = 0.2; canvas.ratio = ElementUI.RATIO_IN; addChild(canvas); var sprite:Sprite = new Sprite(); sprite.graphics.beginFill(0xFF0000, .5); sprite.graphics.drawRect(0, 0, 100, 100); var element:ElementUI = canvas.add(sprite); element.right = 10; element.bottom = 10; canvas.refresh();
The HBoxUI class (horizontal box)
The goal of the HBoxUI class is that it will take from the positioning of its children, and align them in an horizontal way and only one row. Properties such as children gap, children align and children padding are introduced.
var hbox:HBoxUI = new HBoxUI(stage, 400, 300); hbox.backgroundColor = 0xFF0000; hbox.backgroundAlpha = 0.2; hbox.ratio = ElementUI.RATIO_IN; hbox.childrenGap = new GapUI(5, 5); hbox.childrenPadding = new PaddingUI(5, 5, 5, 5); hbox.childrenAlign = HBoxUI.ALIGN_BOTTOM_RIGHT; addChild(hbox); for (var i:int=0; i<8; ++i) { var sprite:Sprite = new Sprite(); sprite.graphics.beginFill(0xFF0000, .5); sprite.graphics.drawRect(0, 0, 100, 100); hbox.addChild(sprite); } hbox.refresh();
The VBoxUI class (vertical box)
The VBoxUI class is much like the HBoxUI one, but you found out, in a vertical way.
var vbox:VBoxUI = new VBoxUI(stage, 400, 300); vbox.backgroundColor = 0xFF0000; vbox.backgroundAlpha = 0.2; vbox.ratio = ElementUI.RATIO_IN; vbox.childrenGap = new GapUI(5, 5); vbox.childrenPadding = new PaddingUI(5, 5, 5, 5); vbox.childrenAlign = VBoxUI.ALIGN_BOTTOM_RIGHT; addChild(vbox); for (var i:int=0; i<8; ++i) { var sprite:Sprite = new Sprite(); sprite.graphics.beginFill(0xFF0000, .5); sprite.graphics.drawRect(0, 0, 100, 100); vbox.addChild(sprite); } vbox.refresh();
The TileUI class
A kind of mix between the HBoxUI and VBoxUI classes, Flex developer are used to it as well. It will show the children in multiple lines, in any direction depending of the setting used.
var tile:TileUI = new TileUI(stage, 400, 300); tile.backgroundColor = 0xFF0000; tile.backgroundAlpha = 0.2; tile.ratio = ElementUI.RATIO_IN; tile.childrenGap = new GapUI(5, 5); tile.childrenPadding = new PaddingUI(5, 5, 5, 5); tile.childrenAlign = TileUI.ALIGN_BOTTOM_RIGHT; tile.childrenDirection = TileUI.DIRECTION_HORIZONTAL; addChild(tile); for (var i:int=0; i<16; ++i) { var sprite:Sprite = new Sprite(); sprite.graphics.beginFill(0xFF0000, .5); sprite.graphics.drawRect(0, 0, 100, 100); tile.addChild(sprite); } tile.refresh();
I’m sure you will enjoy the library, it is very easy to use. Please send me some feedback π
Happy development!
Romuald
Comments
Pingback: Tweets that mention AS3 Layout Manager BaseUI v4 finally released | Soundstep, daily ActionScript. -- Topsy.com
Nice work, I’ve been using Stage Manager for my flash framework and it works ok, but I always had issues with aligning elements within other elements. From what I’ve read so far (while on the train) your reference idea seems to be exactly what I need. I’ll take a closer look when I get a chance!
Without the percentage settings how could you make, say a navigation bar always the width of the stage?
I tried to modify the rect of the elementUI but this doesn’t update on stage resize.
Thanks,
sb
Ah, have been waitiing for this. Great stuff! Have to look deeper to see the differences to v3 though. Thanks for sharing Romu!
@henry
you’re welcome, you shouldn’t have the kind of problem you had with the v3 if you have a look at the rect property π
@sb
you get a 100% width, by setting left = 0 and right = 0
Romu
nice work, this is the tool i looking so long.
thanks ~~
thanks for the update to the framework. I was using BaseUI and ElementUI instances in a project and upgraded to the new build in minutes. The only real alterations where references to the old namespace and changing el.width = “100%”; to el.left = el.right = 0;
Excellent!
Thank you so much for sharing BaseUI v4, Romu. Your efforts have really helped me a LOT in Pure AS3 work.
The new API really hangs together well. I will try it out in the next week.
Nice work! But without the percentage settings how could you make, say two panels, one – 40%, and another 60%?
The only way for now is to recalculate the width yourself in an event before it applies.
This is temporary, I intend to put back the width and height percentage value in the next release.
I tried a lot of thing not to drop any performance, and I’ll add new properties for that. In short, it won’t work as in the previous version (3.x), but it is on the way.
Thanks for the great Classes, I have been looking for somthing like this for a long time, it will save so much time.
I have been playing around with the hBox and vBox Classes and was wondering if there is a way to set a width and have the elements that are added be automaticy distributed evenly within this area.
Hi, posted this in V3 but thought I’d bring it up here as I’ve implemented this in a large framework only to suddenly find it breaks when used with rotation.
Rotating an object and assigning left/right etc will deform it completely, any pointers on how to fix this without me spending many hours testing each aspect?
Thanks π
Hi,
great job here :). In the previous version of your layout manager they are a property on ElementUI : useInitialSize.
In my case I got a “layout = new HBoxUI(this);” witch contain children with a masked picture.
How can I say to my layout: “don’t use the whole width of the children” ?
Sorry for my poor english π
You can set a rect property (Rectangle) to specify the boundaries on the ElementUI, have a try with that one.
Same answer for the rotation, I would have a try with the rect property, or handling it manually in a handler.
Hi,
Great work that looks promising !
After playing a while with the demo some things look confusing to me.
For exemple if is set a padding to a container it stops working if I set the contained element to fill.
I mean in this case there is no more padding?
Not sure what you’re trying to do, did you try with the demo?
http://www.soundstep.com/blog/source/baseuiv4/demo/
Yes that’s what I’ve played with.
What I did :
– Create a VBox
– Set its size to 500×500
– Set its padding to 20,20,20,20
– Create a HBox in VBox (padding are respected)
– Set the HBox to fill the width of VBox by checking top left and top right checkboxes (but maybe that’s not the way to go)
Vbox left and right padding is not respected anymore.
I see, it is not that the padding is not respected. It is that the properties of the hbox (left, right, top, bottom) prevails.
So you can set these previous padding values to be the left, right, top and bottom value of the hbox.
Not sure it is good or bad, but that’s how it works right now.
OK I see. Thanks for your answer.
What I expect when I set the padding of a container is that its childs won’t use this space.
To me there should be a way to specify a global padding for a container so thats contained elements can fill all space minus that global padding avoiding to change left, right, top and bottom values for every elements in the container.
In fact having to set top and left value to zero to specify an element should fill the container is not intuitive to me.
I would prefer a more common system like in Android layouts where you specify either WRAP_CONTENT or FILL_PARENT for an element width and height.
Anyway thanks for your time and for sharing you approach on this matter !