ツリーボックスオブジェクト

このセクションでは、ツリーの描画方法を制御するために利用できる、ツリーボックスオブジェクトについて説明します。

ツリーボックスオブジェクトの概要

ボックスオブジェクトについては、少し前のセクションで既に説明しましたが、 ツリーボックスオブジェクトは、ツリーでの利用に特化した専用のボックスオブジェクトで、 TreeBoxObject インターフェイスを実装しています。

ツリーの再描画を要求する

ツリーボックスオブジェクトの rowCountChanged() 関数は、前のセクションで既に見ています。 この関数は、ツリーに対して 1 つ以上の行が、追加または削除されたことを通知するもので、 ツリーでは影響を受ける領域についての再描画を行うことになります。 しかし、単に 1 つの行が何らかの方法で変更された場合、 例えばセルのラベルが変更されたときなどであれば、rowCountChanged() 関数を呼び出す必要はありません。 こういった場合には、かわりに別のいくつかの描画関数を利用します。 最も簡単なのは、invalidateRow() を呼び出すことで、これはツリーの中の指定した行の再描画を要求します。 このとき、ツリーはビューから更新されたデータを取得して画面上のツリーの内容を更新することになります。

また、それ以外の再描画関数としては以下があります。

  • invalidateCell() は、1 つのセルのみの再描画を要求します。
  • invalidateColumn() は、1 つの列の再描画を要求します。
  • invalidateRange() は、指定範囲の行についての再描画を要求します。
  • invalidate() は、ツリー全体に対する再描画を要求します。

なお、Mozilla は再描画処理をバックグラウンドで平行して行うことはしないため、実際の再描画は呼び出したスクリプトが終了するまでは行われないことに注意してください。

ツリーをスクロールさせる

ツリーボックスオブジェクトから、ツリーをスクロールさせることも可能です。 このためには、4 つのメソッドが利用でき、これらは リストボックスにあるものと類似しています。 まず、特定の行へスクロールさせるために scrollToRow() 関数が利用できます。 以下に、簡単な例を示します。

var el = env.locale; 例 1 : ソース 表示

<script>
function doScroll(){
  var value = document.getElementById("tbox").value;
  var tree = document.getElementById("thetree");

  var boxobject = tree.boxObject;
  boxobject.QueryInterface(Components.interfaces.nsITreeBoxObject);
  boxobject.scrollToRow(value);
}
</script>

<tree id="thetree" rows="4">
  <treecols>
    <treecol id="row" label="Row" primary="true" flex="1"/>
  </treecols>
  <treechildren>
    <treeitem label="Row 0"/>
    <treeitem label="Row 1"/>
    <treeitem label="Row 2"/>
    <treeitem label="Row 3"/>
    <treeitem label="Row 4"/>
    <treeitem label="Row 5"/>
    <treeitem label="Row 6"/>
    <treeitem label="Row 7"/>
    <treeitem label="Row 8"/>
    <treeitem label="Row 9"/>
  </treechildren>
</tree>

<hbox align="center">
  <label value="Scroll to row:"/>
  <textbox id="tbox"/>
  <button label="Scroll" oncommand="doScroll();"/>
</hbox>

この例では、スクロール動作を確認しやすいように、 tree 要素の rows 属性を設定して、同時に 4 行しか表示されないようにしてあるので注意してください。 また、最初の行へスクロールするためには 0 を与える必要があることにも注意してください。

この、doScroll() 関数では、ツリーボックスオブジェクトの取得を行い、テキスト入力欄への入力値を引数に渡して、scrollToRow() 関数を呼び出しています。 また、この例から確認できるように、ツリーボックスオブジェクトも他のボックスオブジェクトと同様に boxObject プロパティから取得できます。 ただし、取得後に QueryInterface() を呼び出して、汎用のボックスオブジェクトを、専用のツリーボックスオブジェクトにキャストする必要があります。 なお、ツリーボックスオブジェクトにキャストした後でも、汎用のボックスオブジェクトの関数も利用可能です。

それ以外スクロールのメソッドとしては、 scrollByLines()scrollByPages()ensureRowIsVisible() 関数があります。

scrollByLines() 関数は、指定した行数に応じてスクロールを行います。 このとき、正数を指定した場合は下方向に、負数の場合は上方向になります。 次の scrollByPages() 関数は、指定したページ数に応じたスクロールを行います。 この関数は、ツリーにフォーカスがあるときに、利用者が page up または page down キーの操作を行った場合にも自動的に呼び出されます。 なお、この場合の 1 ページはツリーに表示可能な行数と同じになります。 つまり、ツリーに同時に 10 行表示可能なときは、1ページは 10 行分の大きさになるということです。 ツリーが伸縮可能な場合に、利用者がリサイズを行うと、 ページの大きさもそれに応じて変わることになりますが、 このメソッドを利用すれば、そういった場合にもページサイズを自分で計算をする必要がないため便利です。 もっとも、ツリーボックスオブジェクトには、1 ページの行数を返す getPageLength() 関数もあるため、この計算自体は特段難しくはありません。 上の例では、getPageLength() は 4 を返すことになります。

Firefox 1.0 と Mozilla 1.7、またはそれ以前のバージョンでは、getPageLength() 関数は getPageCount() という名前で呼ばれていました。名前が getPageLength() に変更されたのは、以前の名前では、その関数が 1 ページの行数を返すもので、全体のページ数を返すものではないということがわかりにくかったためです。なお、ページ数が必要な場合は、行の総数を 1 ページの行数で割れば算出できます。

最後の ensureRowIsVisible() 関数は、scrollToRow() と同様にスクロールを行いますが、 対象の行が、既に表示中の場合にはスクロールを行わない点が異なっています。

セルの座標

ツリーボックスオブジェクトが提供している関数の中で最も興味を引くのは、 指定座標がツリーのどの部分に該当するかを調べたり、 逆にツリーの特定の部分が位置している座標を調べたりするための関数群でしょう。

  • getCellAt() 関数は、特定の位置にあるセルを取得します。位置はピクセル単位で指定します。
  • getRowAt() 関数は、特定の位置にある行を取得します。getRowAt() 関数は、座標を表す x と y の 2 つの引数をとります。
tree.boxObject.getRowAt( 50, 100 );

この例は、水平位置 50, 垂直位置 100 にある行のインデックスを返すことになります。 もっとも、行はツリーの左端から右端までを常に占めることになるため、 x 座標を指定することに実質意味はありません。

重要: 座標系は、ツリーが置かれている文書の左上隅が原点になります。ツリー要素の左上隅ではないので注意してください。

なお、文書の左上隅を原点としているため、これらの関数に対しては、 以下の例の getCellAt() 関数の呼び出しのように イベントから取得した座標をそのまま渡してもかまいません。

var el = env.locale; 例 2 : ソース 表示

<script>
function updateFields(event){
  var row = {}, column = {}, part = {};
  var tree = document.getElementById("thetree");

  var boxobject = tree.boxObject;
  boxobject.QueryInterface(Components.interfaces.nsITreeBoxObject);
  boxobject.getCellAt(event.clientX, event.clientY, row, column, part);

  if (column.value && typeof column.value != "string")
      column.value = column.value.id;

  document.getElementById("row").value = row.value;
  document.getElementById("column").value = column.value;
  document.getElementById("part").value = part.value;
}
</script>

<tree id="thetree" flex="1" onmousemove="updateFields(event);">
  <treecols>
    <treecol id="utensil" label="Utensil" primary="true" flex="1"/>
    <treecol id="count" label="Count" flex="1"/>
  </treecols>
  <treechildren>
    <treeitem>
      <treerow>
        <treecell label="Fork"/>
        <treecell label="5"/>
      </treerow>
    </treeitem>
    <treeitem>
      <treerow>
        <treecell label="Knife"/>
        <treecell label="2"/>
      </treerow>
    </treeitem>
    <treeitem>
      <treerow>
        <treecell label="Spoon"/>
        <treecell label="8"/>
      </treerow>
    </treeitem>
  </treechildren>
</tree>

<label value="Row:"/>
<label id="row"/>
<label value="Column:"/>
<label id="column"/>
<label value="Child Type:"/>
<label id="part"/>

getCellAt() 関数は、座標の指定用に 2 つと、結果の出力用パラメータとして 3 つの、合計 5 つの引数をとります。 この関数では複数の値を返す必要があるため、出力用パラメータを使用して関数から戻り値以外の値を受け取ることになります。 なお、出力用パラメータを使用するインターフェイスはこの他にもいくつかあり、 XULPlanet の オブジェクトリファレンス で確認することが可能です。 (リファレンスでは、出力用パラメータは引数の前に「out」というキーワードを付けて区別されています)。 関数を呼び出すとき、こういった出力用パラメータには、空のオブジェクトを渡す必要があり、 呼び出された関数は、渡されたオブジェクトの value プロパティに必要な値を設定して返します。

getCellAt() 関数の 3 つの出力用パラメータには、「行 (row)」と「列 (column)」および「子の種類 (part)」が設定されて戻ります。 この例を実行した場合は、mousemove イベントのイベント座標を引数として渡すため、 row 引数の値には、マウスが重なっている行のインデックスが 設定されることになります。 なお、指定の座標が行とは重なっていない場合には、値として -1 が設定されます。 また、column 引数には、座標が指す列の情報として、 Mozilla 1.8 (Firefox 1.5) 以降の場合は、対応する列オブジェクト (TreeColumn) が設定されます。 それより古いバージョンの場合は、ツリーの列の識別は、列に設定された id の文字列で行っていましたが、 新しいバージョンからは、個々の列に対応した列オブジェクトが存在しており、ここから列のデータを取得することが可能になっています。

以下に示すコードにより、上の例はすべてのバージョンで動作するようになっています。

if (column.value && typeof column.value != "string") column.value = column.value.id;

column が文字列のときは、 Mozilla 1.7 かそれ以前のバージョンで動作している場合なのでそのままスキップし、 それ以外は、それ以降のバージョンであるため、列オブジェクトから列 id を取得して column に設定することによって、旧バージョンと同等になるようにしています。 もしも、複数のバージョンで動作するコードを書く必要がある場合には、上記のチェックは必ず行わなければなりません。

getCellAt() の最後のパラメータは子の種類、つまり指定した座標はセルのどの部分にあたるかを示す文字列が設定されます。 前の例を実行してマウスをいろいろ動かすと、場所によってラベルが「text」か「cell」に変わることが確認できるはずです。 ここに設定された値が「text」の場合は、その領域はテキストの描画領域であることを、 「cell」の場合は、テキストの周辺部、 例えば通常は行の開閉に使うツイスティが描かれる左側の余白などであることを示しています。 ただし、そこにツイスティがある場合には、値として 「twisty」が設定されます。 これは利用者がツイスティをクリックしたか、行の他の部分をクリックしたかを判別したいときに使用すると便利です。 実際、これは利用者がツイスティをクリックしたときの内部コードでも使用されています。 また、値として「image」が設定される場合は、座標がセル内に置かれた画像にあたることを示しています。 なお、実際のアプリケーション開発では、たいていの場合は、座標が指す点がセルのどの部分にあたるかまでを気にする必要はなく、 単に、座標が指す点が、どの行のどの列に該当するかのみが分かれば十分である場合がほとんどです。

また、getCellAt() の逆の動作、 つまり指定したセルが位置する座標を取得したい場合は、getCoordsForCellItem() 関数を使用します。 この関数は、以下に示すように 7 つの引数をとります。

var x = {}, y = {}, width = {}, height = {};
if (typeof tree.columns != "undefined") column = tree.columns[column];
tree.boxObject.getCoordsForCellItem( row, column, part, x, y, width, height );

rowcolumnpart の各引数は、 getCellAt() 関数で返ってくる値と同様のものを指定します。 ここでも、column に設定する値は、動作する Mozilla のバージョンに応じて、文字列と列オブジェクトの適切な方を使用する必要があります。 また part 引数でセル領域の種類を指定することで、テキスト (text)、セル全体 (cell)、 ツイスティ (twisty)、セル内の画像 (image) のいずれかの領域の座標を取得することが可能です。 領域の種類の指定には、getCellAt() 関数から返される値と同じものを使用します。 getCoordsForCellItem() 関数は、取得した x, y 座標の値、および幅と高さの値を、すべて出力用パラメータに返します。

次のセクションでは、ツリーなどの要素の内容を自動的に設定するために利用可能な RDF について見ていきます。


Document Tags and Contributors

Contributors to this page: ethertank, Morishoji, Mgjbot
最終更新者: ethertank,