Doodle - Part 4: More Power to the User / Page 2 | WebReference

Doodle - Part 4: More Power to the User / Page 2


[previous] [next]

Doodle - Part 4: More Power to the User [con't]

Controls

In order to offer a range of shape types, the Doodle application must provide some way to select the type of shape that will be inserted next. This information will be used by Doodle whenever the user clicks on the canvas. There's an important distinction between this kind of information (application state) and the information stored in the document; application state has no effect on the structure of the document and only affects how the user interracts with the document or application. We need a mechanism that will monitor the user's choice of shape and modify the application state appropriately when the user changes the selection. In other words, we need a control.

The Doodle.Control class provides the base class for all controls in the Doodle application.

While the class itself doesn't do much, it specifies a callback interface (the onUpdate() function) that the application will use to transmit state changes to its controls. Concrete control classes will inherit from this and provide the binding between onscreen user-interraction and the bit of application state they control.

The drop-list is a simple user input device that allows the user to select a single item from a from a set of predefined options. This control will be managed by Doodle.Control.DropList

The DropList is constructed with the select object which it will use to interact with the user along with get and set functions which it will use to interract with the application. The initial state of the select object is assigned and the onchange event is handled by a member function.

When the selection object changes, the DropList transmits the change to the application by calling the setValue() function. If the application state changes, the onUpdate() function gives the DropList the opportunity to modify the onscreen value. In either direction, the new value is compared with the old to avoid problems with infinite loops.

To keep things simple, the set of options is defined in HTML.

The HTML code provides a simple translation from human readable values such as "Point" and "Line" to Application oriented values like 0..3.

Now everything is ready to configure the Doodle application with the new control.

The bindControl() function binds an application value type (identified by its enumeration) with a control object, in this case the Doodle.Control.DropList instance. The DropList is constructed with the select object and the get and set functions for the ShapeType property. Note the use of the Function.prototype.bind() function to provide Function instances for the DropList that will act on the Doodle instance.

The bindControl() method binds a control to an enumerated value type. This enables the Doodle application to keep track of what controls are connected to what values.

Controls may be updated for each value type by iterating through the list and calling onUpdate().

The ShapeType property is handled by two functions.

If the ShapeType value is modified, the updateControls() function is called to alert any controls bound to that value that the value may have changed. Note that the value is not passed to the control, only the value type enumeration. The control will retrieve the value from the application in its onUpdate() call.

The Canvas Reacts

All that's required now is to wait for the user to click somewhere on the canvas to begin inserting a new shape. When called for the first time, the onMouseDown() function uses the application's shape-type value to create the appropriate widget.

Since each widget expects different amounts and kinds of user interaction to create the completed shape, the canvas will do little more than create the widget and let it decide what to do. If a widget is already being edited (indicated by this.primaryWidget), the canvas calls into the Widget's onMouseDown to handle the mouse event. Similarly onMouseMove and onMouseUp call directly into the current primaryWidget.


[previous] [next]