ScriptX Tracking Service
Introduction
This is the documentation for the ScriptX Tracking Service. The Tracking
Service is a set of classes for tracking mouse clicks and movements. It
includes the TrackService class, a controller
that manages interests and handles low level input events, and the Tracker
class, a mixin that participates in the high level tracking protocol with
the tracking service. It also provides the CachedPresenter
class, that draws an offscreen group of presenters into a bitmap cache,
which dramatically improves compositor performance.
This tracking service offers several advantages over the lower level DragController
built into ScriptX. It's designed to be very easy to use, so many of the
common types of mouse tracking can be coded with a minimum effort. It has
some fancy features, but it provides sensible defaults and a clean interface
so it's not complicated, and the features don't get in your way if you don't
need them.
Some of its important features include delegation of tracking to objects
other than the one originally clicked on, and transformation of event coordinates
through an arbitrary TwoDMatrix. These features made it easy to implement
a cached offscreen group, that draws quickly from a bitmap cache, but passes
tracking through to offscreen presenters. (That was not easy to do with
the DragController, since it provides no way to track input events on offscreen
presenters.)
The Tracking Service is used by the DreamScape
demo.
Index of classes: Tracker, TrackerMulti,
TrackerMultiPresenter, TrackerGroupPresenter,
TrackerTwoDSpace, CachedPresenter,
TrackerCachedPresenter, TrackService.
- class Tracker (RootObject)
- This mixin class gives an object the ability to track the mouse, by
participating in the tracking service protocol. You can mix Tracker in with
presentation and model objects to implement all kinds of direct manipulation
interfaces.
- subclasses
TrackerMulti, TrackerMultiPresenter,
TrackerGroupPresenter, TrackerTwoDSpace,
TrackerCachedPresenter.
- instance methods
- method trackStart self
{class Tracker}
The trackStart method is called by the TrackService
in response to a mouse down event. You decide if you want to accept or reject
tracking. To reject tracking, return false. To accept tracking, return true.
If you accept tracking, the track service will call your trackDown,
trackMove, trackPause,
and trackUp methods, after you return true
from trackStart.
If you accept tracking, you can configure the tracking service in several
useful ways, by changing the values of the keys in trackStart's state
argument (a keyed list), before you return true from trackStart. The class
variable TrackService.defaultState
contains a keyed list of the default values, that is copied and passed to
trackStart. Two of the most common useful state keys you might want
to configure are state[@trackTarget]
and state[@trackMatrix].
The @trackTarget key
specifies the Tracker object that will actually be
sent the trackDown, trackMove,
trackPause, and trackUp
messages. By default, it's the object to which trackStart is being sent
(self). But one Tracker object can accept tracking and delegate it
to another, by changing state[@trackTarget].
This is how the TrackerMulti and TrackerCachedPresenter
classes manage to pass tracking on to the Trackers they contain.
The @trackMatrix key
specifies the transformation that is applied to the coordinates passed in
the event argument of the trackDown, trackMove,
and trackUp messages. By default, it's set
up to transform coordinates relative to the origin (0,0) of the object to
which trackStart is being sent (self). This lets you to transform
the events into whatever coordinate system is most convenient for tracking,
so that trackDown, trackMove,
and trackUp will all be called in that coordinate
system.
See the TrackService.defaultState
documentation for a list of the other state keys you can modify.
- method trackDown self
{class Tracker}
The trackDown method is sent from the TrackService
to the trackTarget immediately after
trackStart accepts tracking, in response
to a mouse down event. By default, trackDown calls trackMove,
so you don't have to override trackDown in many cases, if the trackMove
behavior does what you want. If you do override trackDown, you don't have
to call nextMethod unless you mean for it to call trackMove, because it
does!
- method trackMove self
{class Tracker}
The trackMove method is called by the TrackService
every time the mouse moves during tracking. By default, trackUp
and trackDown just call trackMove, unless
you override them, so the common case of doing the same thing on mouse down,
move, and up is easily implemented just by overriding trackMove.
- method trackUp self
{class Tracker}
The trackUp method is called by the TrackService
in response to a mouse up event, that terminates tracking. By default, trackUp
also calls trackMove, so you don't have
to override trackUp in many cases, if the trackMove behavior does what you
want. If you do override trackUp, you don't have to call nextMethod unless
you mean for it to call trackMove, because it does too!
- method trackPause self
{class Tracker}
The trackPause method is called by the TrackService
after the mouse does not move for a certain time. The pauseTime
specifies the time in seconds to wait for the mouse to move, before the
TrackService sends a trackPause message its trackTarget.
By default, the pauseTime is undefined, so trackStart
must set state[@pauseTime]
to a number of seconds in order to get trackPause notification. If you need
to know the coordinates of the last event, you can use "service.lastX"
and "service.lastY".
- method trackDrop self
{class Tracker}
The trackDrop method is called when one Tracker drops
in on another by calling sendTrackDrop.
The x and y arguments are the location to drop relative to
the internal coordinate system of self. The data argument
is the thing being dropped, and doit is a boolean that tells if you
should just preview the drop, or actually perform it. Return true to accept
the drop, or false to reject it.
- method sendTrackDrop
self {class Tracker}
- service
- target
- x
- y
- data
- doit
The sendTrackDrop method sends a trackDrop
message to another object. The target can be a Group, a TwoDSpace,
a MultiPresenter, a Tracker, or any object that can
respond to the trackDrop message. The x
and y coordinates are the location to drop the data, relative
to the target. The data may be any object, and it's up to
the drop target to decide to accept or reject it, and how it interpret it.
The doit flag tells if this is a drop preview, or the real thing.
Only do the drop if doit is true. If doit is false, you can
provide some sort of preview of what will happen if the data is dropped
there. Note: This drag and drop interface is in development, and subject
to change!
- class TrackerMulti (Tracker)
- The TrackerMulti mixin defines the recursive tracking protocol, so
groups can transform and delegate tracking services to their children. This
is an abstract class that is mixed in with concrete presentation subclasses.
- subclasses
TrackerMultiPresenter, TrackerGroupPresenter,
TrackerTwoDSpace.
- instance methods
- method trackStart
self {class TrackerMulti}
This method overrides the trackStart method
of class Tracker, to delegate tracking and transform
the coordinates to one of the sub-objects in this group. If the coordinates
of the event are within the region of one of the sub-objects, it is given
a chance to accept tracking, by translating state[@trackMatrix]
to the location of the sub-object, and passing the trackStart message on
to it. If none of the sub-objects at that point accept tracking, this TrackerMulti
object's trackStartMulti method
is called, to give the group itself a chance to track.
- method trackStartMulti
self {class TrackerMulti}
This method is called by TrackerMulti's trackStart
method, to give this group a chance to accept tracking, when the mouse is
clicked inside of its boundary but not over a sub-object accepting tracking.
If you accept tracking by returning true, then the trackDown,
trackMove, trackPause,
and trackUp messages will be sent to state[@trackTarget],
which defaults to self, as with any Tracker.
This could be used to drag a rectangle around selectable sub-objects when
you press down in the background, for example.
- method trackDrop self
{class TrackerMulti}
This method overrides the trackDrop method
of class Tracker, to delegate drops and transform
the coordinates to one of the sub-objects in this group. If the coordinates
of the event are within the region of one of the sub-objects, it is given
a chance to accept the drop, by translating the x y coordinates
to the location of the sub-object, and passing the trackDrop message on
to it. If none of the sub-objects at that point accept the drop, this TrackerMulti
object's trackDropMulti method
is called, to give the group itself a chance to accept the drop.
- method trackDropMulti
self {class TrackerMulti}
This method is called by TrackerMulti's trackDrop
method, to give this group a chance to accept a drop, when an object is
dropped inside of its boundary but not over a sub-object accepting the drop.
- class TrackerMultiPresenter
(TrackerMulti, Tracker,
TwoDMultiPresenter)
- This is the concrete TwoDMultiPresenter subclass of the abstract TrackerMulti
class.
- class TrackerGroupPresenter
(TrackerMulti, Tracker,
GroupPresenter)
- This is the concrete GroupPresenter subclass of the abstract TrackerMulti
class.
- class TrackerTwoDSpace (TrackerMulti,
Tracker, TwoDSpace)
- This is the concrete TwoDSpace subclass of the abstract TrackerMulti
class.
- class CachedPresenter (TwoDShape)
- This class is a subclass of TwoDShape that caches an offscreen presenter,
usually a group, by drawing into its bitmap boundary. Putting complex groups
of presenters that don't change often into a cached offscreen group dramatically
improves compositor performance. CachedPresenter is independent of the TrackService
and Tracker classes, but its subclass TrackerCachedPresenter
ties them all together.
- subclasses
TrackerCachedPresenter.
- instance variables
- offscreen
- This is the offscreen presenter that is drawn into the cache,
usually a subclass of Group or TwoDSpace, mixing in Tracker
if you want the offscreen objects to receive mouse input.
- cache
- This is the bitmap cache into which the offscreen
presenter is drawn. Its invisibleColor is set to pure blue, presently, so
don't use that color! This will be configurable in the future.
- cacheValid
- Boolean that tells if the cache
is valid, or else the offscreen
presenter needs to be redrawn.
- cacheChanged
- Boolean that tells if the offscreen
presenter has been redrawn and the cache
needs to be composited to the screen by addChangedRegion.
- updateRegion
- This is a Region representing the area of the cache
that needs to be composited to the screen by addChangedRegionTracker.trackMove.
- instance methods
- method init self
{class CachedPresenter}
- #rest args #key
- offscreen:(undefined)
- bbox: (new Rect x2: 640 y2: 480)
When you make a new CachedPresenter, you must specify an offscreen
presenter to draw into the cache, usually
some kind of group. The bbox argument specifies the size of the cache.
This is temporarily built into CachedPresenter, and should be factored out
as an option.
- method invalidate
self {class CachedPresenter}
- This invalidates the entire cache. The whole offscreen
presenter will be redrawn into the cache
next time addChangedRegionTracker.trackMove
calls validate. Carefully use this
and invalidateRegion to
implement incremental updating.
- method invalidateRegion
self {class CachedPresenter}
This invalidates a region of the cache, by adding the region reg
to the updateRegion. The offscreen
presenter will be redrawn into the cache
next time addChangedRegionTracker.trackMove
calls validate, and the compositor
will blit the updated regions onto the screen. Carefully use this and invalidate
to implement incremental updating.
- method validate
self {class CachedPresenter}
- This draws the offscreen
presenter into the cache if necessary,
by calling drawCache. It is called
by addChangedRegionTracker.trackMove.
- method addChangedRegion
self {class CachedPresenter}
This method calls validate, then
adds the updateRegion to the
changed region reg that the compositor will blit from the cache
onto the screen.
- method drawCache
self {class CachedPresenter}
- This method draws the offscreen
presenter into the cache, and adds
any of its changed region to our updateRegion,
for addChangedRegionTracker.trackMove
to tell the compositor about, later on. Several calls to drawCache can happen
before the compositor calls addChangedRegion, and the updateRegion will
correctly accumulate all changes. Called by validate.
- class TrackerCachedPresenter
(Tracker, CachedPresenter)
- This class ties CachedPresenter together
with the TrackService and Tracker
classes, so an instance draws quickly from its bitmap cache,
but pass tracking through to its offscreen,
presenter.
- class TrackService (IndirectCollection)
- This class implements the guts of the tracking service. There is only
one instance of this class, in the global variable theTrackService.
- class variables
- defaultState
- This class variable of the TrackService
is a keyed linked list, containing the default values for the tracking parameters,
that is copied and passed as the state
argument to the trackStart method of class
Tracker. The trackStart method can modify these keys
to effect the tracking behavior.
- @trackTarget:
dynamic Tracker
- The trackTarget is the
Tracker object to which the TrackService
will send trackDown, trackMove,
trackPause and trackUp
messages. By default, the trackTarget is dynamically set to the last object
that returned true from trackStart.
- @trackMatrix:
dynamic TwoDMatrix
- The trackMatrix is a
TwoDMatrix that defines the linear transformation to apply to the screen
coordinates of the mouse down, move, and up events received during tracking.
The TrackService will send trackDown,
trackMove, trackUp
messages with an event as the ev argument,
whose ev.localCoords have been transformed through this matrix. The default
trackMatrix translates to the internal coordinate system of the track target.
- @trackConstraint:
@None
- The trackConstraint name can be @None for unconstrained motion,
@Vertical for up and down motion only, or @Horizontal for back and forth
motion only. The default is @None for no constraints. When tracking motion
is constrained, one of the coordinates of the events passed to trackDown,
trackMove, trackUp
will remain constant, no matter where the mouse moves. This constraint is
applied after the trackMatrix
transformation.
- @xResolution:
0
- @yResolution:
0
- Setting the xResolution or yResolution to non-zero will quantize
the x or y coordinates to be multiples of the resolution. The default is
0 for no quantization. These quantizations are applied after the trackMatrix
transformation.
- @minimumX:
negInf
- @minimumY:
negInf
- @maximumX:
posInf
- @maximumY:
posInf
- Setting the minimum or maximum x or y constrains the coordinates
to be within the specified range. The defaults are negInf and posInf for
no minimum or maximum. These constraints are applied after the trackMatrix
transformation.
- @pauseTime:
undefined
- The pauseTime specifies the time in seconds to wait for the
mouse to move, before the TrackService sends
a trackPause message to its trackTarget.
By default, the pauseTime is undefined, so the trackStart
method must set it to a number of seconds in order to get trackPause notification.
- instance variables
- trackTarget
- The current tracking target, a subclass of Tracker.
Initialized from state[@trackTarget]
after trackStart returns true.
- trackMatrix
- The current tracking coordinate transformation. Initialized from
state[@trackMatrix]
after trackStart returns true.
- pauseTime
- The current time to pause in seconds before sending trackPause.
Initialized from state[@pauseTime]
after trackStart returns true.
- firstX
- firstY
- The transformed x and y coordinates of the initial mouse down
event.
- lastX
- lastY
- The transformed x and y coordinates of the most recent mouse event.
- instance methods
- global instances
- global theTrackService
- This is the single instance of the TrackService
class in the system. To make a Tracker object respond
to mouse input, either append it to theTrackService,
or add it to a TrackerMulti group that itself
is responding to mouse input (by being a member of theTrackService
itself, or nested inside another TrackerMulti
group which is).
- Here is an important optimization: you don't have to add all your
Tracker objects to theTrackService directly, you can just add your outermost
TrackerMulti group, and it will delegate the trackStart
and trackDrop messages on down to its Tracker
and TrackerMulti sub-objects, recursively. Because
of the way TrackerMulti recursively sets the state[@trackTarget]
to point to its sub-object, the TrackService
sends the trackDown, trackMove,
trackPause, and trackUp
methods directly to the innermost Tracker!
- In fact, a scrolling presentation object's trackStart
can transform state[@trackMatrix]
so it maps screen coordinates to arbitrary model coordinates, and set state[@trackTarget]
to delegate the tracking protocol to a model object that mixes in Tracker.
The model has no presentation itself, instead it has a list of views, to
which it sends update messages. So you can have a model with mutiple synchronized
views, which all delegate tracking to the same high level handlers in the
model, which modify the model and update all the views at once!