スクリプトを滑らかに

同じ機能を実現するスクリプトの書き方は何通りもあるが、利用者へのフィードバックの方法によってその使い勝手には大きな差ができる。XCMDは、こうしたスマートなスクリプトの作成にも威力を発揮する。

フィールドとリスト表示

たとえば、一覧を示してその中から必要なものを選択するというのは、フィールドを使ってHyperTalkだけで処理をしても十分可能だ。しかし、この場合はフィールドを表示するためのスペースをカード上に確保する必要がある。また、最新の選択肢を提供するためには、外部からいったん情報を取得してフィールドに書き込むことになるが、この方法では利用者が選択を行い次のアクションをとる前に、どうしてもスクリプトを中断しなければならない。フィールドの項目を選択するには、ハイパーカードをアイドル状態にする必要があるからだ。こういうとき、ダイアログ形式のリストを表示すると、これらの問題をうまく解決し、スムーズな操作性を提供することができる。

ListSelect

これはダイアログ形式のリストを実現するXCMDである。最も単純な使い方は

put ListSelect(1, "red,green,blue") into RGB

というような形。最初の引数は、リストの選択のモード(複数選択の可否など)、続くアイテムリストが、ダイアログで表示されるリストの内容である。この単純な命令では、画面1のようなリストが表示される。結果の変数RGBには、OKボタンをクリックしたときに選択していたアイテム(画面1ならred)が納められる。これだけでも十分役立つが、例によって非常に豊富なオプションが用意されているので紹介しよう。

ListSelect(<mode>,<list> [,"s=#[,#]"] [,"NoDoubleClick"] [,prompt] [,OK] [,Cancel] [,Loc|Rect] [,sep] [,DLOG ID] [font[,size]])

mode

リストからの選択の方法を指定する。この値は、

"0" or "N[o]" : 選択しない

"1" or "O[ne]" : 一つだけ選択する

"2" or "C[on]" : 連続した複数の項目を選択可

"3" or "D[is]" : 不連続の複数の項目を選択可

となっていて、必ずどれかを指定しなくてはならない。

このmodeの前に*を加えると、リストはソートされた状態で表示される。また、#を後に続けると、選択された結果をその内容ではなく、アイテム番号で返す。

たとえば、先ほどのRGBの例で

put ListSelect("*1#", "red,green,blue") into RGB

とした場合、画面1のリストは"blue,green,red"の順で表示される。ここでリストの1行目を選ぶと、変数RGBにはblueという文字列ではなく、アイテム番号が入る。このアイテム番号は表示されている順ではなく、引数として渡す元のアイテムの順番であることに注意が必要だ。つまり、blueを選んだ結果は表示順の1ではなく、元のリストの順番である3となる。

list

カンマか改行で区切られたリスト

s=#[,#]

デフォルトで選択するアイテムを指定できる。2番目と5番目のアイテムを選択した状態にするなら"s=2,5"とする。省略値は1。すなわち、先頭のアイテムが選択された状態で表示される。

NoDoubleClick

この文字列を引数として渡すと、ダブルクリックがOKボタンの代わりにならない。通常はダブルクリックはOKボタンと同じ。

Prompt, OK, Cancel

それぞれ、ダイアログ上部、OK、Cancelボタンに表示する文字列。任意の文字列を引数として渡すと、Prompt, OK, Cancelの順番でダイアログに表示される。また、emptyを渡すと、そのアイテムを表示しない。

Loc|Rect

ダイアログを表示する位置。"x,y"の形で渡すと左上の座標を指定できる。Rect形式で4つの整数を渡すと、ダイアログがその位置、その大きさで表示される。省略した場合は画面1のようなダイアログが画面の中央に表示される。

Sep

表示するアイテムリストの区切り文字。省略した場合、カンマ(もしくは改行)を区切り文字とみなす。

DLOG ID

自分でダイアログ用のリソースを作成して、そこにリストを表示させることができる。たとえばオリジナルのアイコンを使ったリストなどが使えるわけだ(画面2)。省略した場合はリソースを使わずにXCMDがダイアログを作成する。

Font[,Size]

ダイアログで使用するフォント。"Osaka,12"のように、引用符でくくって一つの引数にする。

リスト1は通信ログからメールの一覧を表示し、必要なものだけをピックアップするという簡単なサンプルだ。1996/7号のHTML変換スクリプトの変形なので詳しい説明は省くが、10〜14行目が若干トリッキーなので、変数の関係を整理しておこう(図1)。numには選択された(TitleListの)行番号がreturnで区切られたリストになって入っている。これに対応するtposの行はログファイル中に「題名」が含まれる位置を示しているから、この該当する行-1、つまり題名の1行前には日付などのメールの先頭がある(11行目のpos1)。同様にして、pos2には"action:DELete"の1行前が納められるので、13行目はi番目のメールの内容を取得する命令となるわけだ。

分かってしまえば単純なことだが、リストを扱うプログラムではこのようにどの変数が何を示すのかをきちんと把握することが大切である。ここさえ押さえておけば、ListSelectの使い方は難しくない。

FullHPop

ハイパーカードもバージョン2.2以降はボタンのプロパティを使ってポップアップメニューを実現できるようになったが、このFullHPopを使うと階層型メニューや、アイコンを加えたメニューなどの高度な表現ができる。ボタンのon mouseDownハンドラに

get FullHPop(<menu list>)

という1行を埋め込むだけで使える手軽さも魅力だ。

FullHPop(<menu>[,<loc>] [,<selected item>] [,<font[,size]>] [,<checkmark>] [,<sub owner select>] [,"fastest"])

<menu>

表示するメニューのリストである。単純なメニューなら、アイテムの区切りはreturn, comma, semicolonのどれでもよい。階層表示を行う場合は、親アイテムの区切りをreturnもしくはsemicolon、サブメニューの区切りをcommaにする。

このmenuが4つの英文字である場合(FONTなど)、これはリソースの名前と解釈されて、現在利用可能なりソースの一覧が表示される。

さらに、特殊なメタ文字をアイテムの前に置くことで多彩なメニュー表現が可能:

^nnn :IDがnnnであるアイコンを加える

!X :Xという文字をマークとしてアイテムの前に加える

<X :XにB, I, O, U, Sを使うと、アイテムの文字列のスタイルがそれぞれボールド、イタリック、アウトライン、下線付き、シャドウになって表示される

/X :アイテムに対してXをキーボードショートカットとして割り当てる

( :このアイテムをグレイにして選択できなくする

ャnnn :IDがnnnであるSICNをアイテムの前に加える

アイテム区切り文字やこれらのメタ文字をメニューの内容として使う場合には、その文字の前に\を置いてエスケープする必要がある。

<loc>

メニューの左上を"h,v"の形式で指定する。省略すると、ボタン/フィールドの場合は左上の座標、それ以外ならマウスの位置に表示される。

<selected item>

ポップアップ時に自動選択するアイテムの番号。正の値ならチェックマーク付きで、負の値ならマークなしで選択する。さらに、値を省略したとき、ボタンの名前/フィールドの内容と同じアイテムがメニューにあれば、そのアイテムを自動選択する。

<font[,size]>

メニューのフォント。省略するとボタンやフィールドと同じフォントを使う。

<checkmark>

チェックマークとして使う文字(半角1文字)

<sub owner select>

TRUE/FALSEで、サブメニューを持つアイテム自身を選択できるかどうか設定する。省略値はTRUE。

"fastest"

この文字列を引数として渡すと、SICNなどが使えなくなるが、高速にメニューを表示する。

これらの引数はどんな順番で与えても構わない。選択の結果は、2行にわたって次のような情報を返す:

<item name>,<subitem name>,<item number>,<subitem number>

<item width>,<subitem width>

サブメニューを与えていない場合はsubItem関連のところはemptyとなる。

オプションが豊富すぎて混乱するかもしれないが、具体例を示すと画面3-1のような具合だ。一つのカードに多様な要素を埋め込んで、なおかつ画面をすっきりデザインするには大いに役立つだろう(*注1)。

Promptoid

これは小さなメッセージウインドウを表示するXCMD。長い作業を行うスクリプトを書く場合、

put <現在の作業の内容> into msg

のような命令を要所に加えてユーザーに進行状況をフィードバックするが、これをアイコンなども使ってもう少しスマートに行うことができる。基本的な使い方は、

Promptoid <message>

となる。

Promptoid [<message>[<icon name or id>[,<vloc>[,<width>]]]

<message>

表示するメッセージ

<icon name or id>

表示するアイコンの名前/ID

<vloc>

カードに対する相対的な(垂直の)位置。省略するとカードの中央に表示される。

<width>

ウインドウの横幅。省略すると、メッセージの長さに自動的に合わせられる。

表示されたウインドウは"Promptoid"という名前を持つ。また、このメッセージウインドウはほかにもいくつかプロパティを持っており、それをスクリプトから取得したり設定したりできる。利用可能なプロパティは:

icon, loc, visible, text, vLoc, width

の6つ。textは表示しているメッセージを指す。たとえば

	set text of window "Promptoid" to "New message"

とすれば、メッセージの内容を"New message"に変更することができる。

このtextプロパティの設定を使って、スクリプトの実行中にいろいろな途中経過のメッセージを送ることができるわけだ。さらにiconプロパティを変えていくと、アニメーションのような効果を出すこともできる。リスト2でハイパーカードの標準的なアイコンを使った簡単な遊びをつくってみた。21573というのは画面4のようなお手玉をするアイコンのID。21573〜21576の4つのアイコンがお手玉を回すアニメーションになっているので、それを順番にウインドウに設定して行くわけだ。

SmallLauncherの作成

ListSelectのようなダイアログリストの利点は、必要なときだけリストを表示するため、スペースをとらず、また随時リストの内容を変更できるというところである。一般にローンチャは外部からファイルの登録や変更ができないから、共有ファイルを扱うのには向いていない。追加や変更のたびに全員のローンチャを修正して回るのは大変だからだ。

そこで、共有するファイルは外部の定義ファイルに登録しておき、利用するときはそこから最新情報を得てリスト表示するローンチャを考える。こうしておけば、管理者は定義ファイルを変更するだけでよい。このスタイルならコンパクトなスタックに収まるというのも便利な点だ(画面5)。

Launcher

定義ファイルには、共有文書1件につき1行で

説明,ファイルパス[,アプリケーション名]

という情報を列挙しておく。リスト3はこれを使ってファイルを選択・オープンするスクリプトだ。定義ファイルの1列目を抽出してListSelectで表示し、選ばれた行番号を使って、6行目で実際のファイル名を、7行目で指定アプリケーションを調べる。アプリケーションの指定があれば8行目でそれに従ってファイルを開くが、ファイル名のみの場合は、9行目でリナルディ氏のXCMD、LaunchDoc(*注2)を使う。

ほかにもサンプルスタックにはPromptoidによるちょっとした仕掛けを施している。誌面のゆとりがないので、説明はCD-ROMのREADMEファイルを見ていただきたい。

今回取り上げたようなXCMDは、なければないで何とかなる類のものである。しかし、ユーザーにとってはこうした細かい部分での使い勝手が、システムを積極的に利用するかどうかの分かれ道になることが多い。これらのツールを効果的に活用し、ユーザーにとって使いやすいスタックの設計を心がけたいものだ。

*注1

ただ、残念なのは、一部の日本語が正しく表示されない点である(画面3-2)。見かけを我慢して、返される値のitem nameではなくitem numberを使ってオリジナルのリストから正しい値を取得すればなんとか実用にはなるが、見苦しいことは確かだ。同様な階層型ポップアップメニューを提供するXFCNとして、John Pugh氏のHPopUpがある。こちらは日本語も正しく表示できる。

*注2

これはファインダでアイコンをダブルクリックするのと同じく、作成アプリケーションを調べて自動的にファイルを開いてくれる強力なXCMDである。

リスト

リスト1

NIFTYのオートパイロットログからメールを選択する

 1: function MailReader str
 2:   put "    題名:" into mTop
 3:   put "action:DELete" into mEnd
 4:   put ExtractItems(FullFind(str,mTop,FALSE,TRUE),2) into tpos
 5:   put ExtractItems(FullFind(str,mEnd,FALSE,TRUE),2) into epos
 6:   repeat with i=1 to number of lines of tpos
 7:     put FullReplace(line (line i of tpos) of str,mTop,"") & return after TitleList
 8:   end repeat
 9:   put ListSelect("3#",TitleList) into num
10:   repeat with i=1 to number of lines of num
11:     put (line (line i of num) of tpos) - 1 into pos1
12:     put (line (line i of num) of epos) - 1 into pos2
13:     put line pos1 to pos2 of str & return after res
14:   end repeat
15:   return res
16: end MailReader

リスト2

簡単なアニメーションを行うスクリプト

 1: on mouseUp
 2:   put 21573 into baseIcon
 3:   put 1 into x
 4:   Promptoid "I'm playing juggler ...", baseIcon
 5:   repeat while the mouse is Up
 6:     set icon of window "Promptoid" to baseIcon + (x MOD 4)
 7:     add 1 to x
 8:   end repeat
 9:   close window "Promptoid"
10: end mouseUp

リスト3

共有するファイル情報を<定義ファイル>に保存し、ListSelectで選ぶ

 1: on ShareDocuments
 2:   put <定義ファイル> into fSDoc
 3:   put ReadFile(fSDoc) into xList
 4:   put ExtractItems(xList,1) into myList
 5:   put ListSelect("1#",myList,"ファイルを選んでください") into pos
 6:   put item 2 of line pos of xList into theFile
 7:   put item 3 of line pos of xList into app
 8:   if app is NOT "" then open theFile with app
 9:   else LaunchDoc theFile -- Rinaldi's XCMD
10: end ShareDocuments

(MacUser Japan, September 1996)