Tree Widget Changes

This describes changes in XUL Trees API for Gecko 1.8.

There are no changes to XUL tree tags, however the id attribute is no longer required on treecol elements just to get them to work. That means that the ids can be left out, although it's probably a good idea to use them anyway.

Instead of identifying columns by id, a new column object is used. This object implements the nsITreeColumn interface and holds information about a single column in the tree. A tree will have one of these objects for each column (each treecol element) in the tree. The columns are grouped into a list which implements the nsITreeColumns interface. Both the nsITreeColumn and nsITreeColumns interfaces can be found at layout/xul/base/src/tree/public/nsITreeColumns.idl.

The column objects are created automatically, so you don't have to write any extra code. You can get the columns object which implements the nsITreeColumns interface for a tree using the tree's columns property. From there you can get specific columns, the current sort column, and position and size info about the columns. For the most part these objects are readonly; you can modify the columns by just adjusting the treecol attributes directly.

The tree and view methods no longer take ids as arguments when columns are used. Instead, they use nsITreeColumns. For example, nsITreeView.getCellValue() takes a row index and a nsITreeColumn as arguments, whereas before it took a row index and a column id.

To get a column in JavaScript:

tree.columns.getColumnFor(treeColElement);
tree.columns.getNamedColumn(treeColID);
tree.columns.getColumnAt(index);

You can also just use array syntax to get a column:

tree.columns["lastName"];
tree.columns[5];

Once you have a column, you can get various properties of it:

  • column.index - the index of the column in displayed order
  • column.id - the id attribute of the column
  • column.element - the treecol element
  • column.x - the X position in the tree of the left edge of the column
  • column.width - the width of the column

In C++ code, you can also get the atom attribute of nsITreeColumn which returns an nsIAtom for the column, making it fast to do comparisons.

nsCOMPtr<nsIAtom> atom;
aCol->GetAtom(getter_AddRefs(atom));
if (atom = kMyCol) ...

One feature that has been added is restoreNaturalOrder which may be used to restore the original order of the columns before the user moved them around.

tree.columns.restoreNaturalOrder()

There is also a command on the end of the tree's column picker which the user may use to restore the original column order. This will be hidden if the column redordering is disabled using enableColumnDrag="false".

Some specific changes

You should now get the tree selection object from the view, not the box object, meaning use tree.view.selection instead of tree.treeBoxObject.selection.

Use tree.columns[1].id instead of tree.treeBoxObject.getColumnID(1) to get the id of a column, in this case column 1.

Use tree.columns.getKeyColumn().index instead of tree.treeBoxObject.getKeyColumnIndex().

The nsITreeBoxObject.getPageCount() method has been renamed to make it clearer what it does. It returns the number of rows that can be displayed in the tree. This should correspond to the rows attribute on the tree if it was specified.

tree.treeBoxObject.getPageCount() is now tree.treeBoxObject.getPageLength().

The invalidatePrimaryCell(row) method has been removed, instead use nsITreeBoxObject.invalidateCell() like this invalidateCell(row, tree.columns.getPrimaryColumn()). This may be used to redraw a cell after it or its data has been changed.

The nsITreeView.cycleHeader() method has been changed, cycleHeader(colID, element) is now just cycleHeader(column), since the code can get the element from the column object.

The constants below have been changed, and their integer values are different:

nsITreeView.inDropBefore          -> nsITreeView.DROP_BEFORE            (-1)
nsITreeView.inDropOn              -> nsITreeView.DROP_ON                (0)
nsITreeView.inDropAfter           -> nsITreeView.DROP_AFTER             (1)
nsITreeView.progressNormal        -> nsITreeView.PROGRESS_NORMAL        (1)
nsITreeView.progressUndetermined  -> nsITreeView.PROGRESS_UNDETERMINED  (2)
nsITreeView.progressNode          -> nsITreeView.PROGRESS_NONE          (3)

As well, the drag and drop methods canDropOn and canDropBeforeAfter have been replaced with a single method canDrop(idx,orientation) which handles both. It should return true if a drop is allowed on a row.

Checkbox columns

Tree columns now implement the checkbox type. Previously the value existed but was not implemented. Now it is. You can create a checkbox column by setting the type attribute of a column to checkbox.

<treecol type="checkbox">

You can then set or clear the checkbox for a particular cell in that column by setting the value attribute to true, or leaving out the attribute. Note that it's the value attribute you use, not the label attribute.

<treecell/>
<treecell value="true"/>

You need to specify the checkbox image with CSS for the checkbox to display. (Do NOT set id of the column to be 'checked' it will cause problems with the CSS)

treechildren::-moz-tree-checkbox(checked)
{
    /* css for checked cells */
    list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}

In addition, checkmark columns support editing:

<tree editable="true">
  <treecols>
    <treecol type="checkbox" editable="true">
      ...
    </treecol>
  </treecols>
</tree>

If the column is editable, the user can click the cell to change the state of the checkbox. When the user clicks the cell, the view's setCellValue method will be called with either the value true or false.

Note that the tree must also be marked as editable using the editable attribute in order for this to work. This is shown in the example above. Sometimes, you might have a particular row or cell which you do not want to be editable. In this case, disable editing for that cell by setting editable to false for that cell, as in the following:

<treecell value="true" editable="false"/>

Or, for custom views, return false from the isEditable method.

Currently, only checkbox columns support editing, although the content-based tree handles the nsITreeView.setCellValue() and nsITreeView.setCellText() functions to change the tree content with a script for other types of cells. For instance:

var col = tree.columns.getPrimaryColumn();
treecell.setCellText(5, col, "Banana");

This will change the label of the cell in row 5 and the primary column to Banana. However, this paves the way in the future for more general tree editing features.

Style improvements

You can now specify the cursor to use for a cell using the CSS cursor property.

treechildren::-moz-tree-cell-text {
    cursor: pointer;
}

This allows you to create separate cursors for cells.

The :-moz-tree-separator pseudo has been improved to make it a proper box type and now has additional styling capabilities. Example:

treechildren::-moz-tree-separator {
    margin-top:    1px;
    border-top:    1px solid ThreeDShadow;
    border-left:   1px solid ThreeDShadow;
    border-right:  1px solid ThreeDHighlight;
    border-bottom: 1px solid ThreeDHighlight;
    height:        2px;
}

Original Document Information

  • Author: Neil Deakin
  • Source: here