スタック便利ツール

ここまではデータ処理のためのかなり実用的なXCMDを紹介してきたので、少し趣を変えて、スタック作成に便利なツールを取り上げてみよう。

オブジェクトをドラッグするFullDrag

ハイパーカードのボタンは最初にデザインした位置に固定しておくのが普通である。しかし、時にはこのボタンをマウスでドラッグして、利用者に好みの位置を決めてもらったり、スライドバーのようにして例えばサウンドの音量をコントロールできるようにしたいと考えることはないだろうか?

一般にこういう時に用いられるトリックは、

    on mouseStillDown
      set loc of me to the mouseLoc
    end mouseStillDown

というスクリプトをボタンに組み込むような仕掛けである。少し動きはぎこちないが、これでもボタンをドラッグする雰囲気は出る。このドラッグを、もっと高度に利用できるようにしたXFCNがFullDragである。これを使うと、ボタンがいきなり移動するのではなく、ファインダ上でファイルをドラッグするのと同様に点線で囲まれた枠組みだけが動く。マウスを放した時点でボタン移動させたり、あるいはそのままにしておいたりという選択も可能だ。

FullDrag (<rect(s)>[,<limit rect>[,<test>,<if true>[,<if false>]]])

<rect(s)>
ドラッグに合わせて動くオブジェクトの座標。複数指定すると、一度にいくつものオブジェクトをドラッグできる。
<limit rect>
ドラッグできる範囲の座標。
<test>
例えば"the mouseLoc is within rect of btn 1"のような、TRUE/FALSEを返すHypertalkの文。ドラッグしている間、常に評価されている。
<if true>, <if false>
<test>を評価した結果によって実行するHypertalkの命令文。

マウスボタンを放したとき、その位置が<limit rect>内であれば、元の位置からの相対座標が返される。<limit rect>の外の場合は空白。

これは具体例を示した方が分かりやすい。例えば画面1のような、デスクトップを模したスタックを作ったとする。ここでファイルのアイコンをドラッグすると、ゴミ箱に重なれば黒く反転し、そこでマウスボタンを放すとゴミ箱が膨らんでファイルが消える(見えなくなる)という振る舞いが期待されるだろう。これを実現するには、ファイルのアイコンを持つボタンにリスト1のスクリプトを埋め込んでおけばよい。

3行目のFullDragはずいぶん長い命令だが、用法で説明したとおりである。第3引数でマウスの位置をチェックし、もしマウスがゴミ箱の領域に入ったら(within the rect of btn trash)、第4引数でゴミ箱のアイコンをハイライト、そうでなければ第5引数に従ってアイコンは通常の状態にされる。XFCNの返値(it)として得られる座標はボタンを放したときの相対的な移動を示すから、4〜5行目で実際の位置を計算し、ゴミ箱の上であることが分かったらゴミ箱を膨らませ(7行目)、ファイルアイコンのあるボタンを隠す(8行目)。

こうしたドラッグを用いるときは、アイコンのハイライトなどを利用して、常に今の状態をフィードバックすることが大切だ。

バルーンヘルプ機能を加えるFullBaloons

漢字Talk 7のバルーンヘルプはそれほど使いやすいものではないかもしれないが、うまく利用すれば、スタックの簡単な説明には十分役に立つ。FullBaloonsは標準のバルーンヘルプをHyperTalkから手軽に利用する機能を提供する。

FullBalloons <action>[,<balloon content>[,<tiploc>] [,<hotrect>] [,<tipposition>]]

最初の<action>のみが必須。<balloon content>は<action>が"showノ"の場合に必要。その他は順序も含めて自由である。

<action>
XCMDの動作を指定する文字列で、次のものがある:
on :バルーンヘルプを有効にする
off :バルーンヘルプを無効にする。
isOn :バルーンヘルプが有効かどうか調べる
isShowing :バルーンヘルプが表示されているか調べる
showXXX :XXXの内容をヘルプに表示する。XXXがDirectの場合は指定の文字列(あるいはコンテナ)、PICT, STR, STR#,TEXTの場合は各リソース、Fieldの場合は指定フィールドの内容。
remove :表示中のバルーンを取り除く
<balloon content>
<action>が"showXXX"のとき、文字列、コンテナ、リソースのIDを指定する。showFieldの場合、"cd fld 1"のように指定するとスタイルも含めて表示できる。
<tiploc>
バルーンの吹き出しの先端。省略するとマウスの位置になる。
<hotrect>
マウスがこのエリア内にあるときバルーンを表示する。省略値は該当のオブジェクトのrect
<tipposition>
バルーンの吹き出しの向き。leftTop,topLeft,rightTop,topRight,bottomRight,rightBottom,bottomLeft,leftBottomのように指定する。省略値はleftTop。

このXCMDは通常、ボタンやフィールドのon mouseEnterハンドラにおいて使う。たとえば

    on mouseEnter
      FullBalloons "showDirect","This is a help"
    end mouseEnter

という具合だ。メニューバーで「バルーンを表示」にしておくと、マウスがそのボタン(フィールド)にさしかかったときに"This is a help"というバルーンヘルプが表示される。showPICTやshowTEXTを使えば画面2のような、表現力豊かなヘルプも可能だ。

バルーンヘルプは利用者がメニューバーでOn/Offを切り替えなければならず、使い方の分からない人にとってはあまり親切なものではない。このXCMDを用いればスクリプトでOn/Offを制御できるので、最初の1回だけ強制的にバルーンヘルプを表示するというような工夫が可能だ。たとえばカードに"balloon"というようなフィールドを用意し、そこにこれまでバルーンを表示したかどうかの記録を残すようにしておく。初めてカードを開く場合には、リスト2のような仕掛けで、メニューバーを使わなくてもヘルプが表示されるようにしておくというわけだ。

またリスト3のトリックを用いて、マイクロソフト社の製品のように、ボタンの上にしばらくマウスを置いておくと説明を表示するような時間差ヘルプも考えられる。mouseEnterではグローバル変数gHelpSetを1秒後にセットし(3行目)、mouseWithinで現在時刻とgHelpSetを比較(7行目)。1秒経過していたら、バルーンをオンにしてヘルプを表示するのである(*注1)。マウスがボタンから遠ざかったら(mouseLeave)バルーンを解除しておこう。こうすることで、どうでもいいようなメッセージばかり表示されるバルーンヘルプの煩わしさからも解放される。バルーンヘルプも使いようによっては親切な情報提供ツールになるのではないだろうか。

リソースを扱うXCMD

リソースを追加することで、プログラムを作りなおさなくても機能を拡張できるのがマッキントッシュの特徴である。このシリーズで紹介しているXCMDもリソースの一つだ。これらを利用するためには、まずResEditなどのツールでスタックにコピーしなければならない。これを簡単に、スクリプトの中からでも実行できるようにするのがCopyResだ。

CopyRes <src file>,<dest file>,<res type>,<res name or id>[,<i | r | k>] [,<duplicate name allowed>]

<src file>、<dest file>
リソースをコピーする元になるファイルとコピー先ファイル。フルパスでなく名前のみだと、スタックと同じフォルダにあるとみなされる。名前の代わりに"*"を与えると、現在のスタックのパス名が代入される。
<res type>
XCMDなどリソースのタイプを示す4文字の英数字。
<res name or id>
対象となるリソースの名前もしくはID
<i | r | k>
いずれかの文字で始まる任意の単語を指定すると、コピー先のリソースIDを自動的に新しい番号に変更する。I (incremental)は、存在する一番大きいIDに1を加えた番号を与える。R (random)なら競合しない1000以上の任意の番号。K (krunch)を指定すると、コピー先に同じ名前やIDのリソースがあっても上書きする。
<duplicate name allowed>
TRUEを指定すると、コピー先に同じ名前のリソースがあっても警告なしに上書きする。省略値はFALSE。

KやTRUEによる上書きの指定がないときに同じ名前やIDをみつけると、ダイアログでIDを変更するかどうか確認する。

リソースをResEditでコピーするのは単純作業ながら面倒なものだから、このようなツールを使ってできるだけ自動化したい。とくに、数多くのスタックをアップグレードする場合、スクリプトで必要なリソースをコピーしたり更新したりするようにしておけば、管理者の手間は大きく省けるだろう。CopyResがあればスタックを自動インストーラとして利用できるようになる。

必要なリソースを対話的に(ユーザーが選んで)コピーするなら、FullResListを使ってリソースの一覧を取得し、ListSelectなどのダイアログで選択できるようにすればよい。

FullResList([<t=all|type1[...,typen]>] [,<n=res name>] [,<i=res id>] [,<w=all|filename>] [,<o=a|t|n|s|i|w[...a|t|n|s|i|w]>])

<t=...>
リストアップするリソースのタイプを複数指定できる。省略するとALL。
<n=...><i=...>
名前やIDも指定できる。複数のリソースを同じ名前やIDでセットにして使っているものを調べるときに便利(*注2)。
<w=...>
検索するファイル名。ALLを指定すると、HomeやSystemを含めた、現在利用可能な全リソース(*注3)を対象にする。省略すると現在のスタックを検索する。
<o=...>
アウトプットのフォーマット。A (attributes), T (type), N (name), S (size), I (ID),W (where)が指定できる。省略値は"O=TNI"。AttributesはC (Changed), R (pReload), P (Protected), L (Locked), U (pUrgeable), S (System heap)を組み合わせた文字として示される。

かなり細かい指定が可能だが、普通に使うにはタイプ(と対象ファイル)さえ与えれば十分だろう。現在のスタック中のXCMDを調べるなら

FullResList("T=XCMD")

とするだけだ。

スタックにあるXCMDとXFCNを一覧表示し、選んだものをコピーするためのスクリプトをリスト4に示した。2行目でXCMDとXFCNの一覧を取得し、3行目でダイアログ表示する。ListSelectの最初の引数が"*3"となっているのは、一覧をソートして表示し、複数選択を可能にするため(*注4)。7〜9行目で、選択したリソースの数だけコピーを行う。CopyResの3、4番目引数がこのようなitem指定になるのは、2行目のFullResListでフォーマット指定の省略値"O=TNI"を暗黙のうちに使っているからだ。

さらに、不要なリソースを削除するにはKillResを使う。

KillRes <res type|all>,<res name or id|all>[,<file name>]

タイプ、名前にAllを指定すると、該当する全てのリソースを消去する。<file name>を省略すると現在のスタックのリソースが対象となる。

HyperResCopy画面

今回とり上げたツールを組み合わせて、画面3のようなリソースコピー用のスタックを作ってみた。指定したファイルのリソースをフィールドに表示し、そのフィールドから必要なリソースを右のアイコンにドラッグすると、コピーや削除を行うというものだ。ボタンには時間差ヘルプも設定してある。無理に機能を組み合わせた感もあるが、ドラッグやヘルプを実装する参考になれば幸いである。

*注1

ハイパーカードの反応時間の問題で、ぴったり1秒後に表示できるとは限らない。環境に応じて、タイミングは調整の必要がある。

*注2

たとえば、たいていのダイアログはDITLとDLOGという2種類のリソースを同じIDでセットにして使う。

*注3

ハイパーカードでは、リソースは現在のスタック自身の他、ライブラリ指定したスタック、Homeスタック、Systemという具合に階層をさかのぼって検索される。従ってXCMDはライブラリやHomeにあるものも利用できるし、アイコンやフォントはさらにSystemのものも使えるわけだ。

*注4

通常、リソースのリストはファイルに納められている順番で返されるので、ソートしておかないと分かりづらい。ListSelectについては96/9号を参照

リスト

リスト1

-- ファイルアイコンをゴミ箱にドラッグする。id 131のアイコンは膨らんだゴミ箱。
 1: on mouseDown
 2:   put the mouseLoc into pos
 3:   get fullDrag(rect of me, "0,0,512,342",
      "the mouseLoc is within the rect of btn trash",
      "set hilite of btn trash to true",
      "set hilite of btn trash to false")
 4:   add item 1 of it to item 1 of pos
 5:   add item 2 of it to item 2 of pos
 6:   if pos is within the rect of btn "trash" then
 7:     set icon of btn trash to 131
 8:     hide me
 9:   end if
10:   set hilite of btn trash to false
11: end mouseDown

リスト2

on openCard
  if cd fld "balloon" is "" then
    put "初めて使う方のためにバルーンヘルプを表示します..."
    FullBalloons "On"
    put "done" into cd fld "balloon"
  end if
  pass openCard
end openCard

リスト3

 1: on mouseEnter
 2:   global gHelpSet
 3:   put the seconds + 1 into gHelpSet
 4: end mouseEnter

 5: on mouseWithin
 6:   global gHelpSet
 7:   if gHelpSet < the seconds then
 8:     FullBalloons "isShowing"
 9:     if the result is FALSE then
10:       FullBalloons "on"
11:       FullBalloons "showdirect","This is a help"
12:     end if
13:   end if
14: end mouseWithin

15: on mouseLeave
16:   FullBalloons "off"
17: end mouseLeave

リスト4

 1: on mouseUp
 2:   get FullResList("T=XCMD,XFCN")
 3:   put ListSelect("*3",it) into myList
 4:   if myList is "" then exit mouseUp  
 5:   answer file "コピー先のスタック" of type "STAK"
 6:   if it is "" then exit mouseUp
 7:   repeat with i=1 to number of lines of myList
 8:     CopyRes "*",it,item 1 of line i of myList, item 2 of line i of myList
 9:   end repeat
10: end mouseUp

(MacUser Japan, November 1996)