The Amulet research project is investigating ways
to make the prototyping, design, and implementation
of advanced user interfaces substantially easier.
The goals of Amulet are to be flexible and open
for researchers, easy to learn for students, and robust
and fast for developers.
Amulet is written in C++ and runs on Unix,
Microsoft Windows 95, and Windows NT,
and on the Macintosh.
Unlike other toolkits and interface builders,
Amulet provides high level facilities
to support the entire user interface, not just
the widgets like menus and scroll bars.
For example, this circuit design program
uses Amulet's built-in facilities
for selection handles, for creating, moving, and deleting
objects, for undoing all operations,
for loading and saving the diagrams, et cetera.
As a result, only about 850 lines of code
needed to be written for this entire application.
Amulet is a layered system.
The lowest layer, called Gem, provides
operating system and window manager independence
so that applications written using Amulet
can simply be recompiled and then will run on all platforms.
The look and feel can be changed to look like Motif,
Windows 95, or the Macintosh.
Amulet contains a novel prototype instance object
system implemented on top of the C++ object system.
In a prototype instance object system,
there is no distinction between classes and instances.
Any object can serve as a prototype for other objects.
Objects are represented as a collection
of slots and their values.
For example, we can pop up the inspector showing
the slots of this rectangle.
The slots shown in black are local to this rectangle
and change when the object is manipulated.
Other slots, like the line style,
are shown in blue to indicate that they are inherited.
If I set the slot's value, then the object changes,
and the slot is now shown in black since it
is no longer inherited.
If I now set the line style slot in the prototype rectangle,
all the other rectangles change since they are still inheriting
the prototype's value.
Method slots are treated exactly the same as data slots
and can be local or inherited in each instance.
Amulet's prototype instance object system
is an important advantage over other toolkits
because it allows the properties of objects
to be investigated and manipulated dynamically
at runtime.
It is simpler to learn and to use than conventional object
systems, since it unifies the concepts of instances
and classes and methods and instance variables.
An important feature in Amulet is that any slot of an object
can contain a constraint, which calculates the value.
Constraints can contain arbitrary C++ code and are
re-evaluated whenever the dependent slots change.
Constraints are used throughout Amulet.
In addition to controlling the positions of objects,
constraints are also used to compute the color of buttons,
depending on whether they are selected
or not, and even to compute the size of text objects
based on their current content and font.
Cycles of constraints are also supported,
which makes it easy to keep the application's data
consistent with the display.
For example, here the latitude and longitude
are updated when the objects are moved on the map.
And the objects are updated when the coordinates are edited.
Amulet supports multiple coexisting constraint solvers
since researchers like to explore
new ways to solve constraints.
Amulet includes a one-way formula solver,
an experimental multi-way solver, and a new animation
constraint system.
The animation constraints make it very easy
to add animations to an interface.
When an animation constraint is attached to a slot
and the slot is set with a value,
the animation constraint removes the new value,
puts the old value back into the slot,
and smoothly sets the slot with values
interpolated from the old value to the new value.
Animations can be set on slots of any type,
including the position, the color, and even the vertex list
for a polygon.
We have defined a number of animations
for controlling how objects appear and disappear,
including fading out, flying away, shrinking, and exploding.
All of these effects are provided in a library,
and can easily be added to applications.
For example, the animations were added to this checkers game
with just five lines of code.
On top of the object and constraint system,
the Opal graphics system provides a structured graphics
model.
When the programmer or user changes
a property of the object, Amulet automatically
withdraws the object and any other objects
that are covering it.
This makes it much more convenient for programmers
since the system automatically determines
which objects to redraw.
Amulet uses a novel input model where the graphical objects do
not handle input events instead a separate set of interactor
objects except all input and manipulate the graphics we have
found that there are only a few kinds of behaviors that
are used in direct manipulation interfaces
and Amulet provides these using only a few types
of interactors.
Each type handles a standard kind of behavior,
such as selecting one or more of a set of objects,
moving or growing objects with a mouse, or text editing.
The interactors are independent of the particular graphics
used.
So for example, the same choice interactor
can be used for palettes, menus, or even
to select objects in a graphics editor.
A special feature of Amulet's interactor
supports operations across multiple windows
using either a normal graphical object
or a window as the feedback.
This also supports drag and drop of objects.
Amulet supports operations that use
gestures where a single stroke specifies
not only the operation to perform,
like creating an OR gate, but also where to put it.
Using gestures is generally much faster
than going back and forth to the palette repeatedly.
The gestures themselves are defined
using the Agate tool by giving about 10 examples
of each gesture.
The programmer then writes just a few lines
of code in the application to associate the gestures
with the appropriate actions.
When the user selects an operation,
Amulet allocates a command object
and executes its do method rather
than using callback procedures like in other toolkits.
The advantage is that command objects also
have slots and methods to support
undo, making the commands active or grayed out,
and providing help for the command.
Amulet provides three kinds of built-in undo models.
The first is a simple undo model, like on the Macintosh.
The second is an unlimited undo that allows all operations back
to the start to be undone.
The third is a novel model that also
allows any command in the history
to be selected and repeated or undone.
Selectively undoing a command causes the inverse
of the command to be added to the top of the command history.
Commands can also be selectively repeated on new objects.
Command objects for standard operations,
like selecting, moving, and deleting objects,
are supplied in the library, and can often
be used without change since there is a standard way
to query and modify the graphical objects.
Of course, Amulet also contains a full set
of widgets implemented entirely using
the lower levels of Amulet.
Amulet is in the public domain and is available free
of charge by anonymous FTP or through the world wide web.
Many projects all over the world are
using Amulet for a wide variety of applications.
In the future, we will be concentrating
on providing a set of interactive tools
so that most parts of an interface
can be created by direct manipulation,
as well as adding support for technologies,
like speech and 3D.
The goal is to provide significant productivity gains
in user interface development while still
providing innovative capabilities to end users.