Skip to content
Devin Samarin edited this page May 11, 2011 · 1 revision

The SUIT Canvas Toolkit Architecture

The following is a overview of how the SUIT Canvas Toolkit is set up and organized into sections, and how drawing and events are handled. Development is mainly based in the javascript/ folder where all of the JavaScript files are organized. Generally, each constructor object has its own JavaScript file. For example, each widget is in its own file to make sure everything is organized and that everyone can work on different parts of the toolkit without messing in the same file. Of course, these files need to be loaded in a specific order. A file named __dependencies__.json is in the javascript/ folder which is used to help sort each file so it can be loaded in the right order.

Dependencies File

The dependencies file is used to keep track of which JavaScript files depend on what, so that each file can be loaded in the right order. The format is JSON. It consists of a single JSON object where the keys represent a JavaScript file and the value of the key is an array of files that it depends on (or false if it has no dependencies).

Here is an example:

{
	"Foo": false,
	"Bar": ["Foo"],
	"Baz": ["Foo", "Baz"]
}

In this example, Foo.js will be one of the files that is loaded before anything else, because it has no dependencies. Notice how the file extension is left out. Next, Bar.js depends on only Foo.js, so that is loaded next. And finally, Baz.js is loaded last since is depends on Foo.js and Baz.js to exist. There is PHP code which takes this information and outputs <script> tags in the correct order. This should be ported to JavaScript later on.

The SUIT Namespace

To avoid problems with SUIT objects interfering with user-code, all SUIT objects are members of the suit namespace. So to create a new Button object, you would use new suit.Button();

SUIT.js

This file initializes the suit namespace object and defines some standard (and useful) functions which some browsers may not include. It defines:

  • Function.prototype.inherit: SUIT function; returns an empty prototype object that inherits from the function's prototype chain
  • Function.prototype.bind: ECMAScript 5 function
  • Array.isArray: ECMAScript 5 function

Inheriting

To prevent mismatched code, all objects inherit from its parent the same way. This section describes that. First, the prototype needs to be created so that calls on the children call on the parent. This is done using the Function.prototype.inherit function provided in SUIT.js. Here is a quick, example usage:

suit.Widget.prototype = suit.Object.inherit();

Now, suit.Widget.prototype is ready to append its own methods. Now this is not enough, as some object propertes need to be set from suit.Object when it is constructed. To solve this, we use constructor stealing. Here is an example of how it works:

suit.Widget = function() {
	suit.Object.call(this);
	this.parent = null;
	this.screen = null;
	this.event_mask = suit.Event.None;
};

Notice the line with suit.Object.call(this);. This makes sure that the properties that the suit.Object constructor sets are set before the Widget construtor can apply its own properties. Last but not least, it may be necessesary to call add extra code to a method that an object inherits. The method may be overwritten in order to change the behavior of that function for the object. If that occurs, you can access the method directly from the prototype of the parent constructor. Here is an example:

suit.Widget.prototype.emit = function(signal) {
	suit.log("This widget is about to emit a signal: '%s'", signal);
	suit.Object.prototype.emit.call(this, signal);
	suit.log("This widget emitted a signal: '%s'", signal);
};

Now when you call the normal emit method on an object, if it is a widget, you will get logging info as well as the normal operation inherited from suit.Object.

Widgets

Widgets are a necessary feature in any widget toolkit. suit.Widget is an abstract constructor that is not created directly. They are only used to inherit from. Any constructor that inherits from suit.Widget must implement the following functions:

  • draw(context)
  • get_request_mode()
  • get_preferred_width()
  • get_preferred_height()
  • get_preferred_width_for_height()
  • get_preferred_height_for_width()

For a bare-minimum widget, you need to implement functions that state what your widget looks like, and what size you want it to be.

Containers

You can't talk about widgets unless you talk about widget containers as well. Containers are used to lay out children or provide some functionality that require widgets to be its children. suit.Container is also abstract and it inherits from suit.Widget. Instead of creating a new suit.Container(); directly, you create one of its subclasses.

Widget Drawing

This section describes how the widgets are drawn. When a widget requests to be re-drawn (using queue_redraw()), it notifies the Screen object that widget X needs to be redrawn (it has changed it appearence). Then, when it can, Screen clips drawing to the widget's allocation and calls the draw method on the widget with a passed suit.Graphics to render to. The widget then does it thing. When that is complete, Screen does the process all over again for the widget's children (if it has any).

Widget Sizing

This is a little more complicated and I'm too tired to really explain all the way (at this moment), but basically when a layout container gets a new allocation, gets notified that one if its children needs to be resized, or when its list of children changes, it goes through each children and asks what size it wants to be (or prefers to be). It then calculates this up and gives allocations to its children.