Now browsing the archives for the 'XUL Widgets' category.

xul:textbox を最終行までスクロールする

multiline=”true” な複数行の xul:textbox 要素を最終行までスクロールする。

// assuming that elt is a xul:textbox element
elt.inputField.scrollTop = elt.inputField.scrollHeight;

TOP

xul:prefwindow の設定ダイアログにヘルプボタンを表示する

Firefox 本体の「オプション」ダイアログのように、 xul:prefwindow 要素で作った設定ダイアログに、「ヘルプ」ボタンを表示したい。

xul:prefwindow の buttons 属性?

xul:prefwindow 要素の buttons 属性の説明を読むと、表示したいボタンをカンマ区切りで指定する、と書いてあるので以下のようにしてみた。しかし、OKボタン・キャンセルボタンは表示されるものの、ヘルプボタンは表示されなかった。

<prefwindow buttons="accpet,cancel,help">

ちなみに、 xul:dialog 要素のダイアログであれば、上記方法でもヘルプボタンが表示される。

xul:prefpane の helpURI 属性?

xul:prefpane 要素の helpURI 属性の説明を読むと、設定用パネルに関連付けられた URI を指定する、と書いてあるので以下のようにしてみた。しかし、依然としてヘルプボタンは表示されなった。

<prefpane helpURI="http://www.example.com/">

xul:prefpane の helpTopic 属性!

MDC のドキュメントには説明が無いが、以下のように各設定パネル(xul:prefpane 要素)に helpTopic 属性を指定することで、ようやくヘルプボタンの表示が可能となった。なお、 helpTopic の値は各設定パネルを識別可能な文字列を適当に入れておけばよい。

<prefpane helpTopic="general">

別解

別解として、スクリプトを使って動的にヘルプボタンを見えるようにする方式もある。

<prefwindow onload="document.documentElement.getButton('help').hidden = false;">

余談

設定ダイアログに一切のボタンを表示させたくない場合、 xul:prefwindow 要素の buttons 属性にカンマ一文字を指定する。

<prefwindow buttons=",">

ヘルプボタンクリック時の動作

次に、ヘルプボタンをクリックして、特定のURLをブラウザで開くようにする。
ヘルプボタンクリック時の動作は xul:prefwindow 要素の ondialoghelp 属性に指定する。

<prefwindow ondialoghelp="openHelpURI();">

URLをブラウザで開く際、設定ダイアログがモーダル(instantApply が false)の場合は新しいウィンドウで、モードレス(instantApply が true)の場合は新しいタブで開きたい。そこで、 chrome://browser/content/utilityOverlay.js を読み込んでおくと色々面倒なことを解決してくれる openUILinkIn というユーティリティ関数が使えるようになる。ただし Firefox のアドオン限定。

<script type="application/x-javascript" src="chrome://browser/content/utilityOverlay.js" />
<script type="application/x-javascript"><![CDATA[
    function openHelpURI() {
        var where = document.documentElement.instantApply ? "tab" : "window";
        openUILinkIn("http://www.example.com/", where);
    }
]]></script>

TOP

ウィンドウがアクティブ(最前面に表示されている)かどうかの判定方法

あるウィンドウが Firefox 以外のアプリケーションのウィンドウも含めた中で最前面に表示されているかどうかを判定する方法。

Firefox 3.6 の場合

最前面に表示されたウィンドウにはXULドキュメントのルート要素に active 属性(値は “true”)が自動的に付加される (参考:nsGlobalWindow.cpp)。

var isActive = document.documentElement.hasAttribute("active");

ちなみに、最初これを教わったときに最前面にあるブラウザウィンドウを DOM Inspector で一生懸命調べたものの active 属性なんてものは無かったが、その場合最前面のウィンドウは DOM Inspector になるので、無くて当然である。

Firefox 4.0 の場合

なぜか active 属性を付加する仕組みが削除されたようなので、代替としてCSSの -moz-window-inactive 擬似クラスを利用する。
例えばブラウザウィンドウ (browser.xul) であれば、以下のようにして調べることが可能。

var isActive = !document.querySelector("#main-window:-moz-window-inactive");

他の方法として、window に対する focus, blur イベントを監視して自分でフラグを上げ下げする方法もある。

TOP

persist 属性による属性値の永続化

属性の永続化とは?

XUL要素に persist 属性によって値を保存したい属性の名前を指定しておくと、その属性の値が次回XULドキュメントをロードした時に復元される。保存された属性の値は Firefox 終了時にローカルファイル(プロファイルフォルダ下の localstore.rdf)へ書き出されるので、 Firefox を終了しても保持される。

XULアプリで何らかの設定値を保存するには、 Preferences の仕組みを使って保存する方法が有名だが、UIの見た目に関する設定値(ウィンドウの位置や大きさ、ツールバーのボタンの並び順など)は属性の永続化によって保存される場合が多い。

具体的な例

Firefoxのウィンドウの位置や大きさは、前回開いたときの状態が復元される。これは属性の永続化によって実現されている。 browser.xul のソースを見ると、以下のように persist 属性によって保存したい属性のリストが指定されている。

browser.xul
<window id="main-window"
        ...
        persist="screenX screenY width height sizemode">

localstore.rdf には以下のように {XULドキュメントのURL}#{要素のid} をキーとしてして保存対象の各属性の値が保存される。

localstore.rdf
  <RDF:Description RDF:about="chrome://browser/content/browser.xul#main-window"
                   screenX="115"
                   screenY="87"
                   width="823"
                   height="645"
                   sizemode="normal" />

基本的な使い方

例として、以下のように persist 属性にて value 属性を永続化するようにした xul:label 要素を作成する。idの無い要素は属性の永続化ができないことに注意。

test.xul
<label id="testLabel" value="Test" persist="value" />

次に、以下のようなボタンを作って、 value 属性の値を動的に変更してみる。

test.xul
<button label="Write value"
        oncommand="document.getElementById('testLabel').setAttribute('value', 'xxx');" />

いったん Firefox を終了し、 localstore.rdf を開いてみると、以下のように変更後の foo 属性の値が保存されていることがわかる。

localstore.rdf
  <RDF:Description RDF:about="chrome://.../content/test.xul#testLabel"
                   value="xxx" />

引き続き Firefox を起動して test.xul を開いてみると、ラベルの文言が変更後の xxx になっていることがわかる。

次に、以下のようなボタンを追加し、ボタンをクリックして value 属性の値を削除してみる。

test.xul
<button label="Delete value"
        oncommand="document.getElementById('testLabel').removeAttribute('value');" />

Firefox を終了して localstore.rdf を見ると、 value 属性の値を保存していた部分が削除されていることがわかる。また、 Firefox を起動して test.xul を開いてみると、ラベルの文言が初期値の Test になっていることがわかる。

persist 属性の落とし穴

persist 属性には永続化したい属性の名前(複数ある場合はスペース区切りで連結)を指定するが、注意すべき点として、 persist 属性に指定した属性の名前に部分一致する属性までもが永続化の対象となることが挙げられる。

先ほどの persist="value" とした xul:label 要素の例で、以下のようなボタンを追加する。

test.xul
<button label="Write val"
        oncommand="document.getElementById('testLabel').setAttribute('val', 'xxx');" />

ボタンをクリックして val 属性の値に xxx をセットし、 Firefox を終了して localstore.rdf を見ると、永続化の対象外と思われた val 属性の値までもがちゃっかり保存さていることがわかる。

localstore.rdf
  <RDF:Description RDF:about="chrome://.../content/test.xul#testLabel"
                   val="xxx" />

これはFirefox 本体側のバグと思われる。
Bug 231333 – Unexpected persist when the persisted attribute name contains another

document.persist

document.persist メソッドによって、 persist 属性を使わずに属性の永続化をすることも可能である。

test.xul
<label id="testLabel" value="Test" />
<button label="Write value"
        oncommand="document.getElementById('testLabel').setAttribute('value', 'xxx');
                   document.persist('testLabel', 'value');" />

persist 属性には前述のように部分一致した属性も永続化される問題があるため、こちらの方法を使ったほうが無難かもしれない。

TOP

xul:textbox のプレースホルダー文字列

modest に投稿した記事と同内容です。

注意: この記事は Firefox 3.7 での新機能について触れています。

HTML のテキストボックス

Firefox 3.7a5pre では HTML 5 の仕様である placeholder 属性 が実装されており、以下のようにしてテキストボックスにヒント用文字列を表示させることができます。

<input type="text" placeholder="Your Name">

XUL のテキストボックス

一方、 XUL の textbox 要素では Firefox 3.0 にてすでに同様の機能が emptytext 属性として実装済みですが、 Firefox 3.7a5pre では placeholder 属性でもヒント用文字列を表示することができます。互換性維持のため emptytext 属性も引き続き利用可能ですので、 Firefox 3.6 と 3.7 両対応の拡張機能などでは emptytext 属性を使用したほうが良いでしょう。

<textbox emptytext="Your Name" />
<textbox placeholder="Your Name" />

また、以下のように xul:textbox 要素の emptyText (大文字小文字に注意)および placeholder プロパティを使って JavaScript で動的にヒント用文字列をセットすることも可能です。

document.getElementById(...).emptyText = "Your Name";
document.getElementById(...).placeholder = "Your Name";

TOP

一定時間ドラッグオーバーし続けたら処理を実行する

modest に投稿した記事と同内容です。

拡張機能(XULアプリ)にて、一定時間ドラッグオーバーし続けたときに何らかの処理を実行したい、例えばツールバーに配置したボタン上にブラウザタブを3秒間ドラッグオーバーし続けたら、そのボタンをクリックしたものとみなして処理を実行したいとします。

これは、HTML5のドラッグ&ドロップAPIを使い、ドラッグオーバーし続けた際に dragover イベントが繰り返し発生する特性を利用すると、以下のように実装可能です。

以下は、ボタン上に何かを3秒間ドラッグオーバーし続けると、テキストボックスに現在時刻を表示するサンプルです。なお、サンプルコード全量はこちらに置いてあります。 chrome 権限は不要ですので、ダウンロードして拡張子を.xulにしてFirefoxで開けば、動作確認可能です。

XUL:

<button label="Drag something over here for 3 seconds."
        ondragenter="MyExtension.handleDragEvent(event);"
        ondragover="MyExtension.handleDragEvent(event);"
        oncommand="this.nextSibling.value += new Date() + '
';" />
<textbox multiline="true" flex="1" />

JavaScript:

var MyExtension = {

    _dragStartTime: null,

    handleDragEvent: function(event) {
        event.preventDefault();
        switch (event.type) {
            case "dragenter": 
                // ドラッグオーバー開始時、ドラッグオーバー開始時刻をセット
                this._dragStartTime = Date.now();
                break;
            case "dragover": 
                // ドラッグオーバー中、ドラッグオーバー開始時刻からの経過時間を調べる
                if (this._dragStartTime && Date.now() - this._dragStartTime > 3000) {
                    // 3秒以上経過したら、ドラッグ開始時刻をリセットし、処理を実行する
                    this._dragStartTime = null;
                    event.target.doCommand();
                }
                break;
        }
    }

};

タイマーを用いた実装方式

ドラッグオーバー開始時(dragenter イベント発生時)に setTimeout で一定時間後に処理を実行するためのタイマーを設定し、ドラッグオーバー終了時(dragleave イベント発生時)に clearTimeout でタイマーを解除する、という実装方式ももちろん可能です。

XUL:

<button id="myButton"
        label="Drag something over here for 3 seconds."
        ondragenter="MyExtension.handleDragEvent(event);"
        ondragleave="MyExtension.handleDragEvent(event);"
        oncommand="this.nextSibling.value += new Date() + '
';" />
<textbox multiline="true" flex="1" />

JavaScript:

var MyExtension = {
    _dragOverTimer: null,
    handleDragEvent: function(event) {
        event.preventDefault();
        switch (event.type) {
            case "dragenter": 
                // dragenterイベントが二回連続で発生した場合への対策
                if (this._dragOverTimer)
                    return;
                // ドラッグオーバー開始時にタイマーを設定
                this._dragOverTimer = setTimeout(function() {
                    document.getElementById("myButton").doCommand();
                }, 3000);
                break;
            case "dragleave": 
                // ドラッグオーバー終了時にタイマーを解除
                clearTimeout(this._dragOverTimer);
                this._dragOverTimer = null;
                break;
        }
    }
};

TOP

Firefox 3.7 でのナビゲーションツールバーのアイコン画像サイズ

modest に投稿した記事と同内容です。

注意:このトピックは Firefox 3.7 での仕様変更について触れています。また、 Windows 版のデフォルトテーマを前提としており、他のOSについては未確認です。他のOSについての情報求みます。

Firefox 3.7 では、ナビゲーションツールバーに配置するボタン(戻る・進む・更新・ホームなど)のアイコン画像サイズが下表のように変わるようです。なお、下表の「小さいアイコン」とは、「ツールバーのカスタマイズ」で「小さいアイコンを使用」オプションを有効にしている場合、あるいはブックマークツールバー上にボタンを配置した場合のアイコンを意味します。

Firefox 3.6 Firefox 3.7
通常アイコン 24×24ピクセル 18×18ピクセル
小さいアイコン 16×16ピクセル 18×18ピクセル

拡張機能にてナビゲーションツールバーにボタンを追加している場合、この仕様変更の影響を受けるようです。ここでは、例として、拡張機能にて下記のような XUL オーバーレイによってナビゲーションツールバーにボタンを追加するとします。

<toolbarpalette id="BrowserToolbarPalette">
  <toolbarbutton id="myaddon-button"
                   class="toolbarbutton-1 chromeclass-toolbar-additional"
                   label="My Addon" />
</toolbarpalette>

Firefox 3.6 までは、一般的には以下のようなスタイルシートによって通常アイコンと小さいアイコンのスタイルを別々に定義します(参考)。なお、「largeicon.png」は24×24ピクセルの画像、「smallicon.png」は16×16ピクセルの画像とします。

/* 通常アイコン */
#myaddon-button {
    list-style-image: url("chrome://myaddon/skin/largeicon.png");
}

/* 小さいアイコン */
toolbar[iconsize="small"] #myaddon-button {
    list-style-image: url("chrome://myaddon/skin/smallicon.png");
}

このとき、 Firefox 3.6 では通常アイコン・小さいアイコンともに本来の画像サイズできれいに表示されますが、 Firefox 3.7 では通常アイコンは「largeicon.png」を本来のサイズである24×24ピクセルから18×18ピクセルへと縮小され、小さいアイコンは「smallicon.png」を本来のサイズである16×16ピクセルを18×18ピクセルへ拡大されます。下表のように、小さいアイコンの表示がきれいでなくなる傾向があります。

Firefox 3.6 Firefox 3.7
通常アイコン
小さいアイコン

Firefox 3.7 でも本来の画像サイズでアイコンをきれいに表示したい場合、いくつかの方法があるかと思いますが、ここでは Firefox 3.7 以上専用のスタイルシートを別途追加する方式を解説します。

Firefox が特定のバージョンの場合に限り、指定した XUL に対してスタイルシートを適用したい場合、以下のようにクロムマニフェストの「style」命令へ「appversion」フラグをセットします。なお、ツールバーボタン用のスタイルシートは、「browser.xul」(ブラウザウィンドウ)と「customizeToolbar.xul」(ツールバーのカスタマイズウィンドウ)の両方に適用させます。

# apply stylesheet if Firefox 3.7a or later
style  chrome://browser/content/browser.xul  chrome://myaddon/skin/fx37.css  appversion>=3.7a
style  chrome://global/content/customizeToolbar.xul  chrome://myaddon/skin/fx37.css  appversion>=3.7a

拡張機能の skin パッケージに含めるFirefox 3.7 以上専用のスタイルシート「fx37.css」 には以下のような内容を記述します。 xul:toolbarbutton 要素自体に画像を設定するのではなく、内部の匿名 xul:image 要素に対して画像およびサイズを設定します。

/* 通常アイコン・小さいアイコン共通 */
#myaddon-button > .toolbarbutton-icon {
    list-style-image: url("chrome://myaddon/skin/smallicon.png");
    width: 16px;
    height: 16px;
}

これにより、以下のように通常アイコン・小さいアイコンともに「smallicon.png」が本来の16×16ピクセルできれいに表示されます。

Firefox 3.7
通常アイコン
小さいアイコン

Firefox 標準のツールバーボタンと同じ18×18ピクセルの画像を Firefox 3.7 以降用のアイコン画像として別途作成し、上記「fx37.css」にて適用するのもよいかもしれません。

TOP

タブ切り替えパネル風の半透明ポップアップ

modest に投稿した記事と同内容です。

Tab Flick 拡張機能 のポップアップは、 Firefox 3.6 以降の Ctrl+Tab によるタブ切り替えパネルのような半透明の見た目となっています。

ここでは、そこに至るまでの実装の経緯を記しました。なお、簡単のためコードの一部は実際とは異なるものとなっています。

第1段階

まず、 browser.xul にオーバーレイし、 #mainPopupSet をマージポイントとして新しい xul:panel 要素を追加します。

<popupset id="mainPopupSet">
    <panel id="tabFlickPanel" style="width: 200px; height: 200px;" />
</popupset>

openPopup あるいは openPopupAtScreen メソッドでこの xul:panel 要素を開くと、当然見た目はシンプルなポップアップとなります。

第2段階

次に、 xul:panel 要素に対して KUI-panel クラスを指定します。 KUI-panel クラスは browser.xul にて読み込まれているスタイルシート (browser.css) にて定義されています。したがって、 browser.xul へオーバーレイした XUL 内であれば、特にスタイルを定義することなく利用可能となります。

    <panel id="tabFlickPanel" class="KUI-panel" style="width: 200px; height: 200px;" />

これで Windows XP などでの Ctrl+Tab によるタブ切り替えパネルと同じ、黒い半透明の角丸ポップアップとなります。
KUI-panel クラスのスタイルは Firefox 3.5 に同梱されたスタイルシート (browser.css) でも定義済みですので、 Firefox 3.5 でも有効となります。

第3段階

さらに、 xul:panel 要素へ以下のような内容のスタイルシートを適用します。

#tabFlickPanel:-moz-system-metric(windows-compositor) {
    background: transparent;
    -moz-appearance: -moz-win-glass;
    -moz-border-radius: 0;
    border: none;
}

すると、 Windows Vista または Windows 7 で Windows Aero が有効な場合、Aero Glass 効果のある半透明のポップアップとなります。

第4段階

しかし、ここでひとつ問題が生じます。詳しい理由はわかりませんが、ポップアップの右下角に余計な枠線が表示されてしまいます。
これを解決するには、なぜか xul:panel 要素の collapsed を以下のようにして切り替えてあげる必要があります。

    <panel id="tabFlickPanel" class="KUI-panel" style="width: 200px; height: 200px;"
          collapsed="true"
          onpopupshown="this.collapsed = false;"
          onpopuphiding="this.collapsed = true;" />

これでようやく解決しました。

この方法は裏技的なものですので、拡張機能などでご利用の際はご注意ください。

TOP

ツリーのブックマーク風アイコン表示

xul:treecell 要素へ properties=”Name” という属性を追加する、あるいはカスタムツリービューの基本的な使い方(その9~階層構造 – 表示)にあるように nsITreeView::getCellProperties でツリーセルのプロパティへ “Name” という nsIAtom を追加することで、そのツリーセルにブックマーク風のアイコン(フォルダやページ)が表示されるようになる。

というのは Firefox 3.0 までの話で、 Firefox 3.1 以上では Bug 464916 – Remove non-global rules from global tree stylesheet の影響で “Name” プロパティによるツリーのアイコン表示は不可となった。 Firefox 3.1 以上では、「chrome://browser/skin/places/places.css」を読み込ませ、 “Name” の代わりに “title” というプロパティを追加することでブックマーク風アイコン表示が可能となる。

TOP

nsITreeBoxObject::clearStyleAndImageCaches

ツリーのスタイル付け – MDC の方法によってツリーアイテムのアイコン画像を一時的にアニメーションPNGやアニメーションGIFにすると、アイコンを元に戻した後もCPU使用率が上昇したままとなる問題があった。

アイコンを元に戻した後に nsITreeBoxObject::clearStyleAndImageCaches を呼び出すことで、ツリーのアイコン画像のキャッシュが消去され、この問題が解消された。

nsITreeBoxObject.idl

TOP