This is another fairly dense technical piece. If you were
hoping for
diversion or scandal, sorry. It is a companion to my caching
in trees piece from a few days ago.

For interactive displays, the Fitz display tree will follow a
Model/View pattern. The model is the tree, and the view is a
rendering
module that renders the tree and displays it on the screen.

What happens when you change the tree? If you wanted the
simplest
possible implementation and didn't care about performance, you'd
re-render the tree from scratch, and display that. But I do
care about
performance, and I am willing to sacrifice some of that
simplicity for
"minimal updating". At root, this is the realization that,
if you have
the rendering for the old tree in hand, then to get the
rendering for
the new tree, you may only need to do a small incremental
computation.

Let's look at minimal update in more detail. A deep optimization
running through much of Fitz is culling based on bounding
boxes. Changes to an object are guaranteed not to affect
rendered
pixels outside the bounding box (note that effects like drop
shadows
don't fit neatly into this category; thus, they're not directly
supported in the Fitz imaging model). So, if you change an
object, a
reasonable approximation to the update area is the union of
the old
and new bounding boxes. If you delete or insert an object,
just the
one bounding box.

However, for some objects you may be able to do better. For
example,
when dragging one corner of a semitransparent rectangle, the
minimal
repaint area may be L-shaped. In this case, repainting the
entire
rectangle would be wasteful.

One more performance issue: if the tree is very large, then
the cost
of having one "event listener" per node could be too much.
Keep in
mind that the only justification for all this is
performance. If you
add complexity and make other aspects of performance (like
memory
usage) worse, then it's probably not a win.

So the broad outlines of a change notification system begin
to suggest
themselves. If the tree is small, then having an event
listener on
each node is reasonable. Changes to leaf nodes are
distinguished from
changes to tree structure. The former are dispatched first
to a change
handler for the specific leaf type. The fallback is the
bounding box
approach. Changes to tree structure also invoke the bounding box
method.

However, as the tree grows larger, you only attach event
listeners to
some nodes. It's a type of cache. For nodes that have no event
listeners attached to descendants, you expect a notification
any time
a descendant changes. Otherwise, you only care about changes
to the
node itself, because you know other listeners will handle
the rest.

So now we have the concept of event propagation. For at
least some
kinds of listeners, you want to bubble the event up the tree
until you
hit a listener. In the extreme case, you can have just one
listener at
the root of the tree.

Now I face a difficult design decision. If I want to have
multiple
Views of the same Model, event propagation gets trickier. In
particular, the propagation patterns for different Views can't
interfere with each other. You can't simply stop bubbling an
event
upwards when you hit a listener, because maybe another View
has their
listener further up. Bubbling all the way to the top isn't very
satisfying either, because that will tickle the listener at
the root
even if a deeper listener has already handled the change.

There are a bunch of other hacks that don't work so well
either. You
could, for example, check to see whether a deeper handler
has already
been called, and ignore the notification if so. But you get into
trouble if you want to aggregate several changes together
into one
notification. Keep in mind that if you change most of the
nodes in the
tree, then re-rendering from scratch is almost certainly
better than
doing an incremental computation for each node changed.

If you did have a good solution for the one-View case, then
you could
extend that to the multi-View case by having an "event
group" for each
listener. When a listener catches an event, it sets a bit so
that
other listeners with the same event group farther up the
tree don't
get called. But the complexity is unsatisfying.

That said, let me present one reasonably simple approach
supporting
multiple Views. Here, the tree implementation does just
bubble events
all the way to the root. It's up to the client (the View) to
apply the
event propagation logic. At an internal node, if you have
listeners
for all children, and if the node itself is unchanged
(meaning no
insertions or deletions at that node), then you can ignore the
notification.

However, there is one unappealing aspect of this approach.
If you want
to add more listeners (say, after they've been thrown out of the
cache), then it only makes sense to add listeners to all
children of a node. If you only add it to some, you can't
reliably
ignore the notification, so you might as well not add it to
any. I
expect that the fanout may be quite large, so this could be
a problem.

So far, I haven't been able to come up with what feels like
the Right
Answer, only different tradeoffs. Right now, restricting the
thing to
one View seems like the best tradeoff. For Fitz, I think
this will
very much be the common case. But for other, more general tree
applications (particularly those involving networking), it's
probably
not good enough. It would be nice to have a general solution
that was
simple enough to be practical for Fitz, but I guess I'm just too
incompetent to come up with one. Ah well, such is life.