Vexi Core Reference

Vexi Platform 1.0 Release

Charles Goodwin

charlie@vexi.org

15th August 2004

Reference Version 1.0pre3

Preface

About This Document

The goal of this document is to describe the Vexi Core behaviour in detail, to facilitate development usage of the Vexi Core. This document is not a tutorial, and unsuitable for people unfamiliar with Vexi. Neither is it a specification and hence may not provide enough detail for a reimplementing Vexi, the Core or the Platform.

Any queries regarding this document or about Vexi should be sent to the users@vexi.org mailing list, which can be subscribed to from http://lists.vexi.org on the Vexi website.

Vexi Platform Components

Platform The combination of the following components

Core The native core (or Java byte-code) that powers the UI

Launcher A small mini-application that "shoehorns" the Core onto the
client PC and is beyond the scope of this document

UI / Application The set of files, bundled as a zip file with a ".vexi" extension,
that are used to create and control a Vexi UI

Widgets An official set of Vexi UI files that provide usable widgets
for use in Vexi applications

Required Knowledge

ECMAScript / Javascript ECMAScript (aka Javascript) controls Vexi UI behaviour

XML Vexi UIs are described using XML

Key Concepts

VexiScript The enhanced version of ECMAScript used by Vexi

Put / Write In VexiScript, when you change the value of something you are
putting or writing to it e.g. 'foo = 5' puts 5 to 'foo'

Get / Read In VexiScript, when you access the value of something you are
getting or reading it e.g. 'return bar' reads (then returns) 'bar'

Application Life-Cycle

<describe the life-cycle of a Vexi application, with diagram>

Vexi Fundamentals

Boxes

The fundamental building block for Vexi UIs is the box.

A box may contain other boxes, which is referred to as the parent-child relationship. This is a one-to-many relationship; a parent box may contain many children, but a child box can only have one parent. A box may be a parent, or a child, or both, or neither. A parent-less box is considered an orphan.

The parent of a box and any subsequent parents are referred to as the ascendants of the subject box. The children of a box and any subsequent children of the child boxes are referred to as descendants of the subject box.

The combination of a box and it's ascendants and descendants is referred to as a box tree. The top box of a box tree is referred to as a root box or a surface box.

There are three ways in which to consider a box:

Visual Representation As a rendered object on-screen

Object Representation As a VexiScript object

XML Representation As an XML node (tag)

Boxes are encapsulated objects. That means a child box has no direct way of accessing or ascertaining information about it's parent.

New boxes are created using the <ui:box /> XML tag or by reading from vexi.box (the box property on the Vexi Object).

Surfaces

A top-level window in a Vexi UI is referred to as a surface. There are four kinds of surfaces available to Vexi applications (see the Vexi Object for information on creating surfaces):

Frame A fully-fledged application window decorated
with platform specific borders and buttons

Window A fully-fledged application window but without
any decorations

Dialog A modal dialog attached to an application
window, with platform-specific decorations

Popup A modal dialog, attached to an application
window, but without any decorations

All surfaces are scarred for security reasons. See the appendix on Security Precautions for further information on surface scarring.

Whenever we refer to the size or position of a surface box, we refer to the UI accessible portion of the surface, disregarding any platform specific decoration. This has the caveat that if you set the position of a frame or dialog to (0,0) then the platform specific title-bar will be placed off-screen.

Surface boxes are not directly accessible as VexiScript objects to any descendants of a surface box. However, every box has a object, accessed as 'surface', associated with it which is know as the surface object. Whenever reading this object, the Core recursively reads the surface object of the parent and returns a value when it is encountered. When a box changes surface, including addition and removal, then 'true' is put to the value of this object, and the surface object will read the value of the new surface object. By default, there is no value for the surface object; this is implemented in widget space.

Templates

Boxes are combined to form more useful widgets using templates. Templates are defined using XML files with the .t extension.

The root node of a template is always <vexi />. Any non-XML content in the vexi node is interpreted as VexiScript and forms the static portion of a template. The static content of a template is executed only once when the template is first used. Any reference to the static content of a template is referring to the same object.

The vexi node can contain the following XML nodes:

<ui:box />

Another template e.g. <.path.to.valid.template />

Any custom node using the meta namespace e.g. <meta:foo />

Of the non-meta namespace nodes, only one can have content. The single tag nodes are pre- or post-applied to the template in the order in which they are defined.

Example:

<vexi xmlns:namespace1=".name.space.one">
<meta:doc>I wrote this!</meta:doc>
// static content goes here
<.name.space.two.foo />
<namespace1:bar />
<ui:box>
// regular content goes here
</ui:box>
</vexi>

The Box Model: Layout and Rendering

Box Semantics

A box can have 0 or more child boxes. Child boxes are assigned an index based on their order within the parent, which is numbered '0' (for the first child) to 'n-1' (for the last child).

A parent box has the numerical properties 'cols' and 'rows'. If one is set to a value greater than '0', the other is set to '0'. If one is set to '0', the other is set to '1'. The value of one of either cols or rows for a box is always 0; they can never be simultaneously greater than 0. Whichever of the 'cols' or 'rows' properties has a value greater than zero will always read that value and is considered the constraint for packing child boxes into columns and rows, with the property whose value is '0' being dynamic with no limits.

Child boxes are either packed or non-packed, defined by the boolean property packed.

Packed Boxes

A packed box is a box whose packed property is true.

Packed boxes are placed, in order, into columns and rows. If the constraining property on the parent box is 'cols' then packing is done by placing the next child box into the next available column space, otherwise it is done by row.

A packed box's width is set to the aggregation of the widths of the columns it spans. If this width exceeds the box's maxwidth, if it's hshrink property is false, or the box's content width, if it's hshrink property is true, then the box is sized to it's maxwidth or content width respectively and then aligned horizontally according to it's align attribute within the space occupied by it's spanned columns. This is then repeated for rows and height.

Non-packed Boxes

A non-packed box is a box whose packed property is false.

Non-packed boxes are placed within the parent, irrespective of the parents columns and rows, according to their align property and are offset by their x and y properties.

Grid Layout

Note: [ brackets ] are added for clarity only.

A box's content width is the maximum of [ the minimum of the aggregation of it's columns' minimum widths and the box's maxwidth property ] and the box's minwidth property.

A column's minimum width is the maximum content width of all the boxes in that column.

A column's maximum width is the maximum of [ the maximum maxwidth from each of the column's boxes whose hshrink property is false ] and [ the maximum content width from each of the column's boxes whose hshrink property is true ], but a column's maximum width is never smaller than it's minimum width.

A box with colspan occupying several columns will never affect the minimum or maximum width of a column any more than is necessary to render the box according to it's minwidth and maxwidth properties. A box with colspan will only ever affect the last column it spans.

A column's actual width is then determined by the space available in the parent, but will never be less than the column's minimum width. If the aggregation of the columns' maximum widths is less than the parent box's maxwidth then the remaining space, also called 'slack', is spread equally over the columns.

This is all then repeated for rows and height.

Further Information on Vexi Grid Layout

The Vexi Core grid layout engine is scalable with regards to columns, rows, and spanning children. What this means is that the grid layout for a parent box with 10,000 columns and a child spanning 10,000 columns will be calculated as fast as the single column equivalent.

Box Rendering

Boxes are rendering "back to front" in Vexi, with parents being placed behind children and children rendered in order of index. Only the minimum area needed to update the screen to reflect the current box tree contained in a surface will be rendered at any given time.

They way a box is rendered is controlled by it's rendering properties. These control how a box adapts to the space provided and the children inside of it.

Box Properties

A box is also accessible as a VexiScript object, and with key-value pairs that can be used to control the behaviour of a box.

Layout

Rendering

These properties effect how a box is visually presented on-screen.

Property
Default Value
Description
fill (string)
font (stream)
fontsize (int)
text (string)
textcolor (string)
path (string)
strokecolor (string)
strokewidth (string)

VexiScript

About VexiScript

VexiScript is an adaptation of ECMAScript, Standard ECMA-262, which is most popularly implemented as Javascript. VexiScript implements a few additional features to ECMAScript. There are also some omissions and inconsistencies between VexiScript and ECMAScript.

A familiarity with ECMAScript / Javascript syntax is assumed.

Declare Before Use

In VexiScript any variables must be declared before they can be used.

Private variables are initialized using the 'var' keyword:

// a private variable
var my_var = "only my scope can see me";

Public variables, also known as box properties, are initialized using the 'thisbox' keyword:

// a box property (public variable)
thisbox.my_prop = "any box can see this";

Box properties only need to be declared once per box, and the standard box properties are already declared by the Core.

Traps

Traps are a feature unique to Vexi and it's predecessors. They can be likened to events, except the work on any property and events are just one application of traps. The basic concept is that whenever something that is trapped is accessed, a trap is fired. There are two types of access and hence two types of traps: read traps and write traps.

Traps are assigned using the '++=' syntax. This is peculiar to VexiScript, and hence not part of ECMAScript. You can assign traps either anonymously or by reference:

// assign an anonymous trap
$box.some_prop ++= function(v) { /* do something */ }
// assign a referenced trap
var some_trap = function(v) { /* do something */ }
$box.some_prop ++= some_trap;

Traps can be unassigned using the '--=' syntax. You can easily unassign referenced traps:

// unassign a referenced trap
$box.some_prop --= some_trap;

You can only unassign anonymous traps from within themselves using the 'callee' keyword:

// unassign an anonymous trap
$box.some_prop ++= function(v) {
$box.some_prop --= callee;
}

Read Traps

Read traps are executed whenever a property is read in VexiScript. Read traps are created when the function has zero arguments.

// a read trap
$box.foo ++= function() { return 2 * cascade; }
// will output 10
vexi.log.info($box.foo);

Write Traps

Write traps are executed whenever a property is written to, in VexiCode. Write traps are created when the assigned function has one argument.

// a write trap
$box.bar ++= function(v) { cascade = 3 * v; }
$box.bar = 5;
// will output 15
vexi.log.info($box.foo);

Trap Cascades

Multiple traps can be added on a single property. The execution of the trap functions in order is called cascading.

The order of a trap cascade is the reverse of the order in which the traps are applied.

// trap 1
$box.prop ++= function(v) { vexi.log.info("FOO!"); }
// trap 2
$box.prop ++= function(v) { vexi.log.info("BAR!"); }
// outputs BAR! then FOO!
$box.prop = true;

The cascade can be bypassed by appending an underscore to the property name.

// trap 1
$box._prop ++= function(v) { vexi.log.info("FOO!"); }
// trap 2
$box.prop ++= function(v) { vexi.log.info("BAR!"); }
// outputs FOO! then BAR!
$box.prop = true;

Events