Interactors provide a model for event-handling that allows a programmer to organize events into high-level behaviors rather than treating them as a series of low-level mouse and keyboard events. The original interactors model was based on the observation that most events in graphical user interfaces can be categorized into one of a small number of behaviors that include:
This observation allowed Brad Myers, the creator of the interactors model, to create a set of behavior classes that contained a rich set of parameters that a user could set to customize the behavior without having to do any programming. For example, he devised a class for a move behavior that had parameters which among other things controlled which events started and stopped the behavior, which objects the behavior operated on, which coordinates were set (just the x, just the y, or both), whether the object being moved was or was not snapped to a grid, and whether the object itself was moved as interim feedback or whether an interim feedback object was used instead.
Interactors are implemented using the strategy pattern.
A behavior determines which method to call based on the type of event that it receives. Events can be divided into four types:
Additionally a behavior may have an operating region. If the mouse cursor moves outside this region, then the outside action should be called and should continue to be called as long as running events are received while the cursor is outside the region. When the cursor re-enters the region, the back inside action should be called.
There are three possible ways in which a designer of an interactors class might have the programmer specify the set of objects on which the interactor operates:
The first option matches the way users attach event listeners to Swing components and hence seems most natural for Java implementations. However the other designs might work well in other languages. Additionally, the descriptive parameter approach can provide a powerful way to specify sets of objects without forcing the programmer to remember to associate an object with an interactor each time an object is created.
An interactor may either directly modify the objects on which it operates or allow the programmer to determine how to modify the objects by overriding one or more of the interactor's action methods. Typically the interactor will provide a property that allows the programmer to specify how the graphical objects should be modified. For example, a move interactor might provide a box_xy and a line_xy property to allow the programmer to specify which coordinates should be changed by the interactor (e.g., X, Y, Both, or Custom). If the X, Y, or Both options were supplied, then the interactor would directly set the corresponding properties in the object it is operating on. If the Custom option was specified, then the interactor would allow the programmer to perform the modifications using the start, running, and stop actions.
In order to modify an object's properties the interactor needs to know which methods it can use to perform the modifications. Hence the interactor will typically define an interface that specifies the set of methods that objects on which it operates needs to implement. For example, a move interactor would need to have get and set methods for the left and top properties in order to work on box-like objects and it would need to have get and set methods for the x1, y1, x2, and y2 properties in order to work on line-like objects.
Interactors can either be exclusive, in which case they will be the only interactor executing, or non-exclusive, in which case they can run in parallel with other interactors. If a non-exclusive interactor is running then an exclusive interactor should not start running since it would assume that it was the only interactor running and problems might arise.
Since interactors might vie for the same event, and since interactors can prevent others from running depending on their exclusivity property, it often helps to give interactors priorities. Interactors with higher priorities will receive events first and have the ability to act on them. If an interactor is a "one-shot" interactor that starts and stops on the same event, then the interactor might complete its processing and allow the event to be passed to any other interested interactors. Alternatively the interactor might want to continue its execution, in which case lower priority interactors might not get a chance to act on the event.
Three tasks must be accomplished in order to implement an interactors model:
Each interactor implements the same type of finite state machine, although each interactor has its own private copy of the finite state machine. Since every finite state machine is implemented using the same code, the finite state machine code should be placed in the root class for interactors.
The finite state machine method should implement the following state table:
|Current State||Event||Action to Execute||New State|
|Start||Start Evt||Start Action||Running|
|Running||Stop Evt||Stop Action||Start|
|Running||Running Evt||Running Action||Running|
|Running||Abort Evt||Abort Action||Start|
|Running||Outside Operating Region||Outside Action||Outside|
|Outside||Running Event and Outside Operating Region||Outside Action||Outside|
|Outside||Back Inside Operating Region||Back Inside Action||Running|
|Outside||Stop Evt||Stop Action||Start|
|Outside||Abort Evt||Abort Action||Start|
As noted earlier, the event handler needs to receive events from the window manager and dispatch them to the correct interactor. In order to receive events from the window manager it may be necessary to attach the event handler to a window. For example, in Java one would create mouse and keyboard listeners whose methods called the interactors' event handler. The event handler would presumably be a method in a top-level interactors object. It might be a static method in the interactors root class or it might be contained in an interactors' implementation class, in which case the appropriate implementation object would need to receive the call.
The event handler should implement the following algorithm:
The action methods all do some generic stuff:
The interactors model has both a number of advantages and a number of disadvantages. The advantages include:
The disadvantages include: