Creating Widgets in Control, ep.1

Creating custom widgets is a two step process that gives you complete freedom over what's happening in your interface. It may require some knowledge of Object Oriented Programming and a basic understanding of the event handling system, but you'll see that it's not that hard.

Step 1 : Create the class

Basically, you just need to declare a function as a property of the window object. It looks like this :

window.UberWidget = function() {

};

This is enough to bring your ÜberWidget to life, but it won't do anything. You first need to make it part of the Control Widget family.

In technical words, your newly created class must extend the Widget class, inheriting of all its methods and properties. In Javascript, inheritance is controlled via the function's prototype, like this :

UberWidget.prototype = new Widget();

Each time a new UberWidget will be added to the layout, it will include all methods and properties found in the Widget class, plus the ones that we'll add later. The Widget class provides useful functions to handle creation, sizing, input handling and value updates.

The Widget creation process requires two pieces of data. First, its context, that is to say the HTML page element hosting your interface. Then, a bundle of the different properties defined in the JSON, i.e. its name, bounds, label, destination…

To make it short, you will often begin with this code :

window.UberWidget = function( ctx, props ) {
	this.make( ctx, props );
};
UberWidget.prototype = new Widget();

The main UberWidget function receives some information and passes it to the Widget creation function through the make() function.

It is the bare minimum to define a Widget, though this one will not display anything nor respond to events. We'll see later where to go from here.

Step 2 : Add your widget to the layout

Once your class is ready, you need to reference it in your layout to make it appear on the screen. This step is really simple, just use the class name like you would do with any other widget.

Here's the complete code, with the UberWidget filling the whole screen.

loadedInterfaceName = "ÜberWidget Tutorial";
interfaceOrientation = "landscape";

pages = [[
{
	"name": "uber",
	"type": "UberWidget",
	"bounds": [0, 0, 1, 1]
}
]];

window.UberWidget = function( ctx, props ) {
	this.make( ctx, props );
};
UberWidget.prototype = new Widget();

Note that if you load such an interface, your screen will be entirely blank with no way to get back to the menu. You should always include a "Menu" button in some corner, or you'll break your navigation.

Another common need is a Refresh button, very useful when your are developing your own widget. It reloads the interface from its original location and allows you to test your code changes very easily.

Here's an updated version of the above code, including the Menu and Refresh buttons :

loadedInterfaceName = "ÜberWidget Tutorial";
interfaceOrientation = "landscape";

pages = [[
{
	"name": "uber",
	"type": "UberWidget",
	"isLocal": true,
	"bounds": [0, .1, 1, .9]
},
{
	"name": "refresh",
	"type": "Button",
	"bounds": [.6, 0, .2, .1],
	"isLocal": true,
	"mode": "contact",
	"ontouchstart": "interfaceManager.refreshInterface()",
	"stroke": "#aaa",
	"label": "Refresh"
},
{
	"name": "menu",
	"type": "Button",
	"bounds": [.8, 0, .2, .1],
	"mode": "toggle",
	"stroke": "#aaa",
	"isLocal": true,
	"ontouchstart": "if(this.value == this.max) { control.showToolbar(); } else { control.hideToolbar(); }",
	"label": "Menu"
}
]];

window.UberWidget = function( ctx, props ) {
	this.make( ctx, props );
};
UberWidget.prototype = new Widget();