As mentioned previously, the ComplexItem ``class'' and its subclasses, which define new item types, are not really implemented using classes. Because the natural implementation of complex visual elements in Tk utilizes item tags to avoid recursive tree-walks, we flattened the object structure as well. (In a language that supports fine-grained objects, such as Java, we would use separate objects and recursive tree-walk algorithms.)
Each ``class'' is thus a collection of procedures that accept a slate, the contained canvas, and an item ID as the first three arguments. Methods of the slate call procedures in the appropriate namespace when necessary - for example, a command such as
$slate create Frame 40 40 80 80
will call the construct procedure in the Frame namespace. To simulate inheritance, each namespace contains an associative array mapping method names to the appropriate procedure, which is over-written in each ``sub-class.''
We used some other tricks as well. For example, to access option variables and instance variables, we index a shared array by a combination of a name and the ID of the item. This allows the Slate uniform access to the internals of complex items, regardless of class. For example, a procedure in Frame that accesses its color option would use:
set foo $option(color$id)
We use tags to simulate hierarchical complex items. Although this seems conceptually simple, actually doing it in the presence of event bindings is a little tricky. Each complex item is assigned a unique ID when it is created - we use an integer preceded by an underscore (the Tk canvas uses plain integers). Every simple item within a complex item is tagged with that ID:
Tagging rule 1: Every simple item within a complex item is tagged with that item's ID.
The corollary is that every simple item is tagged with the IDs of all items above it in the hierarchy - this makes moving a subtree easy. Now, user interaction often requires finding the root item containing a simple item. To make this efficient, each simple item within a hierarchy is given a special tag:
Tagging rule 2: Every simple item contained in a complex item is tagged with the ID of its root prefixed by ``!''.
The corollary of this rule is that any simple item that does not have a tag starting with ``!'' is not in a hierarchy. With these rules, it is easy (conceptually - the implementation is a little tricky) to find, move, and manipulate complex and simple items.
Now, some additional rules are needed to effectively deal with event bindings (and, by extension, interactors). Since a simple item can have only one root, it can have at most one tag beginning with ``!''. Therefore, when binding an event to a complex item, we follow this rule:
Binding rule 1: To bind an event to a complex item, bind to the tag constructed by prefixing its ID with ``!''.
The net effect is exactly as it would be if events were propagated up the hierarchy until a marked node were found. Finally, events can also be bound to tags, which is the same as binding to a tag on the canvas:
Binding rule 2: To bind an event to a tag, bind the event to that tag on the canvas.
The implementation of most slate methods based on these rules is reasonably straight-forward. Each method tests for three types of argument - a simple item ID, a complex item ID, or a tag - and acts accordingly. For example, a typical method has the pattern:
if { [string match {[0-9]*} $tag] } { # Process a canvas item ... } elseif { [string match {_*} $tag] } { # Process a complex item ... } else { # It's a tag: find matching items set items [find withtag $tag] ... }