Homework 3_Inter: Interactor Design Guide for Homework 3

05-830, User Interface Software, Fall, 2004

Assigned: October 5, 2004
Due: November 2, 2004


The last time most people used the Interactor model. This file describes the specifications for Interactors that you can use. However, you can feel free to deviate or augment this description.


 

Interactors

An interactor is an object that can be attached to a group to make it respond to mouse or keyboard events. You will write three kinds of interactors:

Each interactor can be modelled by a finite state machine with three states:

Transitions between idle and running states are triggered by input events, such as mouse presses or mouse releases.

All interactors should implement the Interactor interface:

public interface Interactor {
     public Group getGroup ();
     public void setGroup (Group group);

     public int getState ();
         public static final int IDLE = 0;
         public static final int RUNNING_INSIDE = 1;
         public static final int RUNNING_OUTSIDE = 2;

     public Event getStartEvent ();
     public void setStartEvent (Event mask);

     public Event getStopEvent ();
     public void setStopEvent (Event mask);

     public void start (Event event);
     public void running (Event event);
     public void stop (Event event);

}

getGroup() accesses the group that the interactor is attached to, and setGroup() attaches it to a new group.

getState() returns the current state of the interactor: IDLE, RUNNING_INSIDE, or RUNNING_OUTSIDE.

getStartEvent() and getStopEvent() access the events that trigger the interactor. Start events make the interactor transition from idle to running. Stop events make it transition from running back to idle.

start() is called when the start event occurs. It should do everything needed to start the interactor and put it in a running state. start() may perform additional tests to decide whether or not the interactor should be started.

running() is called whenever the mouse moves while the interactor is running. It should update the state of the interactor and determine whether the mouse is inside or outside the area of interest.

stop() is called when the stop event occurs. It should stop the interactor and return it to an idle state.

To make your interactors work, you'll have to write a class that handles Java mouse and keyboard events and makes the appropriate state changes to the interactors. We will provide a class WindowGroup that implements Group and displays its children in a Java window. You will write a subclass, InteractiveWindowGroup, which listens for Java input events on the window. Your subclass should have at least these methods

public class InteractiveWindowGroup extends WindowGroup {
     public void addInteractor (Interactor inter);
     public void removeInteractor (Interactor inter);
}

Note that the InteractiveWindowGroup represents the window. It is both a JFrame and a Group. There will only be one InteractiveWindowGroup per window.

Whenever your InteractiveWindowGroup gets a Java input event, it should scan its collection of interactors and call start(), running(), and/or stop() as appropriate. The input event should be passed to start(), running() or stop() as an instance of the Event class:

You can  make the simplifying assumption that there can only be one interactor running at a time. This means that when an Interactor is already running, the InteractiveWindowGroup can check each event to see if it is the stop event for that running interactor, and if so, call stop on the interactor, and otherwise call running on the interactor. If it calls stop, then it should check using getState after stop returns to see if the interactor is no longer running (if state == IDLE).

public class Event {
     public Event (int id, int modifiers, int key, int x, int y);
     public int getID () { 
     public int getModifiers ();
     public int getKey ();
     public int getX ();
     public int getY ();
     public boolean matches (Event event);

     public final static int KEY_DOWN = 0;
     public final static int KEY_UP = 1;
     public final static int MOUSE_DOWN = 2;
     public final static int MOUSE_UP = 3;
     public final static int MOUSE_MOVE = 4;
}

id is one of the five values KEY_DOWN, KEY_UP, MOUSE_DOWN, MOUSE_UP, or MOUSE_MOVE. MOUSE_MOVE indicates that the mouse has moved but no other event has occurred.

modifiers is a bit mask of CTRL_MASK, SHIFT_MASK, ALT_MASK, BUTTON1_MASK, BUTTON2_MASK, or BUTTON3_MASK, indicating which keyboard keys or mouse buttons are currently pressed. These constants are defined in java.awt.event.InputEvent.

key is the same as java.awt.event.KeyEvent.getKeyCode(). For letters, numbers, and punctuation, it is the character's uppercase ASCII value, like 'A' or '?'. For special keys on the keyboard, like F1 or Page Up, it is one of the VK_ constants defined in java.awt.event.KeyEvent. For a mouse event, key is 0.

x and y are the last known x,y position of the mouse. When an event is passed to an interactor, (x,y) should be in the coordinate system of the group that the interactor is attached to, not the coordinate system of the whole window.

matches() returns true if two events match. This method is used to test whether an input event matches the start event or stop event of an interactor. matches() compares only the id, modifiers, and key fields of the two events -- the mouse position doesn't matter.

Here are some examples of events:

MoveInteractor

A move interactor moves a graphical object around in its group. It has only one required constructor and no required methods:

public class MoveInteractor implements Interactor {
     public MoveInteractor ();
}

A move interactor should start running only if the start event happens when the mouse is over a graphical object in its group. While it is running, it should use moveTo() to make the object follow the mouse. When the mouse goes outside the group, the interactor should stop moving the object, so that it can't be dragged outside the group's clipping area. When the stop event occurs, the interactor should stop moving the object.

ChoiceInteractor

A choice interactor selects one or more graphical objects in a group.

public class ChoiceInteractor implements Interactor {
     public ChoiceInteractor (int type, boolean firstOnly);
     public List getSelection ();

     public static final int SINGLE = 0;
     public static final int TOGGLE = 1;
     public static final int MULTIPLE = 2;
}

The two parameters to ChoiceInteractor affect what kind of selection it makes, as discussed on the homework3 page.

getSelection() returns a java.util.List containing all the currently-selected objects.

In order to be selectable by a choice interactor, a graphical object must implement the Selectable interface:

public interface Selectable {
     public void setInterimSelected (boolean interimSelected);
     public boolean isInterimSelected ();
     public void setSelected (boolean selected);
     public boolean isSelected ();
}

These methods can be used by the graphical object to change its appearance. "Interim selected" means that a running choice interactor is currently selecting the object. Interim selection is always turned off when the interactor stops. "Selected" means that the object was interim-selected when the choice interactor stopped. For example, a graphical object implementing a radio button item might change how it is drawn based on whether it is interim-selected, selected, both or neither.

A choice interactor should start running only if the start event happens while the mouse is over a graphical object in its group that implements Selectable. It should update the interim selection as the mouse moves around. Finally, when the stop event occurs, the interactor should clear the interim selection and make the final selection.

To demonstrate selection feedback, you will write a new Group:

public class SelectionHandles implements Group, Selectable {
     public SelectionHandles (Color color);
}

Usually, SelectionHandles will have exactly one child. The SelectionHandles group should keep its bounding box fit tightly around its child (but leaving enough room to draw handles so the size doesn't change when handles are drawn). Whenever SelectionHandles is selected or interim-selected, it should display a small, filled square at each corner of its child object.

NewInteractor

A NewInteractor creates new instances of a class of graphical objects:

public abstract class NewInteractor implements Interactor {
     public NewInteractor (boolean onePoint);
     public abstract GraphicalObject make (int x1, int y1, int x2, int y2);
     public abstract void resize (GraphicalObject gobj, int x1, int y1, int x2, int y2);
}

make() creates a graphical object from point (x1, y1) to point (x2, y2). This method is declared abstract. It should be overridden in a subclass of NewInteractor, which decides which graphical object to create and how to interpret the (x1,y1) and (x2, y2) coordinates.

resize() adjusts a graphical object created by make() with new points. (x1,y1) is the anchor point, which should be the same as was passed to make(). The point (x2,y2) follows the mouse cursor, so it will be different.

If the onePoint parameter to the constructor is true, then the new interactor needs only one point to create the object. It calls make(x, y, x, y) to create the object, then stops immediately after starting, never calling resize(). This is useful for fixed size objects, like icons.

When a NewInteractor starts, it should call its own make() method to create a new instance of a graphical object. The NewInteractor should add the object returned by make() to the group, so it will appear on screen immediately. While the interactor is running, it should resize the new object to follow the mouse (assuming onePoint is false). When it stops, it should leave the object where it is.

You will create two subclasses of NewInteractor:

These classes should have the following constructors:

public NewRectInteractor (Color color, 
                          int lineThickness);

public NewLineInteractor (Color color, 
                          int lineThickness);

Each class should also have a getParam() and setParam() method for each parameter in its constructor.

Note that your NewLineInteractor and NewRectInteractor don't have to create pure Line and Rect objects. You may want to create lines wrapped inside a SelectionHandles group, or perhaps a subclass of Line that implements Selectable and draws selection-handle feedback itself.

Resources

Files:

All the files can be found in this ZIP archive:

hw3-files.zip

Back to Homework Overview
Back to 05-830 main page