拡張機能の開発の仕方を学ぶ前に,まずは拡張機能を作るための基盤となる技術の「XUL(XML-based User-interfaceLanguage)」について学びましょう.
XUL文書では,用途に応じてルート要素を使い分けます.ここでは代表的な3 種類のルート要素「window要素」「page要素」「dialog要素」を紹介します.
なお,ルート要素ではwindowtype属性で,ウィンドウの種類として任意の種別名を付けることができます.たとえばFirefoxのブラウザウィンドウのwindowtypeは「navigator:browser」,オプションダイアログのwindowtypeは「Browser:Preferences」となっています.4章で紹介する方法を使うと,この値をキーにしてウィンドウを取得することができます.
また,ウィンドウの大きさや表示位置は,ルート要素のwidth,height,screenX,screenYといった属性で指定するこ とができます(いずれもピクセルを表す整数値).これらの属性と前述のpersist属性を組み合わせれば,ウィンドウの位置や大きさを簡単に記憶させておけます(▽注5).
ここまでの例に出てきたwindow要素は,汎用的なウィンドウを定義するためのルート要素です.Firefoxのブラウザウィンドウやブックマークの管理ウィンドウなど,多くのウィンドウはこの要素で表現されています.通常はルート要素としてwindow要素を使うと良いでしょう.
サイドバーパネルなど,インラインフレームの中に読み込まれ る形のXUL文書では,ルート要素としてpage要素を使います. window要素との違いは想定されている用途だけで,機能的には差 はありません.
オプションダイアログや確認のダイアログなどを作る場合は,専用のルート要素としてdialog要素が利用できます.dialog要素では,いくつかの属性を使うことで,WindowsやMac OS Xなどの実行プラットフォームのネイティブアプリケーションに合わせたボタンの配置や表示などを簡単に引き継ぐことができます(▽注6).Firefoxでも,ブックマークのプロパティなどでdialog要素が使われています.
dialog要素では,ダイアログウィンドウの下部にいくつかのボタンが表示されます.表示するボタンの種類としては表1の4種類が利用でき,表示したいボタンの名前をbuttons属性にカンマ区切りで指定します.
ボタン名 | 意味 |
---|---|
accept | OKボタン |
cancel | キャンセルボタン |
help | ヘルプボタン |
disclosure | 追加情報の表示ボタンまたは三角形のマーカー |
また,これらに加えて「extra1」と「extra2」という特殊なボタン名も使用できます.これらの追加ボタンは,ルート要素の「buttonlabelextra1」や「buttonlabelextra2」といった属性を使ってラベル文字列を自由に定義できます.
それぞれのボタンを押したときの動作は,ondialog<ボタン名>という名前のイベントハンドラで定義します.これらのイベントハンドラが指定されていなければ,acceptボタンとcancelボタンのどちらも,押すと単にそのダイアログを閉じる働きをします.リスト8は簡単なダイアログの例です.図5のように表示されます(▽注7).
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet href="chrome://global/skin/"?> <dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="My Dialog" buttons="accept,cancel" ondialogaccept="saveValues(); window.close();" ondialogcancel="window.close();"> <checkbox label="My Option"/> </dialog>
アプリケーションやWebサービスのUIによく求められる機能に,階層構造を持ったドロップダウン形式のメニューがあります.このような複雑なGUIは,従来はHTMLとJavaScriptの組み合わせで実現するのが一般的でしたが,XULでは,タグを書くだけでこのような機能も簡単に実現できます.
メニューはリスト9のように,menu要素とその関連要素の組み合わせで表現します.図6のように表示されます.
<menubar> <menu label="メニュー1"> <menupopup> <menuitem label="項目1"/> <menuitem label="項目2"/> <menuseparator/> <menuitem label="項目3"/> <menu label="サブメニュー"> <menupopup> <menuitem label="項目4"/> <menuitem label="項目5"/> </menupopup> </menu> </menupopup> </menu> </menubar>
メニューの各項目は,menuitem要素で記述します.また,項目同士をグループ化するために,menuseparator要素でセパレータを置くこともできます.menuitem要素もmenu要素も,ラベル文字列はlabel属性で指定します.
menubar要素の中にmenu要素を追加すれば,複数のメニューを並べられます.また,menupopup要素とmenu要素を入れ子にしていけば,階層化されたメニューも簡単に表現できます.
なお,menuitem要素に「class="menuitem-iconic"」(menu要素の場合はmenu-iconic)と指定して,src属性に画像のURIを指定すると,図7のようにメニュー項目名の横にアイコン画像を表示させることができます.
メニューを選択したときに何か処理を実行したい場合は,Dynamic HTMLと同様に,イベントハンドラを使用します.
HTMLではマウスとキーボード両方の操作に対応するために,onclickイベントハンドラでクリック操作を,onkeypressイベントハンドラでキーボードからの入力を捕捉する方法が一般的です.
XULでもこれらのイベントハンドラを利用できますが,マウスでの左クリック(▽注8)による選択やEnterキーによる選択といった,よくある意志決定の操作全般に対して反応する特殊なイベントハンドラとして,XULではoncommandというイベントハンドラを利用できます.リスト10はoncommandイベントハンドラを使った例です.oncommandイベントハンドラはmenuitem 要素以外にも,ボタンや入力コントロールなどさまざまな要素で利用できます.
<menuitem label="プロジェクトページを開く" oncommand="loadURI(this.value)" value="http://piro.sakura.ne.jp/xul/"/>
また,GeckoエンジンはDOM Level 2 Eventのイベントモデルを実装しているので,リスト11のように動的にイベントリスナを定義することもできます.
var item = document.getElementById('menu-item-custom'); function handleCommandEvent(aEvent) { alert('OK'); item.removeEventListener('command',handleCommandEvent, false); item.parentNode.removeChild(item); } item.addEventListener('command',handleCommandEvent, false);
menuitem要素はHTMLのinput要素のように,type属性を指定 するとチェックボックスやラジオボタンのように機能させること もできます.
menuitem要素に「type="checkbox"」と指定すると,選択するとチェックが付き,もう一度選択するとチェックが外れる,というメニュー項目を作れます.チェックボックス型のメニュー項目の例としては,Firefoxの「表示」メニューでのツールバーやサイドバーなどの表示切り替えなどがあります.そのmenuitem要素にチェックが入っているかどうかは,「checked="true"」という指定があるかどうかを見れば判別することができます.
複数のmenuitem要素に「type="radio"」と指定して,name属性に同じ値を指定しておくと,それらのmenuitem要素の中で1つを選択すれば他の選択が外れるという,ラジオボタン的な挙動になります.この種類のメニュー項目の例は,「表示」メニューの「文字エンコーディング」サブメニューなどがあります.ラジオボタン型の項目では,選択された項目にだけ「selected="true"」という指定が付きます.
要素の上で右クリックしたときなどに,任意のメニューをコンテキストメニュー(ショートカットメニュー)として表示するには,context属性を使います.
先ほどはmenu要素の子要素としてmenupopupを置きましたが,コンテキストメニューにしたいポップアップメニューは, menu要素の中には置きません.代わりに,ルート要素の直下などにmenupopup要素などを直接置いておき,そのmenupopup 要素のidを任意のXUL要素のcontext属性に指定しておくと,そのXUL要素の上で右クリックした際に,あらかじめ定義しておいたmenupopup要素の内容がコンテキストメニューとして表示されます.リスト12はコンテキストメニューの例です.
<button label="送信" oncommand="send();" context="button-context"/> <menupopup id="button-context"> <menuitem label="新しいタブで送信" oncommand="sendInNewTab();"/> </menupopup>
ユーザがクリックできるボタンは,button要素で定義します.図8のようにアイコンを表示した い場合は,image属性に画像のURIを記述します(▽注9).
image属性の代わりにicon属性を使うと,いくつかの環境ではプラットフォーム標準のボタンと同じアイコンを表示できます(▽注10).icon属性の値には,表2のものが利用できます.
ボタン名 | 意味 |
---|---|
accept | 決定ボタン |
cancel | キャンセル |
help | ヘルプ |
open | ファイルを開く |
save | ファイルの保存 |
find | 検索 |
clear | 消去 |
yes | はい |
no | いいえ |
apply | 変更などの適用 |
close | 閉じる |
印刷 | |
add | 追加 |
remove | 削除 |
reflesh | 更新 |
go-foward | 進む |
go-back | 戻る |
properties | プロパティ |
select-font | フォントの選択 |
select-color | 色の選択 |
toolbarbutton要素は,ツールバー用のボタンを定義する要素です.toolbar要素によって定義したツールバーの中に置いて使うのが一般的ですが,それ以外の個所でも利用できます.
基本的な使い方はbutton要素と同じですが,toolbarbutton要素はリスト13のようにtype属性で振る舞いを変えることができます.これは図9のように表示されます.
<toolbar> <toolbarbutton label="チェックボックス型" type="checkbox" image="firefox.png"/> <toolbarbutton label="メニュー型" type="menu" popup="button-popup" image="firefox.png"/> <toolbarbutton label="メニューボタン型" type="menu-button" popup="button-popup" image="firefox.png"/> <menupopup id="button-popup"> <menuitem label="項目1"/> <menuitem label="項目2"/> <menuitem label="項目3"/> </menupopup> </toolbar>
「type="checkbox"」と指定すると,ボタンをクリックしたら押し込まれた状態が保持され,もう一度クリックすると元の状態に戻るようになります.押し込まれたボタンは「checked="true"」と指定された状態になります.
ツールバーのカスタマイズで利用できる「ブックマーク」や「履歴」などのボタンがこのタイプです.
「type="menu"」と指定したうえで,子要素としてmenupopup要素(Firefox 3 の場合はpanel要素も可能)を置いておくか,または「popup」属性で別の個所で定義したポップアップのidを参照すると,ボタンを押したときにそのポップアップを表示するようになります.このときは,ボタン自体をクリックしても入力とは見なされず,ポップアップの項目を選択して初めてcommandイベントが発行されます.
タブバーの右端にある「タブの一覧を表示」ボタンなどもこれ にあたります.
「type="menu-button"」と指定した場合,通常のツールバーボタンと「type="menu"」と指定したボタンの両方の特徴を併せ持ったボタンになり,そのボタン自体をクリックしたときにもcommandイベントが発行されます.
これは「戻る」「進む」ボタンで利用されています.
XULでは,HTMLのフォーム関連要素によく似た入力コント ロールの要素もあります.
説明文などのテキストラベルを単体で置くには,label要素を使います.label要素では,value属性の値として指定された文字列が表示されます.control属性で他のXUL要素のidを参照しておくと,そのラベルがクリックされたりフォーカスされたりした時に,参照先のXUL要素にフォーカスを渡すようにもできます.
また,長いテキストを表示するためにも利用できます.自動的にウィンドウ幅に合わせて折り返されるような長いテキストを置きたい場合は,リスト14のようにflex属性を指定して自動的に伸縮するようにした上で,value属性の値ではなく,要素の内容として文字列を置いてください.図10のように表示されます.
<hbox> <label flex="1"> この操作を実行すると,あなたが入力した個人情報が サーバに送信されます.本当に実行してもよいですか? </label> </hbox>
label要素や,各種XUL要素のlabel属性などのように,XULでは 多くの要素でラベルを利用できます.これらの要素に共通で指定 できる属性として,crop属性があります.
ラベルを持った要素やラベル要素にcrop 属性を指定すると,親要素の幅に対してラベルが長すぎる場合に,ラベルの一部が「...」と省略して表示されるようになります.ラベルの省略位置は,crop属性の値に「start(文頭を省略)」,「center(文の真ん中)」,「end(文末)」のいずれかを指定することで変更できます.
ボックスの最大幅を指定するCSSのmax-widthプロパティなどと組み合わせて使うと良いでしょう.
図11のようなチェックボックスは,checkbox要素で記述しま す.チェックが付いた状態では,checked属性の値が「true」と なります.
ラジオボタンは,HTMLではname属性に同じ値を指定された複数のinput要素で記述しますが,XULではradiogroup要素とradio要素という2種類の要素の組み合わせで表現します.radiogroup要素の中に複数のradio要素を置くことで,排他的な 選択肢として機能するようになります.現在選択されているradio要素は「selected="true"」と指定された状態になります.
radio要素にはvalue属性で固有の値を定義できます.value属性の値はそのradio要素が選択された際にradiogroup要素へコピーされるので,選択された値をJavaScriptなどから調べるには,radiogroup要素のvalue属性を取得するだけで済みます.
なお,radio要素は必ずしもradiogroup要素の中に直接置かないといけないということはありません.hboxやvboxなどと組み合わせて,リスト15のように書くこともできます.この場合は図12のように表示されます.
<radiogroup orient="vertical"> <hbox> <radio label="左上"/> <radio label="右上"/> </hbox> <hbox> <radio label="左下"/> <radio label="右下"/> </hbox> </radiogroup>
文字入力を受け付けるにはtextbox要素を使用します.HTMLでは1行の入力にはinput要素を,複数行の入力にはtextarea要素をという風に使い分けますが,textbox要素はリスト16のように,その両方の使い方に対応しています.図13のように表示されます.
<vbox align="start"> <textbox/> <textbox multiline="true" rows="5" cols="15"/> </vbox>
textbox要素では,初期状態の内容はvalue属性で指定でき,入力された内容もvalue属性の値として取得できます.入力可能な文字数に制限を加える場合は,maxlength属性に最大文字数を正の整数で指定します.また,「type="password"」と指定することで,パスワード入力用の特殊な入力欄(▽注11)にもできます.テキストボックスでは,1文字入力されるごとにinputイベントが発生します.oninputイベントハンドラを使えば,リアルタイムで入力内容を反映するといった処理も実現可能です.
textbox要素に「type="autocomplete"」と指定すると,文字入力の自動補完(オートコンプリート)機能が有効になります.
ただし,実際に補完機能を使うにはautocompletesearch属性で補完候補の検索先を指定する必要があります.ロケーションバーへの入力履歴を参照したい場合は「history」,検索バーの入力履歴では「search-history」,その他のフォームの入力履歴は「form-history」を,いずれか1つ書くか,または複数の値をスペース区切りで列挙しておいてください.
また,オートコンプリート用の履歴を実際に読み込むためには,4章で解説する「XPConnect特権」が必要となります.
ドロップダウンリストから項目を選択するようなタイプのコントロールを作るには,menulist要素を使用します.これはリスト17のように,menulist要素とmenupopup要素の組み合わせによって実現されます.図14のように表示されます.
<menulist> <menupopup> <menuitem label="項目1" value="1"/> <menuitem label="項目2" value="2"/> <menuitem label="項目3" value="3"/> </menupopup> </menulist>
クリックするとドロップダウンリストが表示され,選択した項目のlabel属性とvalue属性の値が,menulist要素自身のlabel属性とvalue属性に設定されます.初期状態では最初のメニュー項目が選択された状態になりますが,任意のメニュー項目に「selected="true"」を指定しておくと,その項目が選択された状態になります.
なお,menulist要素に「editable="true"」という指定を加えると,ワープロソフトのフォント選択リストなどのように,自由な文字入力とドロップダウンリストからの選択の両方を受け付けるようなメニューリストを作ることもできます.
メニュー項目やボタンのアイコン以外で画像を単体で配置したい場合には,image要素を使用します.これはHTMLのimg要素と同様のもので,src属性で画像のURIを指定します.
XULではイベントハンドラに直接JavaScriptのコードを記述する他,HTMLと同様にscript要素を使って,スクリプトを埋め込んだり外部のスクリプトを読み込ませたりすることができます.
ページ内にスクリプトを埋め込む場合,「&」などの文字がエンティティ参照の開始と見なされてエラーになることを防ぐために,リスト18のようにscript要素の内容をCDATAマーク区間としておくと良いでしょう.
<script type="application/x-javascript"><![CDATA[ var nodes = gBrowser.mTabContainer.childNodes; for (var i = 0; i < nodes.length; i++) alert(nodes[i].label); ]]></script>
XULでは,インラインフレームも利用できます.iframe要素に対してsrc属性でURIを指定すれば,他のXUL文書やWebページなどを内部に表示させることができます.ただし,XULではiframe要素が使われることは稀で,実際には代わりにbrowser要素が使われる場合が多いです.
browser要素は,Webブラウザとしての基本機能を備えた高機能なインラインフレームです.browser要素はiframeと違って,前後のページへ移動するための機能を持っていたり,フレーム内部から外部へのスクリプトによるアクセスを禁止する機能があったりと,外部のWebページと連携したアプリケーションを作成するうえで,より安全で便利なように設計されています.
tabbrowser要素はbrowser要素をさらに強化したもので,Firefoxタブ機能の基本的な部分を内蔵しています.利用するにはオートコンプリートと同様に,4章で解説する「XPConnect特権」が必要となります.
browser要素にsrc属性でURIを指定しておくと,初期状態でそのURIのページが読み込まれます.また,src属性の値を変更すると動的に他のWebページやXUL文書を読み込ませることもできます.
「戻る」「進む」「更新」などの機能は,それぞれ「goBack」「goForward」「reload」といったメソッドを使っ て呼び出すことができます.「stop」メソッドを使えば,読み込みを途中で中断することもできます.
HTMLのiframe要素やframe要素などによって定義されたフレームでは,windowオブジェクトのparentプロパティやtopプロパティを取得することで,入れ子になったフレームの親や祖先にあたるフレームにアクセスすることができます.しかし任意のWebページを読み込むようなアプリケーションでは,WebページのスクリプトからXUL文書のフレームにアクセスされて,重要な個人情報などを抜き取られてしまうおそれがあります.
browser要素に「type="content"」という指定を加えると,セキュリティのためのアクセス制限が有効になります.この時,browser要素に読み込まれたWebページにとっては,そのWebページ自身が最上位のフレームとして見えるようになります.Webページを読み込んで表示するインターフェースを持ったアプリケーションを開発する場合などには,必ずこの指定によって親フレームへのアクセスを制限しておきましょう.
なお,特別な場合として「type="content-primary"」という指定を使うこともできます.この指定がされたbrowser 要素は,そのXUL文書の中でも特別なbrowser要素として扱われるようになり,表示されているWebページのwindowオブジェクトに「window.content」というプロパティでアクセスできるようになります.Firefoxにおいても,アクティブなタブの内容領域に該当するbrowser要素は「type="content-primary"」が指定されています.
Dynamic HTMLベースのインターフェースでキーボードショートカット(CtrlキーやShiftキーなどを押しながら他のキーを押す操作)を実現するには,キーボードからの入力を捕捉するようなスクリプトを使用しないといけません.
XULではキーボードショートカットを,リスト19のようにkey要素を置くだけで定義できます.
<key id="key-save" key="s" modifiers="accel,shift" oncommand="save();"/> <key id="key-scroll-up" keycode="VK_PAGE_UP" oncommand="advanceFocus(-1);"/>
英数字,記号,およびスペースキーをトリガーにする場合は,key属性にそのキーの文字を指定します.大文字小文字どちらを指定しても等しく扱われます.
それ以外の特殊文字をトリガーにするには,keycode属性でキーコード名を指定します.基本的にはすべてのキーに対応するキーコード名が用意されていますが,代表的なものを表3に挙げました.
キー | キーコード名 |
---|---|
リターン | VK_RETURN |
Enter | VK_ENTER |
BS | VK_BACK_SPACE |
Delete | VK_DELETE |
ESC | VK_ESCAPE |
↑ | VK_UP |
↓ | VK_DOWN |
← | VK_LEFT |
↓ | VK_RIGHT |
Ctrl(Control)キーやShiftキーなどの修飾キーと同時にキーが押された場合にのみ反応させるには,modifiers 属性に修飾キーの名前「control」「alt」「shift」「meta(▽注12)」のいずれかまたは複数をカンマ区切りで列挙します(▽注13).
なお,key要素自身は画面上には表示されません.図15のように定義されたキーボードショートカットを示す文字列を表示させるには,menuitem要素またはmenu要素のkey属性からkey要素のidを参照してください.
XULには,GUI部品のレイアウトを調整するためだけの要素もいくつかあります.その中でもよく使うものを紹介します.
ボタンとボタンの間に間隔を空けたい場合などには,spacer要素を使います.flex属性やstyle属性などで伸縮率や大きさを指定すれば,任意の大きさの空白を作れます.
HTMLでtable要素を使って内容を配置するような用途には,XULではgrid要素を使います.リスト20のように,列数を指定してから行の内容を定義します.この例では図16のように表示されます.rowsを先に記述すると,行数を指定してから列の内容を定義することもできます.
なお,gridにはHTMLのtable要素におけるrowspan属性やcolspan属性のような機能はありません.
<grid> <columns> <column/> <column flex="1"/> </columns> <rows> <row align="center"> <label value="ユーザID"/> <textbox/> </row> <row align="center"> <label value="名前"/> <textbox/> </row> </rows> </grid>
複数のGUI部品を重ねて表示させたい場合には,stack要素を使います.stack要素は通常のボックスとは異なり,中に置かれた要素を下から順番に重ねて表示します.リスト21は,処理の進行状況を示すプログレスバーに文字を重ねる例で,図17のように表示されます.
<stack> <progressmeter mode="normal" value="50"/> <hbox align="center"> <label value="処理中です..."/> </hbox> </stack>
プロパティのダイアログなどのように複数のページをタブで切り替えられるようにするには,tabbox要素とその関連要素を使います(リスト22).図18のように表示されます.
<tabbox> <tabs> <tab label="tab1"/> <tab label="tab2" selected="true"/> </tabs> <tabpanels> <tabpanel> <checkbox label="check1"/> </tabpanel> <tabpanel orient="vertical"> <checkbox label="check2"/> <checkbox label="check3"/> </tabpanel> </tabpanels> </tabbox>
△注5: | Firefoxでもウィンドウの大きさや位置を保存するのにこの方法が使われています. |
△注6: | 例えば,Windowsでは「OK」ボタンが左,「キャンセル」が右側に表示されますが,Mac OS Xでは逆の並び方になります. |
△注7: | ここで紹介しているdialog要素の機能は,4章で紹介している「XPConnect特権」を必要とするため,この例はFirefox本体のコードやインストール済みの拡張機能のコードとして実行した場合にのみ正常に動作します.サンプルをそのままFirefoxに読み込ませても正常には動作しませんので注意してください. |
△注8: | 左利き用設定の場合は右クリックです. |
△注9: | サンプルを実際に表示してみる場合,ボタンに表示する画像は,任意の物を用意してXUL文書と同じフォルダに置いてください. |
△注10: | Firefox 2では,LinuxのGNOME環境のみ対応しています. |
△注11: | 文字を入力すると伏字で表示されるようになります. |
△注12: | Mac OSのCommandキー(リンゴのマークのキー)のことです. |
△注13: | WindowsやLinuxなどにおける標準の修飾キーはCtrl,MacにおいてはCommandと,異なっています.これらの修飾キーを指定する代わりに「accel」を修飾キー名として使用すると,WindowsやLinuxではCtrlキー,MacではCommandキーを押したときに反応するという,標準的なキーボードショートカットを簡単に定義できます. |