Cocoa Break Logo Top Soft Develop  脱力空間 Logo
Apple Web Badge
made by mi

概要 翻訳 ソース リンク がらくた

概要 Examples ADC Samples 3rd Parties etc CBOriginals

Goodbye World

以下は、Mac OS X 10.4.11 上の Xcode 2.5 で説明しています。BlankWidget で解説したことは繰り返しませんので、そちらを参照してください。Goodbye World は段階的に機能を追加していく形のサンプルです。各段階ごとに説明していきます。

目次:
1 Start
2 With Flip
3 Saving Preferences
4 Multiple Instance Aware
5 まとめ

1 Start

1.1 ウィジェット

「1-Start」フォルダ内のウィジェットをインストールすると以下のようになります。

単に「Hello World」という文字列が表示されているだけです。これは HelloWorld と変わりませんが、枠の形と背景の色が違っています。

1.2 ファイル構成

ファイル構成は以下のようになります。

HelloWorld よりは複雑で CSS や JavaScript ファイルが別になっています。ただし、JavaScript は内容が何もありません。情報プロパティリストでは特別なキーは設定されていません。主要 HTML は GoodbyeWorld.html です。Default.png はデフォルト画像で、文字なしの背景部分だけです。このファイルと同じものが images 内にもあります。Icon.png はウィジェットバーに表示されるアイコンです。Default_reverse.png は、Default.png より暗い画像ですが、この段階ではまだ使用しません。

1.3 GoodbyeWorld.html

それでは、主要 HTML ファイルを見てみます。

GoodbyeWorld.html
<html> <head> <!-- スタイルシートは別のファイルに維持されたほうがいい。これはウィジェットのデザインを収容する --> <style type="text/css"> @import "GoodbyeWorld.css"; </style> <!-- JavaScript ファイルはこのウィジェットに必要な論理を収容する --> <script type='text/javascript' src='GoodbyeWorld.js' charset='utf-8'/> </head> <body> <!-- 前面 div はウィジェットの主要 UI。これは最初に目に見え、環境設定が表示される時隠される --> <div> <img span="backgroundImage" src="Images/Default.png"> <!-- 前面に対する背景 --> <div id="worldText">Hello, World!</div> <!-- デフォルトテキスト --> </div> </body> </html>

スタイルが CSS ファイルに移されている以外は、HelloWorld と同じです

1.4 GoodbyeWorld.css

つぎにスタイルシートを見てみます。

GoodbyeWorld.css
body { margin: 0; } #worldText { font: 20px "Lucida Grande"; font-weight: bold; text-align: center; color: white; position: absolute; top: 47px; left: 20px; width: 180px; } .backgroundImage { position: absolute; top: 0px; left: 0px; }

HTML にあった2つの要素に対してスタイルを設定しているだけです。この段階のサンプルは JavaScript がないので、これで説明は終了です。詳細については、BlankWidgetHelloWorld を見てください。

2 With Flip

2.1 ウィジェット

「2-With Flip」フォルダ内のウィジェットは、「1-Start」とほとんど変わりませんが、マウスカーソルを近づけると情報ボタンが表示される所が違っています。。

このボタンをクリックすると、背面が表示されることになります。まだ実際の設定内容はありません。単に「Done(完了)」ボタンがあるだけです。

2.2 ファイル構成

ファイル構成は以下のようになります。

赤で囲んだ部分が追加されたものです。ただし、「images」内の「Default_reverse.png」は、前の段階では使われておらず、実質的にはこの段階からのものです。情報プロパティリストには、BackwardsCompatibleClassLookup キーが追加されています。これは Apple クラスを使うときに互換性を上げるためのものです。これについて、およびアニメーターの説明は、Stretcher を見てください。また AppleButton.js と AppleInfoButton.js については、Fader を見てください。このサンプルの説明では、詳細については触れません。

2.3 GoodbyeWorld.html

それでは、主要 HTML ファイルを見てみます。

GoodbyeWorld.html
<html> <head> <!-- スタイルシートは別のファイルに維持されたほうがいい。これはウィジェットのデザインを収容する --> <style type="text/css"> @import "GoodbyeWorld.css"; </style> <!-- JavaScript ファイルはこのウィジェットに必要な論理を収容する --> <script type='text/javascript' src='GoodbyeWorld.js' charset='utf-8'/> <!-- 10.4.3 より前の互換性: ウィジェットのトップレベルに AppleClasses ディレクトリを含める --> <script type='text/javascript' src='AppleClasses/AppleButton.js' charset='utf-8'/> <script type='text/javascript' src='AppleClasses/AppleInfoButton.js' charset='utf-8'/> <script type='text/javascript' src='AppleClasses/AppleAnimator.js' charset='utf-8'/> </head> <!-- onload ハンドラはAppleClassesによる情報ボタンとDoneボタンの提供で必要 --> <body onload="setup();"> <!-- 前面divはウィジェットの主要UI。これは最初に目に見え、環境設定が表示される時隠される --> <div id="front"> <img span="backgroundImage" src="Images/Default.png"> <!-- 前面に対する背景 --> <div id="worldText">Hello, World!</div> <!-- デフォルトテキスト --> <!-- 以下のdivは情報ボタンを含む、これはマウスが上にあるときウィジェット右下に提示される onload ハンドラは、アートワークと動作を提供するコンストラクタを含む --> <div id='infoButton'></div> </div> <!-- 背面divは、ウィジェットの環境設定UI。これはスタイルシートで隠されshowPrefs()関数で表示 --> <div id="back"> <img span="backgroundImage" src="Images/Default_reverse.png"> <!-- 背面に対する背景 --> <!-- 以下のdivはDoneボタンを収容する。 onload ハンドラは、アートワークと動作を提供するコンストラクタを含む --> <div id="doneButton"></div> </div> </body>

前の段階と違って、背面 div があります。前面は情報ボタンが追加されている以外は、同じですが、body 要素に読み込み完了時のハンドラが設定されていることに注意してください。これは JavaScript ファイル内で定義されている関数です。

裏面divは、背景と「Done」ボタンだけの簡単な構成です。

2.4 GoodbyeWorld.css

つぎにスタイルシートを見てみます。

GoodbyeWorld.css
... #infoButton { position:absolute; bottom: 27px; right: 50px; } #doneButton { position: absolute; bottom: 20px; left: 82px; } #front { display: block; } #back { display: none; }

情報ボタンとDoneボタンのサイズが指定されています。それ以外の属性等は、Appleクラスで自動的に設定されます。また、最初に前面を表示し、背面を隠すようにも設定しています。

2.5 GoodbyeWorld.js

この段階から JavaScript ファイルが実装されています。

GoodbyeWorld.js > グローバル変数
// 情報とDoneボタンに対するグローバル変数、後で修正するために保持しておく var glassDoneButton; var whiteInfoButton;

単に、次の setup 関数でコンストラクタを使って作成するオブジェクトを保持するためのものです。つぎに、body 要素に読み込み完了時のハンドラとして設定されていた setup を見てみます。

GoodbyeWorld.js > setup
// setup()は、ウィジェットのonloadハンドラ // 既存のdivを各コントロールで満たすAppleInfoButtonとAppleGlassButtonのコンストラクタを呼び出す function setup() { glassDoneButton = new AppleGlassButton(document.getElementById("doneButton"), "Done", hidePrefs); whiteInfoButton = new AppleInfoButton(document.getElementById("infoButton"), document.getElementById("front"), "white", "white", showPrefs); }

単純にボタンコンストラクタを呼び出して、それをグローバル変数に格納しているだけです。Fader で説明しましたが、ガラスボタンのコンストラクタの引数は、順番に、ボタン要素、タイトル、クリックされた場合のハンドラです。情報ボタンのコンストラクタの引数は、順番に、ボタン要素、前面要素、前景色、背景色、裏面表示の際のハンドラです。

つぎに情報ボタンのハンドラとして設定された showPrefs を見てみます。

GoodbyeWorld.js > showPrefs
// showPrefs 関数は環境設定ボタンがクリックされたときに呼ばれる // これはウィジェット前面を凍結し、前面divを隠し、背面divを表示し、ウィジェットを反転する function showPrefs() { var front = document.getElementById("front"); var back = document.getElementById("back"); if (window.widget) widget.prepareForTransition("ToBack"); // ユーザーが気づくことなく変更できるようウィジェット凍結 front.style.display="none"; // 前面を隠す back.style.display="block"; // 背面を表示 if (window.widget) setTimeout ('widget.performTransition();', 0); // ウィジェットを反転させる }

これについては、『Dashboard プログラミングトピック』>「ウィジェットの裏面と環境設定」を見てください。また、Fader でも説明しました。凍結を行わなければ、反転アニメーションの前に、一瞬で表面が消え、裏面が表示されてしまいます。それを防ぎ、反転をアニメーションとして実行できるように、最初に表示を凍結しているわけです。つぎに逆に背面の Done ボタンに設定されていたハンドラを見てみます。

GoodbyeWorld.js > hidePrefs
// hidePrefs() は、ウィジェット背面のDoneボタンによって呼ばれる // これは showPrefs() が行っていたのと逆の遷移を行う function hidePrefs() { var front = document.getElementById("front"); var back = document.getElementById("back"); if (window.widget) widget.prepareForTransition("ToFront"); // ウィジェットを凍結し、背面から前面へ反転させる準備をする back.style.display="none"; // 背面を隠す front.style.display="block"; // 前面を表示 if (window.widget) setTimeout ('widget.performTransition();', 0); // ウィジェットを背面から前面へ反転 }

showPrefs とほぼ同じなので、説明しなくてもわかると思います。このように、ボタンをコンストラクタで作成し、それにハンドラを割り当て、ハンドラで前面と背面の切り替えを行っています。この段階では行っていることはこれだけです。

3 Saving Preferences

3.1 ウィジェット

「3-Saving Preferences」フォルダ内のウィジェットでは、環境設定が実際に有効になっています。

このように環境設定で表示文字列を選ぶと、前面の表示は変更されます。

3.2 ファイル構成

ファイル構成は「2-With Flip」と同じです。情報プロパティリストも特に変わった所はありません。

3.3 GoodbyeWorld.html

それでは、主要 HTML ファイルを見てみます。前と同じ所は省略します。

GoodbyeWorld.html
<html> ... <div id="back"> <img span="backgroundImage" src="Images/Default_reverse.png"> <!-- 背面に対する背景 --> <select id='worldPopup' onchange='changeWorld(this);'> <!-- テキストを選ぶためのポップアップメニュー --> <option value=1>Hello, World!</option> <option value=2>Goodbye, World!</option> </select> ... </div> ... </html>

前の段階と違って、背面 div 内にポップアップメニューがあります。このポップアップメニューに、ハンドラ changeWorld が設定されていることに注意してください。

3.4 GoodbyeWorld.css

つぎにスタイルシートを見てみます。

GoodbyeWorld.css
... #worldPopup { position: absolute; top: 41; left: 40; width: 140; } ...

単にポップアップメニューのスタイルが追加されただけです。

3.5 GoodbyeWorld.js

JavaScript ファイルは、ポップアップメニューの changeWorld ハンドラ関数が追加されたほか、setup 関数も修正されています。これは環境設定の値を起動時に反映させるためです。まず changeWorld を見てみましょう。

GoodbyeWorld.js > changeWorld
// changeWorld()はウィジェット環境設定でメニュー項目が選ばれた時に呼ばれる // これはどのオプションが選ばれたかをメニューに問い合わせ、ウィジェットに変更を適用し、環境設定を保存する function changeWorld(elem) { var world = document.getElementById("worldText"); switch( elem.selectedIndex ) // どのオプションが選ばれたか調べる { case 0: // オプション #1 ("Hello, World!") なら world.innerText="Hello, World!"; // 前面テキストを「Hello, World!」に変更しスタイル調整 if(window.widget) { widget.setPreferenceForKey("Hello, World!","worldString"); // 新しい環境設定をディスクに保存 } break; case 1: ... } }

まずオプションを調べて、その後で文字列を設定し、環境設定を保存しています。この環境設定は、「ユーザーフォルダ > ライブラリ > Preferences」に、widget-com.apple.widget.goodbyeworld.plist という名前で保存されます。2 番目のオプションを選んだ後では以下のようになっています。(プロパティリストエディタで開いてダンプさせたもの。)

widget-com.apple.widget.goodbyeworld.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>worldString</key> <string>Goodbye, World!</string> </dict> </plist>

さて、setup の修正部分を見てみましょう。

GoodbyeWorld.js > setup
function setup() { ... (前と同じくボタンコンストラクタ)... if(window.widget) // ダッシュボードで実行されていることを確認するため常にチェック { // 環境設定を取得する var worldString = widget.preferenceForKey("worldString"); if (worldString && worldString.length > 0) // 取得された環境設定が空でなければ、復元する { document.getElementById("worldText").innerText = worldString; if( worldString == "Goodbye, World!" ) // Goodbye が取得された値なら... { document.getElementById("worldPopup").selectedIndex = 1; //それを反映するようポップアップ修正 } } } }

環境設定で文字列が変更できるようになったため、保存された環境設定がある場合、起動時にそれが設定されるようにしています。

4 Multiple Instance Aware

4.1 ウィジェット

「4-Multiple Instance Aware」フォルダ内のウィジェットでは、複数のウィジェットが配置されている時に環境設定が衝突せず、別々のキーで保存されるように修正されています。以前の段階のウィジェットをインストールし、「Goodbye World」を表示するように設定した後で、ウィジェットバーから2つめのウィジェットを配置します。すると、両方のウィジェットが同じ「Goodbye World」を表示していることがわかります。

今回の段階のウィジェットでは、新しい配置されたほうのインスタンスは、初期設定のままの別の文字列を表示します。

4.2 ファイル構成

ファイル構成は同じです。情報プロパティリストも変わった所はありません。

4.3 GoodbyeWorld.html

主要 HTML ファイルも同じです。

4.4 GoodbyeWorld.css

スタイルシートも同じです。

4.5 GoodbyeWorld.js

JavaScript ファイルでは、個別インスタンスごとのキーを作る makeKey 関数が追加され、それを利用するように修正されているだけです。これによって、個別インスタンスごとの環境設定値の保存が行われています。

GoodbyeWorld.js > makeKey
// makeKey はウィジェットの複数インスタンスを可能にする function makeKey(key) { return (widget.identifier + "-" + key); }

これについては、Framing_Gallery で説明しました。環境設定はウィジェットごとに保存されますが、複数のインスタンスがあっても同じファイル内に格納されます。identifier プロパティは、複数インスタンスがあっても、別々の値が割り当てられるため、それと結びつけることで、一意的なウィジェットキーを作成しています。環境設定ファイルは以下のようになります。。(プロパティリストエディタで開いてダンプさせたもの。)

widget-com.apple.widget.goodbyeworld.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>41aa7182bda428b3-worldString</key> <string>Goodbye, World!</string> <key>41aa718431767fd4-worldString</key> <string>Goodbye, World!</string> </dict> </plist>

たとえば、setup 内の取得部分はつぎのように修正されています。

GoodbyeWorld.js > setup
var worldString = widget.preferenceForKey(makeKey("worldString"));

このようにして、個別インスタンスごとに別々の環境設定値が取得・保存されます。

5 With Widget Events

5.1 ウィジェット

「5-With Widget Events」フォルダ内のウィジェットでは、ウィジェットイベントハンドラが追加されています。キーボードフォーカスと除去時の動作です。ウィジェットをインストールした後で、ダッシュボード内の他のウィジェット上でクリックし、そちらをフォーカスしてみます。すると、フォーカスが外れた、このサンプルは以下のようになります。

4.2 ファイル構成

ファイル構成は同じです。情報プロパティリストも変わった所はありません。

4.3 GoodbyeWorld.html

主要 HTML ファイルも同じです。

4.4 GoodbyeWorld.css

スタイルシートも同じです。

4.5 GoodbyeWorld.js

JavaScript ファイルでは、ウィジェットイベントに対応する部分が追加されています。ウィジェットイベントに対するハンドラの設定は、HTML 内の要素ではなく、ウィジェットそのものに対して設定するため、HTML ではなく、JavaScript ファイル内で、読み込み時に実行される関数外のコードとして記述します。

GoodbyeWorld.js > 関数外コード
// ここでいくつかのウィジェットイベントを登録 if(window.widget) { widget.onremove = removed; window.onfocus = focused; window.onblur = blurred; }

ダッシュボード内で実行されている場合だけ、除去時には removed、フォーカス時には focused、フォーカスが外れた時には blurred が呼ばれるよう設定しています。

GoodbyeWorld.js > removed
// removed は、ウィジェットがダッシュボードから除去された時に呼ばれる function removed() { widget.setPreferenceForKey(null,makeKey("worldString")); }

ここでは環境設定値を除去しています。

GoodbyeWorld.js > focused
// focused はウィジェットがキーフォーカスを得たときに呼ばれる function focused() { document.getElementById('worldText').style.color = 'white'; }

フォーカスを得たときには、文字色を白に戻しています。これは最初に起動された時と同じ色です。

GoodbyeWorld.js > blurred
// blurred は、ウィジェットがキーフォーカスを失った時に呼ばれる function blurred() { document.getElementById('worldText').style.color = 'gray'; }

キーフォーカスが外れたときには、文字色を灰色にしています。これによって、他がクリックされた時に変更されます。

さて、この段階では、JavaScript に対して別の変更もなされています。

GoodbyeWorld.js > blurred
function showPrefs() { ... document.getElementById('fliprollie').style.display = 'none'; // 前面側の後片付け、情報ボタンのまわりの円を隠す }

とはいうものの、前段階と今段階のウィジェットを使って実験してみましたが、特に変わりはありませんでした。互換性キーによりシステムのものが使われている場合、この配慮は問題ないようです。

5 まとめ

このサンプルでは、少しずつ機能を増やす形で、ウィジェット作成を学べるようになっています。すでに他のサンプルで説明したことは省いたので、詳細については、それらを見てください。特に、Stretcher ではアニメーション関連、Fader ではボタン関連の Apple クラスについて、Apple が提供する JavaScript ファイル内のすべての関数を説明していますので、細かい動作が気になる方はそちらを見てください。


管理人:神吉 秀典 E-mail:puer@ba.wakwak.com