Vexi PlatformVexi Platform
vexi: easy, extensible, flexible

Sunday, July 08, 2007

The Children Trap

New to Vexi3 is the Children property. It replaces the previous notion of ChildAdded and ChildRemoved events, which were rather weak. Now we can completely control how boxes enter and leave as children of a box or template by placing read and write traps on it's Children property. As ever, an example is the best way to show off how this works.

Let's create a basic grid widget. Grids were removed from the core in Vexi2, so this is a useful example - although simplified - of how they are being replaced.

First the basic template structure:

<vexi xmlns:ui="vexi://ui">
    <ui:box>
        <ui:box id="grid" orient="vertical" />
        
        // JS to go here
        
    </ui:box/>
</vexi>

We have to have an inner box - $grid - because we are adding/removing containing rows so this keeps that logic simple, that is we do not have to consider rows in our Children write and read traps. Also $grid has a vertical orient because it is more common to work in rows before columns.

Let's start with the Children write trap which is fired every time thisbox[index] is put to:

thisbox.Children ++= function(v) {
    var i = arguments.trapname;
    var c = i%numcols;
    var r = vexi.math.floor(i/numcols);
    if (i+1 > total) total = i+1;
    while (r > $grid.numchildren - 1)
        $grid[$grid.numchildren] = vexi.box;
    var row = $grid[r];
    while (numcols > row.numchildren - 1) {
        var b = vexi.box;
        b.layout = "absolute";
        row[row.numchildren] = b;
    }
    // remove previous occupant
    if ($grid[r][c][0])
        $grid[r][c][0] = null;
    if (v) $grid[r][c][0] = v;
    return;
}

A few things to notice from the above code:

  • The index is passed as arguments.trapname
  • The child (or null if we are removing a child) is passed as the argument to the function
  • We return at the end because we have already handled the child placement and do not want the core to place the child in the box on which we are trapping
  • This handles non-null puts to an index with existing child in a different way than the core - the core inserts the new child, whereas this example overwrites the old child with the new one
  • We are using absolute layout for the cells here to keep the example simple - if we use a packed layout then the columns are not guarranteed to line up if the content size of any cell is significant

So, now we are handling the destination of child boxes of this template, we want to be able to also control how children are accessed as well. So we place a read trap on the Children property:

thisbox.Children ++= function() {
    var i = arguments.trapname;
    var c = i%numcols;
    var r = vexi.math.floor(i/numcols);
    // if the $grid[r] exists so does $grid[r][c]
    if ($grid[r]) return $grid[r][c][0];
    // otherwise we are out of grid bounds
    return null;
}

Simple enough, eh?

No comments: