XBL Example

XBL の例

This section will describe an example XBL element.

このセクションでは、XBL 要素の例を説明します。

A Slideshow Element

スライドショー (Slideshow) 要素

Let's construct a full example of an XBL element. This will be a widget that stores a deck of objects, each displayed one at a time. Navigation buttons along the bottom will allow the user to cycle through the objects while a text widget between the buttons will display the current page. You could put anything within the pages, however, this widget might be useful for a set of images. We'll call this a slideshow element.

XBL 要素の完全な例を作ることにしましょう。これは、一度に一つずつ表示される オブジェクトのデック (deck) を保存するウィジェットです。下部にあるナビゲーション ボタンによって、ユーザは、オブジェクトをたどることができます。ボタンの間にある テキストウィジェットは現在のページを表示します。このページには何でも置くことが できますが、イメージセットに使うと役立つかもしれません。これをスライドショー (slideshow) 要素と呼ぶことにします。

First, let's determine what elements need to go in the XBL content. Because we want page flipping, a deck element would be the most suitable to hold the page content. The content of the pages will be specified in the XUL file, not in XBL, but we'll need to add it inside the deck. The children tag will need to be used. Along the bottom, we'll need a button to go the previous page, a text widget to display the current page number, and a button to go to the next page.

まず、XBL 内容にどんな要素が必要かを決めることにしましょう。 ページがめくれるようにしたいので、ページの内容を保持するには deck 要素が最適です。ページの 内容は XBL ではなく XUL ファイルで指定しますが、それはデック内に追加する 必要があります。children タグを使わなければならないでしょう。下部には、前のページに戻るためのボタンと 現在のページ番号を表示するテキストウィジェット、次のページに進むボタンが必要です。

Example 11.8.1: Source
<binding id="slideshow">
  <content>
    <xul:vbox flex="1">
      <xul:deck xbl:inherits="selectedIndex" selectedIndex="0" flex="1">
        <children/>
      </xul:deck>
      <xul:hbox>
        <xul:button xbl:inherits="label=previoustext"/>
        <xul:label flex="1"/>
        <xul:button xbl:inherits="label=nexttext"/>
      </xul:hbox>
    </xul:vbox>
  </content>
</binding>

This binding creates the slideshow structure that we want. The flex attribute has been added to a number of elements so that it stretches in the right way. The label attributes on the two buttons inherit their values from the bound element. Here, they inherit from two custom attributes, previoustext and nexttext. This makes it easy to change the labels on the buttons. The children of the element that the XBL is bound to will be placed inside the deck. The selectedIndex is inherited by the deck, so we may set the initial page in the XUL.

このバインディングは、必要だと思ったスライドショーの構造を作っています。 flex 属性を要素の幾つかに追加し、正しく伸縮する ようにします。2 つのボタンの label 属性は、 それが結び付けられた要素から値を継承します。ここでは、2 つのカスタム属性である previoustextnexttext を継承しています。これによって、ボタンのラベルの変更が簡単になります。 XBL が結び付けられている要素の子供は、 deck 内部に置きます。 selectedIndex が deck に継承されているので、 XUL で最初のページを設定できます。

The following XUL file produces the result in the image.

以下の XUL ファイルは、下の画像にある結果になります。

<box class="slideshow" previoustext="Previous" nexttext="Next" flex="1">
  <button label="Button 1"/>
  <checkbox label="Checkbox 2"/>
  <textbox/>
</box>

The style sheet used here is:

使用したスタイルシートは次のものです:

.slideshow {
  -moz-binding: url("slideshow.xml#slideshow");
}

The first button, 'Button 1' has been used as the first page of the deck. The label widget has not appeared as no value has been specified for it. We could set a value, but instead it will calculated later.

最初のボタン 'Button 1' は、デックの最初のページとして使われています。 label ウィジェットは表示されて いません。そのための value が指定されていないからです。 値を設定することもできますが、そうする代わりに、後で計算することにしましょう。

Next, a property that holds the current page will be added. When getting this custom property, it will need to retrieve the value of the selectedIndex attribute of the deck, which holds the number of the currently displayed page. Similarly, when setting this property, it will need to change the selectedIndex attribute of the deck. In addition, the text widget will need to be updated to display which page is the current one.

次に、現在のページを保持するプロパティを追加します。このカスタムプロパティを 得るには、現在表示されているページの番号を保持している、デックの selectedIndex 属性の値を取得する必要があります。 同様に、このプロパティを設定するには、デックの selectedIndex 属性を変更する必要があります。更に、 どのページが現在表示されているのかを示すため、テキストウィジェットを更新する 必要があります。

<property name="page"
    onget="return parseInt(document.getAnonymousNodes(this)[0].childNodes[0].getAttribute('selectedIndex'));"
    onset="return this.setPage(val);"/>

The 'page' property gets its value by looking at the first element of the anonymous array. This returns the vertical box, so to get the deck, we need to get the first child node of the box. The anonymous array isn't used as the deck is not anonymous from the box. Finally, the value of the selectedIndex attribute is retrieved. To set the page, a method 'setPage' is called which will be defined later.

'page' プロパティは、無名配列の最初の要素を見ることによって、その値を取得します。 これは垂直方向のボックスを返します。そのため、デックを取得するには、ボックス の最初の子供ノードを取得する必要があります。ここでは、無名配列は使用しません。 デックはボックスから見ると無名ではないからです。最後に、 selectedIndex 属性を取得し、それを返します。 'page' を設定するには、後で定義する 'setPage' を呼び出します。

An oncommand handler will need to be added to the Previous and Next buttons so that the page is changed when the buttons are pressed. Conveniently, we can change the page using the custom 'page' property that was just added:

oncommand ハンドラは、ボタンが押されたときページが 変更されるようにするため、Previous ボタンと Next ボタンに追加する必要があります。 便利なことに、たった今追加したカスタム 'page' プロパティを使ってページを変更する ことができます。

<xul:button xbl:inherits="label=previoustext"
               oncommand="parentNode.parentNode.parentNode.page--;"/>
<xul:description flex="1"/>
<xul:button xbl:inherits="label=nexttext"
               oncommand="parentNode.parentNode.parentNode.page++;"/>

Because the 'page' property is only on the outer XUL element, we need to to use the parentNode property to get to it. The first parentNode returns the parent of the button which is the horizontal box, the second its parent, the vertical box, and finally, its parent which is the outer box. The 'page' property is incremented or decremented. This will call the onget script to get the value, increment or decrement the value by one, and then call the onset handler to set the value.

'page' プロパティは、外側の XUL 要素だけにあるので、そこに行くには parentNode プロパティを使う必要があります。最初の parentNode はボタンの親を返します。 これは水平方向のボックスです。その 2 番目の親は垂直方向のボックスです。 最後の親は外側のボックスになります。'page' プロパティは加減算されます。 これを行なうには、まずその値を取得する onget スクリプトを呼び出し、値に 1 足すか引くかして、値を設定する onset ハンドラを呼び出します。

Now let's define the 'setPage' method. It will take one parameter, the page number to set the page to. It will need to make sure the page is not out of range and then modify the deck's selectedIndex attribute and the text widget's label attribute.

'setPage' メソッドを定義しましょう。これはパラメータを一つ取ります。page に設定するページ番号です。ページが範囲外ではないことを確かめ、デックの selectedIndex とテキストウィジェットの label 属性を変更する必要があります。

<method name="setPage">
  <parameter name="newidx"/>
  <body>
    <![CDATA[
      var thedeck=document.getAnonymousNodes(this)[0].childNodes[0];
      var totalpages=this.childNodes.length;  

      if (newidx<0) return 0;
      if (newidx>=totalpages) return totalpages;
      thedeck.setAttribute("selectedIndex",newidx);
      document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1]
              .setAttribute("value",(newidx+1)+" of "+totalpages);
      return newidx;
    ]]>
  </body>
</method>

This function is called 'setPage' and takes one parameter 'newidx'. The body of the method has been enclosed inside '<![CDATA[' and ']]>'. This is the general mechanism in all XML files that can be used to escape all of the text inside it. That way, you don't have to escape every less-than and greater-than sign inside it.

この関数は 'setPage' という名前で、'newidx' というパラメータを取ります。 メソッドの本体は '<![CDATA[' と ']]>' の内側にあります。これは、 その中にあるテキストすべてをエスケープするため、あらゆる XML ファイルで 使われる一般的なメカニズムです。こうすれば、内部にある小なり記号と大なり記号 すべてをエスケープする必要がありません。

Let's break down the code piece by piece.

コードを部分ごとに細分化して見ていくことにしましょう。

  • var thedeck=document.getAnonymousNodes(this)[0].childNodes[0];
    Get the first element of the anonymous content array, which will be the vertical box, then get its first child, which will be the deck element.
  • var totalpages=this.childNodes.length;
    Get the number of children that the bound box has. This will give the total number of pages that there are.
  • if (newidx<0) return 0;
    If the new index is before the first page, don't change the page and return 0. The page should not change to a value earlier than the first page.
  • if (newidx>=totalpages) return totalpages;
    If the new index is after the last page, don't change the page and return the last page's index. The page should not change to one after the last page.
  • thedeck.setAttribute("selectedIndex",newidx);
    Change the selectedIndex attribute on the deck. This causes the requested page to be displayed.
  • document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1].setAttribute("value",(newidx+1)+" of "+totalpages);
    This line modifies the label element so that it displays the current page index. The label element can be retrieved by getting the first element of anonymous content (the vertical box), the second child of that label element (the horizontal box), and then the second element of that box. The value attribute is changed to read '1 of 3' or something similar. Note that one is added to the index because indicies start at 0.
  • var thedeck=document.getAnonymousNodes(this)[0].childNodes[0];
    無名内容配列の最初の要素を取得する。これは垂直方向のボックスである。 次に、その最初の子供を取得する。これはデック要素である。
  • var totalpages=this.childNodes.length;
    結び付けられたボックスがもつ子供の数を取得する。これは存在するページの総数である。
  • if (newidx<0) return 0;
    新しいインデックスが最初のページより前になったら、ページ変更はせずに 0 を返す。 ページは、最初のページより前のものに変更すべきではない。
  • if (newidx>=totalpages) return totalpages;
    新しいインデックスが最後のページより後になったら、ページ変更はせず、最後の ページのインデックスを返す。ページは、最後のページより後のものに変更すべきではない。
  • thedeck.setAttribute("selectedIndex",newidx);
    デックの selectedIndex 属性を変更する。 これによって、リクエストされたページが表示される。
  • document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1].setAttribute("value",(newidx+1)+" of "+totalpages);
    この行は、現在のページインデックスを表示する label 要素を変更する。 label 要素は、無名内容の最初の要素 (垂直方向のボックス) 、その 2 番目の子供 (水平方向のボックス)、そしてそのボックスの 2 番目の要素を得ることによって取得できる。 value 属性は、'1 of 3' やそれに似たものに変更される。 インデックスは 0 から始まるため、1 がそのインデックス値に追加されていることに 気をつけてください。

We will also need a constructor to initialize the label element so that it displays correctly when the slideshow is first displayed. We use similar code as to the method above to set the page number. The reference to 'this.page' will call the onget script of the page property, which in turn will retrieve the initial page from the selectedIndex attribute.

label 要素を初期化するコンストラクタも必要でしょう。それによってスライドショーが 最初に表示されたときにも、正しく表示されます。ページ番号をセットするのに、 上のメソッドに似たコードを使います。'this.page' への参照は page プロパティの onget スクリプトを呼び出し、今度は selectedIndex 属性から初期時のページを取得します。

<constructor>
  var totalpages=this.childNodes.length;
  document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1]
          .setAttribute("value",(this.page+1)+" of "+totalpages);
</constructor>

We can add some additional features as well. Some keyboard shortcuts could be used for the Previous and Next buttons, (say backspace and the Enter key). First and Last buttons could be added to go to the first and last pages. The label element could be changed to a field where the user could enter the page to go to, or a popup could be added to allow selection of the page from a menu. We could also add a border around the deck with CSS to make it look a bit nicer.

追加機能を加えることもできます。キーボードショートカットが Previous ボタンと Next ボタンに使えるでしょう (例えば、バックスペースと Enter キー)。最初のページ と最後のページに行くために First ボタンと Last ボタンを追加することもできるでしょう。 label 要素をフィールドに変更して、ユーザが行きたいページを入力するようにする こともできます。あるいは、ポップアップを追加して、メニューからページの選択が できるようにすることもできるでしょう。CSS を使ってデックの周囲に境界を付ける こともできます。そうすれば、見栄えが少しよくなるでしょう。

The final code is as follows:

最終コードは次の通りです。

Example 11.8.2: Source
<binding id="slideshow">
  <content>
    <xul:vbox flex="1">
      <xul:deck xbl:inherits="selectedIndex" selectedIndex="0" flex="1">
        <children/>
      </xul:deck>
      <xul:hbox>
        <xul:button xbl:inherits="label=previoustext"
                    oncommand="parentNode.parentNode.parentNode.page--;"/>
        <xul:description flex="1"/>
        <xul:button xbl:inherits="label=nexttext"
                    oncommand="parentNode.parentNode.parentNode.page++;"/>
      </xul:hbox>
    </xul:vbox>
  </content>

  <implementation>

    <constructor>
      var totalpages=this.childNodes.length;
      document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1]
              .setAttribute("value",(this.page+1)+" of "+totalpages);
    </constructor>

    <property name="page"
          onget="return parseInt(document.getAnonymousNodes(this)[0].childNodes[0].getAttribute('selectedIndex'));"
          onset="return this.setPage(val);"/>

    <method name="setPage">
      <parameter name="newidx"/>
      <body>
        <![CDATA[
          var thedeck=document.getAnonymousNodes(this)[0].childNodes[0];
          var totalpages=this.childNodes.length;  

          if (newidx<0) return 0;
          if (newidx>=totalpages) return totalpages;
          thedeck.setAttribute("selectedIndex",newidx);
          document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1]
                  .setAttribute("value",(newidx+1)+" of "+totalpages);
          return newidx;
        ]]>
      </body>
    </method>
  </implementation>

</binding>

(Next) Next, we'll see some additional features of a window.

(進む) 次は、ウィンドウの追加的な機能を幾つか見ることにしましょう。

Examples: 11.8.1 11.8.2


Copyright (C) 1999 - 2004 XulPlanet.com