DashboardはMacOS X 10.4(Tiger)にて導入された機能です。
Dashboard用のソフトウェアはWidget(ウィジェットと)呼ばれ、比較的簡単に作成できそうです。
○ 目次
関連エントリにおける最終版(0.3.3.wdgt.zip)
現時点の最新版(PiyoTrackbackPing.wdgt.zip)
○ 参考文書
TOC
参考文書は、アップルより色々と提供されています。
- Developing Dashboard Widgets
- Dashboard Programming Guide
- Dashboard Reference
- Debugging Dashboard Widgets
このエントリから始まる一連のエントリは簡単なWidgetを作成する模様を記述してみます。
最終的に作るものはTrackBack Pingを実行する以下のようなWidgetです。

front画面

back画面
作成手順としては、最初から完成形を想定して作成せず、ベースとなるものを作成し徐々に機能を追加する形をとります。
今回のエントリでは、通常のfront画面から、「i」ボタンによりback画面へと切り替わる所までを作成します。
○ Widgetの構成
TOC
Widgetの最小構成は次の4つのファイルです。
- htmlファイル
- 背景用のpngファイル
- アイコン用のpngファイル
- プロパティファイル
ただし、参考文書1によると、アイコン用ファイルは必須ではないかもしれません。
これらだけだと見た目も機能も弱いので、基本的には
などが利用されることになります。
さらに凝ったものにするにはCocoa Bundleによるplug-inの作成が必要になります。なお、今回は私が作れないのでplug-inは作成しません。(:-P
1.0. ベースのhtmlファイルを作成する
|
TOC|
1.1→
とりあえず、背景画像上に
PiyoTrackBack
という文字を表示することを考えます。
PiyoTrackBack.htmlという名前で以下の内容で作成します。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>PiyoTrackBack</title>
<style type="text/css">@import "PiyoTrackBack.css";</style>
</head>
<body>
<img src="Default.png" />
<span class="piyoTrackBackText">PiyoTrackBack</span>
</body>
</html>
このhtmlファイルでは、
ということをしています。
この時点で、htmlファイルをSafariで開くと
のように画像が用意されていないため、「?」が表示されます。
1.1. 背景のPNGファイルを作成する
←1.0|
TOC|
1.2→
適当な画像処理ソフトを利用して作成してください。参考文書2によるとDrop Shadowを利用する場合は
50% opacity
90 degree angle from horizontal
4 pixel offset (distance from source)
10 pixel size, using Gaussian blur
が"standard"だそうです。
ファイル名は"Default.png"が"standard"みたいです。
今回は、
このようなものを用意しました。
この時点で、htmlファイルをSafariで開くと
のように表示されます。文字の位置がDefault.pngの隣に表示されていることがわかります。
1.2. cssファイルを作成する
←1.1|
TOC|
1.3→
背景画像の上に文字列"PiyoTrackBack"を表示するために、PiyoTrackBack.cssというファイル名で以下のようなcssファイルを作成します。
@charset "shift_jis";
.piyoTrackBackText {
font-family: "ヒラギノ角ゴ Pro W3", Osaka, "MS ゴシック", sans-serif;
font-size: 26px;
font-weight: bold;
color: white;
position: absolute;
top: 90px;
left: 85px;
}
#font-familyに日本語のフォントを指定してもSafariだとうまく表示されないようです。FireFoxだとうまく表示されますが‥。将来直ったときのために一応日本語フォントの指定も追加しています。
この時点で、htmlファイルをSafariで開くと
のように表示されます。文字の位置や大きさが変更されたことがわかります。
1.3. 設定画面用にhtmlを変更する
←1.2|
TOC|
1.4→
設定画面が表示できるようにhtmlを変更します。
div要素としてfront画面(class="front")とback画面(class="back")を用意します。front画面からback画面への切り替えは"flip"のクリック、back画面からfront画面への切り替えは"doneButton"のクリックとします。
PiyoTrackBack.htmlを以下のように変更します。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>PiyoTrackBack</title>
<style type="text/css">@import "PiyoTrackBack.css";</style> <script type="text/javascript" src="PiyoTrackBack.js" />
</head>
<body>
<div id="front">
<img class="backgroundImage" src="Default.png" />
<span class="piyoTrackBackText">PiyoTrackBack</span>
<div class="flip" onclick="showPrefs()"></div>
</div>
<div id="back">
<img class="backgroundImage" src="Default_reverse.png" />
<div class="doneButton" onclick="hidePrefs()">Done</div>
</div>
</body>
</html>
このhtmlファイルでは、以前のものと比較して
- JavaScriptファイルの指定:
<script type="text/javascript" src="PiyoTrackBack.js" />
- frontとbackのdiv要素の追加:
<div id="front">
...
</div>
<div id="back">
...
</div>
- 設定画面用の背景ファイルの指定:
<img class="backgroundImage" src="Default_reverse.png" />
- flipとdoneButtonの追加:
<div class="flip" onclick="showPrefs()"></div>
...
<div class="doneButton" onclick="hidePrefs()">Done</div>
ということをしています。
この時点で、htmlファイルをSafariで開くと
のように表示されます。設定要画面が画像がないのとcssファイルが対応していないために、うまく表示されていないことがわかります。
1.4. 設定画面用の背景のPNGファイルを作成する
←1.3|
TOC|
1.5→
適当な画像処理ソフトを利用して作成してください。
参考文書2によると設定画面用の背景は、通常画面と区別するため、暗めにする等の変化をつけた方がよい、とのことです。
ファイル名は"Default_reverse.png"が"standard"みたいです。
今回は、多少変化をつけた、
このようなものを用意しました。
この時点で、htmlファイルをSafariで開くと
のように表示されます。cssファイルが対応していないため、文字の位置等が適切でないことがわかります。また、実際にはfrontとbackのdiv要素は同時に表示されてはいけないため、初期表示時にはback要素を表示しないようにする必要があります。
1.5. プロパティファイルを作成する
←1.4|
TOC|
1.6→
これ以降、Safari上での確認が難しくなるため、cssファイルの修正の前に、Widgetとしての体裁を整えます。Widgetとして最低限必要なファイルでまだ作っていないものは、Info.plistです。
Info.plistはWidgetに関する情報を記載します。具体的には以下のようになります。
| Property | 今回の値 | 意味 |
| CFBundleIdentifier | com.piyosystems.widget.piyotrackback | 必須:ユニークな識別子。逆ドメインのフォーマットを用いる。 |
| CFBundleName | PiyoTrackBack | 必須:Widgetの名前。 |
| CFBundleDisplayName | PiyoTrackBack | 必須:表示される名前。 |
| CFBundleVersion | 1.0 | Widgetのバージョン。 |
| CloseBoxInsetX | 20 | オプション:Widgetを閉じるときに使用する"X"ボタンの置く位置(x)。 |
| CloseBoxInsetY | 15 | オプション:Widgetを閉じるときに使用する"X"ボタンの置く位置(y)。 |
| Height | (nothing) | オプション:Widgetの高さ。 |
| MainHTML | PiyoTrackBack.html | 必須:Widgetを実現しているメインのhtmlファイル名。 |
| Width | (nothing) | オプション:Widgetの幅。 |
1.6. cssファイルを変更する
←1.5|
TOC|
1.7→
初期表示時にback要素を表示しないことと、flipとdoneButtonを適切に表示するために、PiyoTrackBack.cssを以下のように変更します。
@charset "shift_jis";
#back {
display: none;
}
.piyoTrackBackText {
font-family: "ヒラギノ角ゴ Pro W3", Osaka, "MS ゴシック", sans-serif;
font-size: 26px;
font-weight: bold;
color: white;
position: absolute;
top: 90px;
left: 75px;
}
.backgroundImage {
position: absolute;
top: 0px;
left: 0px;
}
.flip {
position:absolute;
bottom: 25px;
right: 25px;
width:13px;
height:13px;
opacity:1;
background:
url(file:///System/Library/WidgetResources/ibutton/white_i.png)
no-repeat top left;
}
.doneButton {
font-family: "ヒラギノ角ゴ Pro W3", Osaka, "MS ゴシック", sans-serif;
font-size: 12px;
font-weight: bold;
color: white;
text-align: center;
line-height: 42px;
position: absolute;
bottom: 25px;
right: 15px;
width: 105px;
height: 42px;
background: url("button.png") no-repeat top left;
}
以前のものと比較して
- backのdiv要素を表示しない:
#back {
display: none;
}
- backgroundImageのdiv要素の表示位置を左上とする:
.backgroundImage {
position: absolute;
top: 0px;
left: 0px;
}
- flipのdiv要素の表示位置を右下からすこし離れた位置とし、画像ファイルを指定する:
.flip {
position:absolute;
bottom: 25px;
right: 25px;
width:13px;
height:13px;
opacity:1;
background:
url(file:///System/Library/WidgetResources/ibutton/white_i.png)
no-repeat top left;
}opacityは0から1の範囲で値をとる透明度です。0で完全に透明になります。
- doneButtonのdiv要素は、フォント関係を設定し、右下に配置し、ボタン用の画像を背景とする:
.doneButton {
font-family: "ヒラギノ角ゴ Pro W3", Osaka, "MS ゴシック", sans-serif;
font-size: 12px;
font-weight: bold;
color: white;
text-align: center;
line-height: 42px;
position: absolute;
bottom: 25px;
right: 15px;
width: 105px;
height: 42px;
background: url("button.png") no-repeat top left;
}今回は、ボタン用画像として、

(button.png)を用意しました。
Widget化の方法
各種ファイルの集まりをWidget化するのは簡単です。
1つのフォルダにファイルを含めた状態で、集めたフォルダ名に拡張子wdgtをつけるだけです。
現時点であれば、PiyoTrackBackというフォルダに、作成したすべてのファイル(button.png、Default_reverse.png、Default.png、Info.plist、PiyoTrackBack.css、PiyoTrackBack.html)をいれて、念のためフォルダごと複製したもの作成し、その複製のフォルダ名をPiyoTrackBack.wdgtにするだけです。
ちゃんとできていれば、ダブルクリックで起動します。
この時点で作成したWidgetを起動すると
のように表示されます。1画面で表示され、右下に設定画面へ遷移するための「i」が表示されています。ただし、JavaScriptファイルが存在しないため、「i」を押しても画面は切り替わりません。
1.7. JavaScriptファイルを作成する
本節の修正後のファイル|
←1.6|
TOC|
1.8→
この時点のhtmlファイルにて、利用しようとしている
を定義するため、PiyoTrackBack.jsという名前で以下の内容で作成します。
function showPrefs() {
var front = document.getElementById("front");
var back = document.getElementById("back");
if (window.widget) {
// freezes the widget so that you can change it
// without the user noticing
widget.prepareForTransition("ToBack");
}
// hide the front, show the back
front.style.display="none";
back.style.display="block";
if (window.widget) {
// and flip the widget over
setTimeout ('widget.performTransition();', 0);
}
}
function hidePrefs() {
var front = document.getElementById("front");
var back = document.getElementById("back");
if (window.widget) {
// freezes the widget and prepares it
// for the flip back to the front
widget.prepareForTransition("ToFront");
}
// hide the back, show the front
back.style.display="none";
front.style.display="block";
if (window.widget) {
// and flip the widget back to the front
setTimeout ('widget.performTransition();', 0);
}
}
この時点で作成したWidgetを起動し、右下の「i」を押すと、くるっと回るトランジションの後
のように表示されます。ほぼ今回のエントリの目的は達成できましたが、「i」が常に表示されていて、通常のWidgetの動作(マウスが該当Widget内に入ったときに「i」ボタンが浮かび上がる)と異なります。
1.8. 正しい「i」ボタンの動作のためにhtmlファイルを変更する
本節の修正後のファイル|
←1.7|
TOC|
1.9→
正しい「i」ボタンの動作のためにhtmlファイルを変更します。
具体的には、frontとflipのdiv要素に対してマウスに関するイベントハンドラを設定することと、「i」ボタンを明確にするためにfliprollieのdiv要素を追加します。
PiyoTrackBack.htmlを以下のように変更します。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>PiyoTrackBack</title>
<style type="text/css">@import "PiyoTrackBack.css";</style>
<script type="text/javascript" src="PiyoTrackBack.js" />
</head>
<body>
<div id="front"
onmousemove="mousemove(event)"
onmouseout="mouseexit(event)">
<img class="backgroundImage" src="Default.png" />
<span class="piyoTrackBackText">PiyoTrackBack</span>
<div class="flip" id="fliprollie"></div>
<div class="flip"
id="flip"
onclick="showPrefs()"
onmouseover="enterflip(event)"
onmouseout="exitflip(event)"></div>
</div>
<div id="back">
<img class="backgroundImage" src="Default_reverse.png" />
<div class="doneButton" onclick="hidePrefs()">Done</div>
</div>
</body>
</html>
この時点で作成したWidgetを起動しても
のように表示され、「i」ボタン部分には特に変化はありません。
1.9. 正しい「i」ボタンの動作のためにcssファイルを変更する
本節の修正後のファイル|
←1.8|
TOC|
1.10→
正しい「i」ボタンの動作のためにcssファイルを変更します。
具体的には、flip classの共通部分を抜き出し、frontとflipにおいて個別部分を設定します。
PiyoTrackBack.cssを以下のように変更します。
@charset "shift_jis";
#back {
display: none;
}
.piyoTrackBackText {
font-family: "ヒラギノ角ゴ Pro W3", Osaka, "MS ゴシック", sans-serif;
font-size: 26px;
font-weight: bold;
color: white;
position: absolute;
top: 90px;
left: 75px;
}
.backgroundImage {
position: absolute;
top: 0px;
left: 0px;
}
.flip {
position:absolute;
bottom: 25px;
right: 25px;
width:13px;
height:13px;
}
#flip {
opacity:0;
background:
url(file:///System/Library/WidgetResources/ibutton/white_i.png)
no-repeat top left;
z-index: 8000;
}
#fliprollie {
display:none;
opacity:0.25;
background:
url(file:///System/Library/WidgetResources/ibutton/white_rollie.png)
no-repeat top left;
z-index:7999;
}
.doneButton {
font-family: "ヒラギノ角ゴ Pro W3", Osaka, "MS ゴシック", sans-serif;
font-size: 12px;
font-weight: bold;
color: white;
text-align: center;
line-height: 42px;
position: absolute;
bottom: 25px;
right: 15px;
width: 105px;
height: 42px;
background: url("button.png") no-repeat top left;
}
表示位置とサイズは、flipとfliprollieで共通なのでclass flipとして定義
flipの個別は、
- opacityを最初0として非表示
- z-indexをfliprollieよりも上の8000にする
の2点を変更しています。
fliprollieの個別は、
- 非表示
- opacityを0.25とする
- 画像はDashboardにて規定の白色の円
- z-indexをflipよりも下の7999にする
の2点を変更しています。
後述のJavaScriptファイルにてこれらのopacityが変更され、「i」ボタンは動作することになります。
この時点で作成したWidgetを起動すると
のように表示され、「i」ボタン部分はopacityが0のため表示されません。
1.10. 正しい「i」ボタンの動作のためにJavaScriptファイルを変更する
本節の修正後のファイル|
←1.9|
TOC|
2.0→
正しい「i」ボタンの動作のためにJavaScriptファイルを変更します。
具体的には、
- マウスカーソルがWidgett内にあれば、「i」ボタンのopacityを1になるようにする。(=「i」ボタンが浮かび上がる)
- マウスカーソルがWidgetから出たら、「i」ボタンのopacityを0になるようにする。(=「i」ボタンが徐々に消滅する)
- マウスカーソルが「i」ボタン内に来たら、薄い白い丸であるfliprollieを表示する。
- マウスカーソルが「i」ボタンから出たら、薄い白い丸であるfliprollieを非表示にする。
ということを実現します。
PiyoTrackBack.jsを以下のように変更します。
function showPrefs() {
var front = document.getElementById("front");
var back = document.getElementById("back");
if (window.widget) {
// freezes the widget so that you can change it
// without the user noticing
widget.prepareForTransition("ToBack");
}
// hide the front, show the back
front.style.display="none";
back.style.display="block";
if (window.widget) {
// and flip the widget over
setTimeout ('widget.performTransition();', 0);
}
// clean up the front side - hide the circle behind the info button
document.getElementById('fliprollie').style.display = 'none';
}
function hidePrefs() {
var front = document.getElementById("front");
var back = document.getElementById("back");
if (window.widget) {
// freezes the widget and prepares it
// for the flip back to the front
widget.prepareForTransition("ToFront");
}
// hide the back, show the front
back.style.display="none";
front.style.display="block";
if (window.widget) {
// and flip the widget back to the front
setTimeout ('widget.performTransition();', 0);
}
}
// PREFERENCE BUTTON ANIMATION
// (- the pref flipper fade in/out)
// a flag used to signify if the flipper is
// currently shown or not.
var flipShown = false;
// A structure that holds information
// that is needed for the animation to run.
var animation = {duration:0,
starttime:0,
to:1.0,
now:0.0,
from:0.0,
firstElement:null,
timer:null};
// mousemove() is the event handle assigned
// to the onmousemove property on the
// front div of the widget.
// It is triggered whenever a mouse is moved
// within the bounds of your widget. It prepares the
// preference flipper fade and then calls animate()
// to performs the animation.
function mousemove (event) {
// if the preferences flipper is not already showing...
if (!flipShown) {
// reset the animation timer value, in case a value was left behind
if (animation.timer != null) {
clearInterval (animation.timer);
animation.timer = null;
}
// set it back one frame
var starttime = (new Date).getTime() - 13;
// animation time, in ms
animation.duration = 500;
// specify the start time
animation.starttime = starttime;
// specify the element to fade
animation.firstElement = document.getElementById ('flip');
// set the animation function
animation.timer = setInterval ("animate();", 13);
// beginning opacity (not ness. 0)
animation.from = animation.now;
// final opacity
animation.to = 1.0;
// begin animation
animate();
// mark the flipper as animated
flipShown = true;
}
}
// mouseexit() is the opposite of mousemove()
// in that it preps the preferences flipper
// to disappear. It adds the appropriate values
// to the animation data structure and sets
// the animation in motion.
function mouseexit (event) {
if (flipShown) {
// fade in the flip widget
if (animation.timer != null) {
clearInterval (animation.timer);
animation.timer = null;
}
var starttime = (new Date).getTime() - 13;
animation.duration = 500;
animation.starttime = starttime;
animation.firstElement = document.getElementById ('flip');
animation.timer = setInterval ("animate();", 13);
animation.from = animation.now;
animation.to = 0.0;
animate();
flipShown = false;
}
}
// animate() performs the fade animation for
// the preferences flipper. It uses the opacity
// CSS property to simulate a fade.
function animate() {
var T;
var ease;
var time = (new Date).getTime();
T = limit_3(time-animation.starttime, 0, animation.duration);
if (T >= animation.duration) {
clearInterval (animation.timer);
animation.timer = null;
animation.now = animation.to;
} else {
ease = 0.5 - (0.5 * Math.cos(Math.PI * T / animation.duration));
animation.now = computeNextFloat (animation.from, animation.to, ease);
}
animation.firstElement.style.opacity = animation.now;
}
// these functions are utilities used by animate()
function limit_3 (a, b, c) {
return a < b ? b : (a > c ? c : a);
}
function computeNextFloat (from, to, ease) {
return from + (to - from) * ease;
}
// these functions are called when the info button
// itself receives onmouseover and onmouseout events
function enterflip(event) {
document.getElementById('fliprollie').style.display = 'block';
}
function exitflip(event) {
document.getElementById('fliprollie').style.display = 'none';
}
簡単に解説すると以下のようになります。
マウスカーソルがWidgett内にあれば、「i」ボタンのopacityを1になるようにする。(=「i」ボタンが浮かび上がる):
mousemove()により実現されています。opacityを1へ変化させているのはanimate()です。
マウスカーソルがWidgetから出たら、「i」ボタンのopacityを0になるようにする。(=「i」ボタンが徐々に消滅する)
mouseexit()により実現されています。opacityを0へ変化させているのはanimate()です。
マウスカーソルが「i」ボタン内に来たら、薄い白い丸であるfliprollieを表示する。
enterflip()により実現されています。fliprollieを表示します。showPrefs()により、設定画面に移る際にfliprollieは非表示にされます。
マウスカーソルが「i」ボタンから出たら、薄い白い丸であるfliprollieを非表示にする。
exitflip()により実現されています。flipprollieを非表示にします。
この時点で作成したWidgetを起動すると、最初に
のように表示されます。マウスがWidget内にはいると「i」ボタンが浮かび上がるように表示され
のようになります。マウスが「i」ボタン内にはいると
のようになります。これらの挙動は一般的な「i」ボタンのそれと同じです。
本エントリは以上です。
次のエントリでは最低限のtrackback ping機能を実現するところまでです。
TOC|
2.0→