I love XUL trees. I even
from time to time.
But what I don’t like is trying to build a hierarchy of objects in them – even
though that’s probably the best use for them.
Imagine that you want to show this tree of objects, with properties of each
object horizontally, and the objects themselves laid out vertically, indented
and illustrated to show which objects have which parent objects. DOM Inspector
does this with DOM nodes all the time. My chrome registry viewer code does
something similar for files (file systems are tree-like), and when you want to
see the properties of an object, JS object inspection is usually through a tree.
Even Venkman uses trees to show you functions in a file or webpage.
Still, for every different object tree I’ve come across, there’s a
different view that has to be built. Usually it’s custom-built for that tree.
So you’ve got two options: build your own view, from scratch, every time… or
build a XUL tree DOM and let Gecko’s own tree utilities show it to you.
Believe it or not, I’ve tried both approaches… and finally decided to
roll my own baseline solution. (If someone else has done this before, please let
me know. It’s best to have this in a common place.) More details in the extended section.
Here’s the kind of image that comes to mind every time I’ve looked at the
Seriously, what in the name of
(it’s Easter weekend, I don’t care to push my luck) what in
the world is this? At least half the methods on it deal with row-specific or
cell-specific details. Couldn’t the people who work with this just given me a
nsITreeRow interface and a getRow() method on the tree view?!?
I mean, it feels so dirty. It feels…
Not object-oriented at
all. How do you map a row in this tree to an object? You don’t through this
interface – because there’s no row to grab.
It’s also an interface with a lot of methods on it to implement. Yeeeuck.
It’s not immediately obvious which ones you need, and when. I’ve never truly
understood this tree view stuff, even with XULPlanet’s tutorial lending a hand.
<xul:treeitem/> and friends
I do understand treeitems, treechildren, treerow, and the like, though.
It’s DOM! It’s something we already have! It’s easy to inspect, to debug! It’s
This was my preferred method of building trees for five years. Never mind
the various people who kept telling me that tree views were so much better,
ignoring me when I said, “No, make this a DOM set of trees, that doesn’t need a
lot to understand.” It’s simple. I like simple.
Until two or three months ago, when I discovered
For those of you who don’t know about it, this is what takes your pretty
DOM-based XUL tree and converts it into a nsITreeView object.
So here’s what happens: My app spends a lot of time crafting this oh-so-nice,
fifteen-levels-deep, memory-crushing, CPU-melting DOM fragment. My program then
appends it, forcing nsTreeContentView to go to work, ripping that fragment apart
and creating… a nsITreeView that I did everything to avoid dealing with, and
which you just waited far too long for. Not to mention the bootstrapping I have
to put on top of that DOM tree to bind each row to some object.
There are a few things
that are more efficient than this approach…
So finally I said, “enough of this.” If I’m going to build a tree for
objects, I’m going to do it right. I’m going to create a generalized tree view
component, reading from both the tree and from objects, to show the object
hierarchy. I’m going to define a very simple, and very flexible, API that my
component’s users can build in and make it all work.
The first concept is that tree columns define what the cells show. So
let’s just go ahead and define a
propertyname attribute to stick
<xul:treecol/>. element. We still need those,
anyway. For more complex properties, define a fallback by allowing the tree’s
author to set a function on the column element. I do this through the DOM 3
The second concept is that there’s a 1:1 mapping between rows and objects.
That is, for each row, there should be exactly one object, and vice versa. So,
at least internally, we need a
TreeRow class to store a reference
to the original objects.
The third concept is that the tree view needs to know how to get child
objects of a given object. To do that, the tree view requires you to pass in
a function which takes an object and returns an array of objects, which you say
are children of that object.
The fourth concept is to provide a way to add top-level objects to this
tree. Every DOM tree has a root node, every file system has a few items at the
top. A generic tree view class can’t know about them beforehand, so you have to
tell it about them.
Put these four together, and you have enough to build a generic algorithm,
a generic “class tree viewer”.
Sample chrome code (copied, altered from the XUL Tutorial on
Element and MatterState represent (in this case) two similar classes which
live in the same tree – so really, you’d only need one of them. One column has
propertyname attribute on it. The other one (further down in
init() function) has a
cellGetter function on it.
getObjectChildren() function tells the tree view how to go from
one object to its children. Everything else is just raw data, initializing the
tree and the tree view, and adding top-level objects to the tree. It’s really
just that simple, and pretty light-weight.
Now, I will admit this
ClassTreeView is not complete.
For instance, it’s read-only, and doesn’t yet support progress meters, check
boxes, etc. I don’t need those at this point, and to me it’s just more
complexity (see nsITreeView for details). As a starting point for showing a
true object hierarchy, though, it’s good enough. If you know tree views and
want to finish this, patches accepted!
API-wise, it’s a “best guess”. I designed this to work based on my needs
and understanding. Maybe there are better approaches – but for this particular
problem, I think it’s a good start.
The one true weakness to
ClassTreeView is that content web
pages cannot use it. The reason for that is buried at the end of the
nsITreeView.idl file: the
nsINativeTreeView interface. So perhaps
interfaces, and with a lot of reviews to make sure I get it right – and I’ll
make it available to the Web. (Then again, you don’t see a whole lot of XUL
on the Web… maybe for reasons like this.)