Basic user interface construction

In SiteFusion, all user interface building blocks (XUL elements) are objects of a class descending from the Node class. There are two basic kinds of nodes, Node and BranchNode. The difference is that classes extending BranchNode can contain child nodes, and classes extending Node can not. The child nodes of a BranchNode are accessible through the BranchNode::$children property. Child nodes hold a reference to their parent node in the Node::$parent property. For more information on which XUL elements there are and what they do, Mozilla Developers Center has the answers.

When a node object is constructed it only exists in PHP on the server. To bring it into existence on the client side it must be registered, by becoming a child of an already registered node. The only registered node in an empty application is the root window, it is an object of the XULWindow class and contained by the Application object under the Application::$window property. Nodes can be added to the root window or to each other with the BranchNode::addChild(), BranchNode::addChildFirst(), BranchNode::addChildBefore() and BranchNode::$addChildAfter() methods. When a node becomes registered it is able to send JavaScript commands to the client side, and to receive event messages back. Unregistered nodes can also become children of other unregistered nodes. When the root node becomes registered, its children automatically will be too. To find out if a node is registered, use the Node::$isRegistered property. The binding between the two parts is done through the Registry. This is a layer mostly invisible to a regular application, where remote object construction, linking and removal is conducted.

All attributes of the XUL counterpart of a node can be set through methods on the PHP object. Most nodes offer a, for lack of a better word, 'dynamic constructor'. This means that the constructor does not have fixed positions for arguments, but rather an order and type based determination of what is what. BranchNode constructors always accept any number of child nodes or arrays of child nodes as the last arguments. The advantages are that a dynamic constructor lets you set the most common attributes of an element and directly construct new childnodes within the constructor arguments, allowing tight, clean and easy on the eyes code. For instance the XUL element 'box', represented by the PHP class XULBox, has the attributes 'flex', 'align' and 'pack', which respectively determine how much relative available space the box is going to take up, how it's going to align its child nodes and where it is going to pack them. Constructing a box that will take up one relative space unit, align its children to the center of the box and pack them at the end position would look like this:

<?php

$box = new XULBox( 1, 'center', 'end' );

If it were to have an empty textbox and a button that says 'Press me' as child nodes, it would look like this:

The constructor has a list of attributes to look for, ordered by type and priority. The flex value of a XUL element is an integer number, and the align and pack values are strings. The flex value is most used, so it is looked for first. If the first argument is an integer, it is taken as the flex value. If the first argument is a string, or if the first argument turned out to be an integer and thus the flex value and the second argument is a string, it is considered to be the align value, because it is usually more often used than the pack value. If a second string follows after the first, it is the pack value. This means that the first two arguments can be set independently and the third can only be set when the one preceding it with the same type is set.

This may look somewhat odd and confusing, however it saves a lot of typing in big constructions and this actually allows for a XUL/XML like code structure with a lot of nested constructors, and the containing constructor spanning many lines. Compare the following:

Notice the chaining of method calls. Most node methods return a reference to the self ($this) when called with a value. If called without parameters, they return the current value or NULL if none is set.

A full list of methods and constructor attributes for all nodes can be found in the Documentation section.

Nodes can also be detached and reattached somewhere else. In the process, they loose their original registration, become PHP only objects again, and then get registered again.

<?php

$box = new XULVBox($topBox = new XULHBox($checkBox = new XULCheckBox),$bottomBox = new XULHBox());

Notice the difference between extracting and removing. PHP is still vulnerable to memory leaks due to undetected circular referencing, and because of this removed nodes are often kept in memory even when the program holds no references to them anymore. This is because nodes always reference their child nodes and parent node, and thus create circular references. To enable PHP to free the memory, we need to break those references. The methods BranchNode::removeChild() and Node::removeNode() do this. Nodes removed with these methods lose their parent-child relations. If you want to leave their parent-child structure intact, use BranchNode::extractChild() or Node::extractNode().