Layout manager

User interface has to be adaptive to an environment and a hardware where it can be shown. To avoid potential UI layouting problems developers should not positioning and sizing UI components by assigning dedicated (x,y) coordinates and (width,height) size.

Layout managers is well known solution to get adaptive UI that helps developing adjustable UI layout. Layout manager doesn’t trust fixed UI components positions and sizes. It uses rules-based manner to order UI components. Layout manager “knows” two important things:

Usage layout manager in zebkit looks something like the following:

zebkit.require("ui", "layout", function(ui, layout) {
    ...
    var pan = new ui.Panel();
    // Set layout manager for the container
    pan.setLayout(new layout.BorderLayout()) 
    ...
    // Border layout manager orders child components 
    // with number of predefined alignments (top, center, left, 
    // etc). Let's add a component at the top of panel
    pan.add("top", new ui.Button("Ok"));
    ...
});

Zebkit is supplied with a number ready-to-use layout managers. In most cases they are enough for building UI application.

Raster Layout

The layout manager orders child components basing on its exact locations and sizes that are set via “setLocation(x, y)”, “setSize(w,h)” or “setBounds(x, y, w, h)” methods. But also the manager can use preferred sizes and custom vertical and horizontal alignments to place the child components.

Raster layout
zebkit.require("ui","layout",function(ui, lay) {
    var r = new ui.zCanvas().root;
    // raster layout that size components according to 
    // its preferred size
    r.setLayout(new lay.RasterLayout(true));

    // add children UI components with different constraints
    r.add("center", new ui.Button("Center,\npreferred Sized"));
    r.add("bottomLeft",new ui.Button("BottomLeft,\npreferred sized"));
    r.add("topRight",new ui.Button("TopRight,\npreferred sized"));
});

Stack Layout

This the most simple layout manager that place child components on top of each other stretching (or using preferred size) its to fill all available parent component space.

Stack layout
zebkit.require("ui","layout",function(ui, lay) {
    var r = new ui.zCanvas().root;
    // setup stack layout
    r.setLayout(new lay.StackLayout());

    // add child component stretched vertically and 
    // horizontally over the whole parent component area
    r.add(new ui.Button("Stretched\n\n\n\n button"));

    // add child component sized according its 
    // preferred size
    r.add("usePsSize", new ui.Button("Ok"));
});

Border Layout

The layout manager splits container area into five logical areas: “left”, “right”, “top”, “bottom”, “center”. Child components can be added to occupy one of the listed above logical area:

Border layout
zebkit.require("ui","layout",function(ui, lay) {
    var r = new ui.zCanvas().root;
    // set border layout manager
    r.setLayout(new lay.BorderLayout());

    // add children UI components with different constraints
    r.add("center", new ui.Button("CENTER"));
    r.add("left",   new ui.Button("LEFT"));
    r.add("right",  new ui.Button("RIGHT"));
    r.add("top",    new ui.Button("TOP"));
    r.add("bottom", new ui.Button("BOTTOM"));
});

List layout

List layout manager treats child components as sequence of list items ordered vertically. The items can be either aligned horizontally or stretched to occupy the whole available horizontal space.

List layout
zebkit.require("ui","layout", function(ui, layout) {
    var r = new ui.zCanvas().root;
    // set list layout manager that orders components as list 
    // items and stretches child components horizontally
    r.setLayout(new layout.ListLayout());

    // add child components
    r.add(new ui.Button("Stretched Item 1"));
    r.add(new ui.Button("Stretched Item 1"));
    r.add(new ui.Button("Stretched Item 1"));
});

Percentage layout

This layout manager orders child components vertically or horizontally according to specified as constraints height or width. Height and width are specified in percents.

Percentage layout
zebkit.require("ui","layout",function(ui, lay) {
    var r = new ui.zCanvas().root;
    // set percent layout manager that stretches components 
    // vertically and sizes component horizontally according 
    // to its percentage constraints
    r.setLayout(new lay.PercentLayout());

    // add button that takes 20% of horizontal space
    r.add(20, new ui.Button("20%"));
    // add button that takes 30% of horizontal space
    r.add(30, new ui.Button("30%"));
    // add button that takes 50% of horizontal space
    r.add(50, new ui.Button("50%"));
});

Flow layout

Flow layout manager orders child components vertically or horizontally according to its preferred size. Depending of a selected child components ordering (vertical or horizontal) they can be aligned “top”, “bottom”, “center” or “left”, “right”, “center”.

Flow layout
zebkit.require("ui","layout", function(ui, lay) {
    var r = new ui.zCanvas().root;

    // set flow layout with vertical components ordering and center 
    // vertical and horizontal alignments
    r.setLayout(new lay.FlowLayout("center","center","vertical",2));

    // add child components
    r.add(new ui.Button("VCentered"));
    r.add(new ui.Button("VCentered"));
    r.add(new ui.Button("VCentered"));
});

Grid layout

Grid layout splits container area to number of logical cells where child components have to be placed. Every child component can have specified constraints that says how the component has to be aligned inside the virtual cell (vertical alignment, horizontal alignment, stretching).

Grid layout
zebkit.require("ui","layout", function(ui, layout) {
    var r = new ui.zCanvas().root;
    // create layout that splits a container area 
    // into four virtual cells (2 rows, and 2 columns)  
    r.setLayout(new layout.GridLayout(2,2));

    // add child components
    r.add(new ui.Button("1x1"));
    r.add(new ui.Button("1x2"));
    r.add(new ui.Button("2x1"));
    r.add(new ui.Button("2x2"));
});

Custom layout manager

If existent layout managers are not enough it is not a big deal to implement a custom one. To do it inherit “zebkit.layout.Layout” interface and implement the following two methods:

Let’s develop layout manager that orders components along parent component diagonal:

zebkit.package("ui.demo", function(pkg, Class) {
   // declare layout manager class
   pkg.DiagLayout = Class(zebkit.layout.Layout,[
       // preferred size target component wants to have is calculated 
       // as sum of preferred sizes of child components
       function calcPreferredSize(target) {
          var psW = 0, psH = 0;
          for(var i=0; i < target.kids.length; i++) {
              var kid = target.kids[i];
              if (kid.isVisible) {
                  var ps = kid.getPreferredSize();
                  psW += ps.width;
                  psH += ps.height;
              }
          }
          return { width:psW, height:psH };
       },
       // define rules how child components of the
       // given "target" have to be ordered
       function doLayout(target) {
          var x = target.getTop(), y = target.getLeft();
          for(var i=0; i < target.kids.length; i++) {
              var kid = target.kids[i];
              if (kid.isVisible) {
                  var ps = kid.getPreferredSize();
                  kid.setBounds(x, y, ps.width, ps.height);
                  x += ps.width;
                  y += ps.height;
              }
          }
       }
   ]);
});

Use the developed diagonal layout manager as follow:

Custom diagonal layout
zebkit.require("ui", "ui.demo", function(ui, demo) {
     var r = new ui.zCanvas().root;
     r.setPadding(8);
     r.setBorder("plain");
     // set developed above diagonal layout manager
     r.setLayout(new demo.DiagLayout());
     // add children components
     r.add(new ui.Button("One ..."));
     r.add(new ui.Button("Two ..."));
     r.add(new ui.Button("Three ..."));
});