Vexi PlatformVexi Platform
vexi: easy, extensible, flexible

Friday, July 27, 2007

Understanding Event Processing

Understanding how events are propogated throughout the box model can enable you to pull off a lot of neat tricks when implementing more complicated widgets and user interfaces. This basic template is an interactive demonstration. Add extra traps (blocking and non-blocking) to understand exactly how traps and events work together.

<vexi xmlns:ui="vexi://ui">
    <ui:box tag="outer">
        vexi.ui.frame = thisbox;
        <ui:box id="innerbox" tag="inner" />
        
        var showTag = function(v) {
            vexi.log.info(trapee.tag+": "+trapname);
            cascade = v;
        };
        
        $innerbox.Press1 ++= showTag;
        $innerbox.KeyPressed ++= showTag;
        thisbox.Press1 ++= showTag;
        thisbox.KeyPressed ++= showTag;
        $innerbox._Press1 ++= showTag;
        $innerbox._KeyPressed ++= showTag;
        thisbox._Press1 ++= showTag;
        thisbox._KeyPressed ++= showTag;
        
    </ui:box>
</vexi>

Friday, July 20, 2007

Fortress

Fortress is a castle building game written in Vexi which is in the early prototyping stages. The game is isometric so a full isometric display engine is being built in Vexi. This showcases the flexibility of Vexi as well as pushing it's performance, and may lead to a number of new features for Vexi as a platform.

Wednesday, July 11, 2007

Changing Themes

I just made a change to the surface widget that makes theming really really easy should you want a custom widget theme.

<vexi xmlns:ui="vexi://ui" xmlns="vexi.widget">
    <ui:box theme=".path.to.custom.theme" />
    <surface />
    <ui:box>
        // your app starts here
        vexi.ui.frame = thisbox;
    </ui:box>
</vexi>

If you omit the box declaring your theme, vexi.widget.surface will apply a default theme for you. Nice and simple and no hardcoding - and it even opens the door for individual themes per surface with some more clever updates to vexi.widget.surface and the core.

For the curious, the way vexi.widget.surface automatically handles the theming is rather straightforward. If the theme attribute is defined, it applies to as a trap to the namespace vexi.theme, if not it just applies a default theme to that namespace:

// no theme has been specified by the user
if (thisbox.theme == null)
    theme = vexi..org.vexi.theme.win2k;
// set up the theme
vexi..vexi.theme ++= theme;
// apply the theme surface template
vexi..vexi.theme.surface(thisbox);

Fairly easy, eh? The downside is that you must apply vexi.widget.surface for theming to work but the reality is that few (if any) of the widgets will work without it being applied to the root boxes of your applications.

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?

Saturday, July 07, 2007

Vexi Blog Revival

There are too many cool Vexi developments to keep this thing closed, so it's coming back. I'm going to be blocking about all the cool things going on around Vexi and lots of cool tricks you can do in it. Look out for more posts in the near future on vexi.blogspot.com! :-D