ツリー

XUL では、ツリーを利用して表や階層リストを作成する方法を提供しています。

ツリー

XUL の要素の中で最も複雑ものの 1 つに、ツリーがあります。 ツリーは、テキスト行を複数の列に分けて表示するために使用されます。 このとき、行はフラットに並べたり、階層を作って整理したりすることができます。 ツリーでは、利用者による行の並べ替えや、個々の列の非表示化、表示幅の変更を行う操作も可能です。 ツリーの例としては、メールアプリケーションのメッセージリストや Mozilla のブックマークウィンドウなどがあります。

いくつかの点で、ツリーはリストボックスと類似しています。 どちらも複数の行と列を持った表を作成するのに利用でき、 どちらも各列に見出しをつけることができます。 また、相違点もあります。 行の入れ子は、ツリーでのみサポートされ、リストボックスではできません。 逆に、中に含むことができる内容については、リストボックスでは任意の種類のものが可能ですが、ツリーではテキストと画像のみに限られます。

ツリーは 2 つの部分、「列のセット」と「本体」から構成されています。

  • 列のセットは、一連の treecol 要素で定義されます。個々の treecol は 1 つの列に対応しており、ツリーの上端に見出しとして表示されることになります。
  • ツリー本体は、ツリーに表示されるデータを保持するもので、treechildren タグによって作成されます。

ツリーは、その本体が単一のウィジェットから構成されていて、それがツリー内のすべてのデータの描画を行います。 これはツリー独特の構造で、 例えばリストボックスの場合は、本体は listitemlistcell タグで指定される個々の行の集合として構成されています。 このために、ツリーでは、表示される全てのデータは、ツリービューと呼ばれる別のオブジェクトから供給されます。 具体的には、セルを実際に表示するタイミングで、ツリーのウィジェットからツリービューへ表示する内容について問い合わせが行われ、その結果がツリーへ描画されることになります。 ツリーは、実際に表示が必要な行の情報のみをビューへ問い合わせる点で効率的です。 これにより、表示中の内容に必要なデータのみを読み込むようにビューを最適化することができます。 極端な話、たとえツリーに数千の行があったとしても、ほとんどの行は表示エリアの境界の外にあって、スクロールさせない限りは見えないため、ツリーでは情報を保持しません。 このため、ツリーは行数がどれだけ増加しても、パフォーマンスの問題が発生しないスケーラブルなオブジェクトであるといえます。 ただし、ビューオブジェクトのパフォーマンスは、別途考慮する必要があります。

ツリービューとは、nsITreeView インターフェイスを実装したオブジェクトのことです。 このインターフェイスには 30 のプロパティと関数があるので、ツリービューを作成するためには、それらを適宜実装していくことになります。 これらの関数は、ツリーオブジェクトから、対応するデータや状態を取得するために必要に応じて呼び出されます。 例えば、getCellText() 関数は、ツリー中の個別のセルに対してつけるべきラベルを取得するときに呼び出されます。

ツリービューを使用する利点としては、 対象のデータを、そのデータにおける最適な方法で保持できることや、データをその行の表示に応じてオンデマンドで読み込めることがあります。 これらによって、ツリーを利用する場合に高い柔軟性を得ることができるわけです。

単純なツリーの場合は特にそうなのですが、ツリーごとに毎回 30 ものプロパティやメソッドを持ったツリービューを実装するのは、当然ながら非常に面倒な作業になります。 幸い、XUL には、面倒な作業のほとんどが済んでいるビューの実装が 2 つ、組み込みで用意されています。 ほとんどのツリー、特に初めてツリーを使うような場合には、この組み込みビューのどちらかを利用することになると思います。 また、必要ならビューを完全にスクラッチから実装することもできます。 その場合は、データを配列や JavaScript のデータ構造として保持するか、あるいは XML ファイルから読み込むことを検討するとよいかもしれません。

ツリーの本体全部が単一のウィジェットであるため、 通常のやり方では、個別に行や列のスタイルを変えることはできません。 これは、リストボックスのように、 個々のセルを表示するための要素が存在しないことに起因しています。 子要素を持つ代わりに、ツリー本体のすべての描画は、ビューから供給されるデータを元に行われることになります。 これは重要な点なのですが、多くの XUL 開発者がこの側面を理解するのに難儀しています。 なお、ツリーのセルの外観を変更するためには、ビュー側でいくつかのキーワードを行と列に関連付けるようにする必要があります。 そして特別な CSS 構文を使って、ツリー本体の構成部分でキーワードと対応するものについてスタイルを設定します。 これは、ある意味 CSS クラスを利用した場合と似ています。 ツリーのスタイル付けの詳細については、後のセクションで説明する予定です。

ツリー関連の要素

詳細はこの後に説明しますが、 ツリーは tree 要素で作成することが可能です。 また、それ以外にツリーに表示される列の定義に関する要素が 2 つあります。

tree
ツリーの最も外側の要素です。
treecols
treecol 要素を置くための場所です。
treecol
ツリーの 1 つの列を宣言します。この要素には、列内のデータをどのような順で並べるかや、ユーザが列幅を変えられるかといった追加情報も指定できます。また id 属性は常に指定しておく必要があります。Mozilla 1.8 以降で必須ではなくなりましたが、以前の Mozilla は列を並び替えたり隠したりする際に id によって列を識別したため必須でした。なお必須ではなくとも、列に id を指定しておくことは良い考えだと思います。
treechildren
表示される個々の行や列を含んだツリーの本体です。

2 つの列を持つツリーの例

以下に 2 つの列を持つツリーの例を示します。

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

<tree flex="1">

  <treecols>
    <treecol id="nameColumn" label="Name" flex="1"/>
    <treecol id="addressColumn" label="Address" flex="2"/>
  </treecols>

  <treechildren/>

</tree>

まず、表全体が tree 要素で囲われています。 これにより、表またはツリーとして使用される部分を 1 つの要素として宣言します。 また、HTML のテーブルと同様に、ツリー内のデータは、常に行に基づいて構成されます。 そのため、列は treecols タグの中にまとめて指定することになります。

ツリーには、いくつ列を追加してもかまいません。 リストボックスと同様に、ツリーでも列の見出し行が表示されます。 また、利用者が個々の列について表示の有無を設定できるように、ツリーの右上隅にドロップダウンメニューが表示されます。 個々の列は、treecol 要素により作成することができ、 見出しに表示するラベルは、この要素の label 属性を使用して設定できます。 また、ツリーが 伸縮可能な場合、列もツリーに応じて伸縮できるように、伸縮可能に設定したいはずです。 この例では、flex 属性の比率に従って、2 番目の列の幅は、最初の列のおおよそ 2 倍になります。 なお、ツリーでは全ての列は treecols 要素内に直接置く必要があります。

この例では、ツリーにデータを与えるためのビューが指定されていないため、 列の見出しと空のツリー本体以外は見ることはできません。 さらに表示するべきデータがないため、本体部分を見るためには、ウィンドウをリサイズする必要があります。 このツリーは伸縮可能であるため、本体も利用可能なスペースに応じて伸張します。 ツリーを伸縮可能に設定することは、ごく普通に行われています。 ツリーのデータは、表示されている情報の中でも最も重要であることが多いため、 リサイズに応じてツリーが大きくなるようにするのは有意義なことが多いからです。 しかしながら、tree 要素に rows 属性を設定することで、 ツリー内に、特定の行数のみを表示させるようにすることも可能です。 なお、この属性はユーザインターフェイスにおいて何行表示させるかを指定するものであり、データが何行あるかを指定するものではありません。 全体の行数はツリービューによって与えられます。 データの行数が表示行数を超える場合、利用者が残りの部分を閲覧できるようにスクロールバーが表示されます。 rows 属性を指定しない場合、デフォルト値は 0 なので、データがないときは、なにも表示されません。 この場合も、ツリーを伸縮可能にしておくことで、利用できるスペースがあるときには、あわせて大きくなるようにできるため rows 属性を明示的に設定する必要はありません。

コンテントツリービュー

ツリーに表示するデータは、XULタグではなくビューから与えられると述べたところですが、 偶然にも、組み込みのツリービューに、XUL タグからデータを取り出すような仕組みになっているものがあります。 少し混乱するかもしれませんが、 つまり組み込みビューのうちの 1 つでは、ツリーに供給するデータに関する情報を指定するために、いくつかのタグを使用できるということです。

treeitem
ツリーの親となる単独の行と、その子孫を含んでいます。この要素は、利用者に選択されうる項目としても働きます。treeitem タグは、選択時に子孫も含めて選択されるように、行全体を囲みます。
treerow
ツリー中の 1 つの行に対応します。treeitem タグの中に置く必要があります。
treecell
ツリー中の 1 つのセルに対応します。この要素は、treerow 要素の中に置きます。

便利なことに、これらのタグは treechildren タグの中に直接置くことができます。 これらのタグを、上に記述した順で入れ子に置いていくことで、ツリー本体に表示するデータを定義することができます。 この場合、ツリーは組み込みのツリービューを利用することになります。 このツリービューは、コンテントツリービューと呼ばれ、これらの要素で指定されたラベルと値をツリーのデータとして供給します。 つまり、ツリーで行の表示が必要になったとき、 ツリーはコンテントツリービューの getCellText() 関数を呼び出してセルのラベルを問い合わせ、 該当する treecell 要素のラベルとして設定されているデータを取得することになります。

なお、これらの 3 つの要素は、直接表示されることはなく、 ビューが供給するデータの取得元としてのみ使用されます。 したがって、treeitem 要素と、その関連要素に対して適用可能な属性は、ほんの一握りです。 具体的には、style 属性などの CSS プロパティを利用して外観を変えたり、 伸縮可能性 (flex) や、方向 (orient) などボックス関連の機能を利用するようなことはできません。

実際、ツリー固有の属性を別にすれば、 有効な属性は、セルのテキストラベルを設定するための label 属性と、画像を設定する src 属性のみです。 なお、ツリーには、スタイルを付けたり、他の機能についての設定するために特別な方法が用意されており、 それらについては、後のセクションで説明する予定です。

また、イベントについても treeitem や、その子要素へは送信されず、全て treechildren 要素へ送信されます。

treeitem 系の要素が、他の XUL 要素と異質であることが、XUL 開発者を混乱させる共通の原因になっています。 コンテントツリービューの本質はビューであり、単にツリーに供給するセルのデータをツリー内に置かれたタグから取得しているだけに過ぎません。 したがって、別の種類のビューを利用する場合、データは他のところから供給されるため、treeitem 系の要素はまったく必要ありません。

コンテントツリービューの例

それでは、コンテントツリービューを利用して、複数の列を持つ単純なツリーの作り方を見てみましょう。 メールメッセージのリストとして使えるものを作成してみることにします。 したがって、このリストには、送信者 (Sender) や題名 (Subject) など、複数の列を作成します。

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

<tree flex="1">

  <treecols>
    <treecol id="sender" label="Sender" flex="1"/>
    <treecol id="subject" label="Subject" flex="2"/>
  </treecols>

  <treechildren>
    <treeitem>
      <treerow>
        <treecell label="joe@somewhere.com"/>
        <treecell label="Top secret plans"/>
      </treerow>
    </treeitem>
    <treeitem>
      <treerow>
        <treecell label="mel@whereever.com"/>
        <treecell label="Let's do lunch"/>
      </treerow>
    </treeitem>
  </treechildren>

</tree>
画像:trees1.png

画像から明らかなように、2 つの行 (row) を持ったツリーが作成されています。

また、このツリーは 2 つの列 (column) を持ち、 2 列目は 1 列目よりも幅が広くとられています。 通常、列は伸縮可能にすると思いますが、width 属性により明示的に幅を指定することも可能です。 treecol 要素は、ツリーの列の数と同じ数を置く必要があります。 そうしないと、おかしな挙動が発生するかもしれません。

見出し行は自動的に生成されます。 また、右上隅のボタンで、列を隠したり表示させたりすることができます。 このボタンを表示したくない場合は、ツリーに対して hidecolumnpicker 属性を置いて、値 true を設定します。 このボタンを非表示にすることで、利用者が列を隠すことを抑止できます。

各列に id 属性を設定するようにしてください。 そうしないと、Mozilla の古いバージョンで、列の表示と非表示の切り替えが動作しません。

treechildren 要素は、すべての行を囲むように置きます。 その中には個々の行が置かれており、場合によって入れ子の行を含んでいることがあります。 単純なツリーでは、各行は treeitemtreerow 要素からなっています。 treerow 要素は、1 つの行に含まれるすべてのセルを囲み、treeitem 要素は、1 つの行と、そのすべての子孫を囲みます。 なお、入れ子になった行を持つツリーについては、次のセクションで説明します。

行の中には、個々のツリーのセルを置きます。 これらのセルは、treecell 要素を使用して作成します。 セルのテキストは label 属性で与えます。 ツリーに表示されるときは、その行で最初の treecell 要素が、最初の列に表示される内容と対応し、 2 番目の treecell 要素が 2 列目に表示される内容に対応するといったように、順に対応しています。

利用者は、マウスクリックかキーボードからのハイライト操作によって、ツリーの行を選択することができます。 シフトキーかコントロールキーを押した状態で、追加したい行をクリックすることで、複数の項目を選択することも可能です。 複数選択を無効にしたい場合は、ツリーに seltype 属性を置いて、値 を single に設定します。 これにより、利用者は 1 度に 1 行しか選択できないようになります。

ファイル検索ダイアログにツリーを追加

それでは、ファイル検索ダイアログに検索結果の表示のためにツリーを加えてみましょう。 このツリーでは、コンテントツリービューを使用します。 スプリッターのときに、当座の間に合わせとして、iframe を置いた場所に、次のコードを加える必要があります。

<tree flex="1">
  <treecols>
    <treecol id="name" label="Filename" flex="1"/>
    <treecol id="location" label="Location" flex="2"/>
    <treecol id="size" label="Size" flex="1"/>
  </treecols>

  <treechildren>
   <treeitem>
     <treerow>
       <treecell label="mozilla"/>
       <treecell label="/usr/local"/>
       <treecell label="2520 bytes"/>
     </treerow>
   </treeitem>
  </treechildren>
</tree>

<splitter collapse="before" resizeafter="grow"/>

追加したツリーに「ファイル名」「場所」「サイズ」の 3 つの列が加えられています。 2 列目の幅は、2 倍の伸縮性を設定しているので、2 倍広く表示されます。 ここでは、行の情報は、とりあえず行を持った表の外観を確認するために、1 行だけを加えてあります。 現実の実装では、行の情報は、検索を実行するスクリプトによって動的に追加されるか、 検索結果のデータを保持するカスタムビューを作成することになるでしょう。

var el = env.locale; ここまでのファイル検索ダイアログの例 : ソース 表示

次のセクションでは、さらに高度なツリーの作成について学びます。


ドキュメントのタグと貢献者

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