muhuk's blog

Nature, to Be Commanded, Must Be Obeyed

April 30, 2009

Using Layouts In Qooxdoo - Part 5: Basic & Canvas

This is the fifth part of a tutorial series about layout managers, container objects and object hierarchies in Qooxdoo. Read the first part here.

Basic Layout Manager

qx.ui.layout.Basic layout manager positions your widgets with absolute coordinates. It doesn’t have much to offer except the freedom of manual positioning.

We supply a left and top property when we add our widgets. Following example places buttons in a circular path, like a clock.

/*
#asset(qooxdoolayouts/*)
*/
qx.Class.define("qooxdoolayouts.Application",
{
    extend: qx.application.Standalone,
    members: {
        main: function(){
            this.base(arguments);

            var main_container = new qx.ui.container.Composite();

            var layout_manager = new qx.ui.layout.Basic();
            main_container.setLayout(layout_manager);

            var btn_size = 50;
            var distance = 175;
            var pi2 = 2 * 3.141592;
            center = {
                x: Math.round(qx.bom.Viewport.getWidth()/2)-btn_size/2,
                y: Math.round(qx.bom.Viewport.getHeight()/2)-btn_size/2
            }

            for (var i=0; i<12; i++) {
                var button = new qx.ui.form.Button("CW"+(i+1).toString());
                button.setWidth(btn_size);
                button.setHeight(btn_size);
                var dist_x = Math.round(distance * Math.sin(-(i/12+0.5)*pi2));
                var dist_y = Math.round(distance * Math.cos(-(i/12+0.5)*pi2));
                main_container.add(button, {
                    left: center.x + dist_x,
                    top: center.y + dist_y
                });
            }

            var application_root = this.getRoot();
            application_root.add(main_container);
        }
    }
});
Basic Example

Basic Example

Nevermind the math. What is important is; this would be much harder to achieve with other, more advanced managers such as Grid.

Canvas Layout Manager

qx.ui.layout.Canvas is a slightly advanced version of Basic. Canvas lets you define margins (of all four directions) and dimensions in percentage as well as in pixels.

Canvas allows you to define all four margins. You don’t have to define all of them of course. For example if you define a top and bottom margin remaining space will be your widget’s height. Alternatively you can specify a bottom margin and a height for you widget. This way it will keep its position relative to container’s bottom edge.

One thing to keep in mind is margins are defined against container, not against a bounding box like DOM. Say, if you set the same left on two widgets, they’ll be stacked on top of each other if they happen to have the same vertical coordinates. The following example mimics a Grid layout using Canvas:

/*
#asset(qooxdoolayouts/*)
*/
qx.Class.define("qooxdoolayouts.Application",
{
    extend: qx.application.Standalone,
    members: {
        main: function(){
            this.base(arguments);

            var main_container = new qx.ui.container.Composite();

            var layout_manager = new qx.ui.layout.Canvas();
            main_container.setLayout(layout_manager);

            main_container.add(new qx.ui.form.Button("Child Widget 1"), {
                left: 20,
                top: 10
            });
            main_container.add(new qx.ui.form.Button("Child Widget 2"), {
                left: 140,
                right: 20,
                top: 10
            });
            main_container.add(new qx.ui.form.Button("Child Widget 3"), {
                left: 20,
                top: 50,
                bottom: 10
            });
            main_container.add(new qx.ui.form.Button("Child Widget 4"), {
                left: 140,
                right: 20,
                top: 50,
                bottom: 10
            });

            main_container.getChildren()[0].setWidth(100);
            main_container.getChildren()[2].setWidth(100);

            var application_root = this.getRoot();
            application_root.add(main_container);

            application_root.addListener("resize", function(e) {
                main_container.set({
                    "width": qx.bom.Viewport.getWidth(),
                    "height": qx.bom.Viewport.getHeight(),
                });
            }, this);
        }
    }
});
Canvas Example

Canvas Example

We set two widgets’ widths manually (see lines 37 & 38) to get all the margins (almost) equal. The first row only has top margin set, but the second row has both top and bottom margins. We have given widgets in the second row top margins taking:

  • Top margin of first row
  • Height of the widgets in the first row

and

  • Margin between first and second rows

into account. The same thing is true for the left margin of second column. This is because, as I mentioned, margins are calculated relative to container edges.

If you need to position your controls relative to one another, like DOM positioning, you should probably look into other layout managers like Grid. A good example of a use case for Canvas can be a mapping application such as Google Maps.

As I mentioned earlier Canvas layout accepts parameters in percentages. Following example sets vertical margins in pixels but horizontal margins in percentages:

/*
#asset(qooxdoolayouts/*)
*/
qx.Class.define("qooxdoolayouts.Application",
{
    extend: qx.application.Standalone,
    members: {
        main: function(){
            this.base(arguments);

            var main_container = new qx.ui.container.Composite();

            var layout_manager = new qx.ui.layout.Canvas();
            main_container.setLayout(layout_manager);

            var button = new qx.ui.form.Button("Child Widget 1");
            button.setMinWidth(250);
            button.setMaxWidth(400);
            main_container.add(button, {
                left: "30%",
                right: "30%",
                top: 40,
                bottom: 40
            });

            var application_root = this.getRoot();
            application_root.add(main_container);

            application_root.addListener("resize", function(e) {
                main_container.set({
                    "width": qx.bom.Viewport.getWidth(),
                    "height": qx.bom.Viewport.getHeight(),
                });
            }, this);
        }
    }
});

You can find a list of posts in this series here


1: Whether your widget grows this high depends. Let’s say that height is reserved for your widget.

If you have any questions, suggestions or corrections feel free to drop me a line.