ウィンドウ
【以下【】の中は訳者が付け足したものです。】
grDemoのソースリストに戻りましょう。 dPaneと三つのIndicatorの境界値と位置揃え値を設定した後、dViewを宣言します。これはわたしたちのウィンドウのcontViewになります。別段、特別な性質は必要ありませんから、単なるViewでかまいません。
続いて五つの値が生成されます。初めの一対はMacスクリーン上での、このプログラムが占めるウィンドウの左上角の点の座標です。座標40,60になります。次の二つはこのプログラムウインドウの右下角の座標です。これらの値は、小さいMacスクリーン(例えば、Mac Classicのような)に合うものとなっていますが、どんなMacのスクリーン上でもちゃんと見えるはずです。これらの特徴は、後でこのプログラムのためのウィンドウを生成するときに想起されることになります。
次に、特別な種類のウィンドウを定義するクラスに移りましょう。:その中にコントロールを持ち、図像が描画されるであろうウィンドウです。
Macintoshツールボックスは六つの既定義ウィンドウをもっており、それぞれが一意的なウィンドウ定義IDと結びつけられています。以下は六つのウィンドウとその名前、及びIDのイラストです。
あとで混乱しないようにするため、第一のウィンドウはID=8であることに注意してください。というのは、ズームボックス–上部右側の角にある小さな長方形–を持っているからです。MopsのWindow+クラスは次の行でズームボックスを加えています。:
true setZoom: self \ zoomable
ウィンドウが生成されるとき、Window+は、ズームがtrueに設定された場合には、procIDに8を加算します。そのためdocWind(0)はZoomdocWind(8)になり、拡大(grow)不能のdocWind(ID=4)は、ZoomNoGrow(12)になります。しかし、すべてのウィンドウがズームボックスを持てるわけではありません。ですから、例えばrndWindを利用する場合にはズームをTRUEにセットしないでください。
新しいウィンドウを定義する場合はいつも、数値によって一つのウィンドウタイプを選んでください。68k Mopsは三つの定数 — docWind, dlgWind, rndWind — を持っています。数値よりも名前の方が覚えやすいというときには、これを数値の代わりに用いることができます。Mops定数名は上の図に見ることができます。このMopsデモグラフィックプログラムは、docWindを用いています。というのは、Appleのヒューマンインターフェイスガイドライン【リンク先はOSX用です】では、スタンダードな拡大可能ウィンドウにはこれを用いることが推奨されているからです。
Carbonウィンドウマネージャーにおいては、Macintoshウィンドウは、WindowClassとWindowAttributesの二つのパラメターで決定されます。PowerMopsにおけるWindowクラスのインスタンスは、デフォルトではCarbon ドキュメントウィンドウクラスに属します。そのウィンドウオブジェクトの
パブリックインスタンス変数であるWindowClassの値を、NEW:メッセージを送る前に設定することによって、デフォルト設定を変更することができます。例えば、フローティングウィンドウが必要ならば、次のようにすることができます。:
konst kFloatingWindowClass put: ivar> WindowClass in aWindowObj
...
... new: aWindowObj
パブリックインスタンス変数に付いては、第二部レファレンスを参照してください。
ウィンドウ属性(attributes)には、クローズボックス(ボタン)、ズーム可能性、リサイズ可能性などが含まれます。これらの属性は"ウィンドウ定義ID"としてnew:メッセージと共に渡されることになります。現状では、PowerMopsには二つの定数、docWindおよびNoCloseDocWindが定義されています。
このMopsデモグラフィックプログラムは"docWind"を用いています。アップルのヒューマンインターフェイスガイドラインが、標準的な拡大可能なウィンドウにはこれを用いることを推奨しているからです。しかし、PowerMopsでのdocWind引数が意味しているのは、そのウィンドウが、標準的なドキュメントウィンドウに適切な通常のウィンドウ属性をすべて持つということであることに注意してください。つまり、ここではdocWindはドキュメントウィンドウクラスを意味しているわけではありません。ウィンドウクラスとウィンドウ属性の詳細については、カーボンウィンドウマネージャードキュメンテーションを参照いてください。
簡単なウィンドウでも、Machintoshツールボックスの中では比較的複雑なオブジェクトです。この複雑さを理解するには、既定義クラスであるWindowの長いインスタンス変数のリストをみるとよいでしょう。(Module sourcesフォルダーにあるWindowMod.txt(68k Mops)あるいはzWindowmod.txt(PowerMops)にあります)。 ウィンドウ内であなたがコントーロールできる—そしてときどきそうしなければならない—アイテムとしては:
- ウィンドウの種類(上記参照)
- スクリーン上のそのウィンドウを囲む長方形領域
- ウィンドウがグロウ可能かどうか
- スクリーン内でウィンドウがグロウできる範囲内の領域
- そのウィンドウがドラッグできるかどうか
- スクリーン内でウィンドウがドラッグできる範囲内の領域
- キーを押したり、マウスでクリックしたりするイベントに対してどのように反応するのか
GrDemoウィンドウ
grWindクラスは、Window+クラスのサブクラスですが、それはこのグラフィックデモンストレーションのウィンドウのためのフレームワークを設置するものです。前のレッスンで見たように、Window+クラスのオブジェクトはすべて、ビューであるcontViewを一つ持っており、これがウィンドウの全領域を覆っています。ウィンドウはどのビューがそのContViewであるかを知っています。というのは、NEW:メッセージがそのウィンドウオブジェクトに送られて始動されるときに、それは特定されているからです。わたしたちの例では、わたしたちのgrWindウィンドウのcontViewはdViewと呼ばれています。
ウィンドウにはスクリーン上にそれが現れるために十分な引数を与えなければなりませんが、それはこのプログラムではNEW:メソッドで単純化されています。このメソッドは、Window+クラスのNEW:メソッドの拡張となっています。ここで必要な引数は、そのウィンドウのタイトル文字列のアドレスと長さ、そしてcontViewのアドレスだけです。ウィンドウの長方形境界領域、ウィンドウのタイプ(rndWindなど)【PowerMopsでは属性】、直ちにスクリーン上に見えるようにするのか、クローズボックスをもつようにするか【PowerMopsでは無効】、を決めるフラグ、といったその他の引数は、そのメソッド内で供給されるか、既に定義された定数として提供されます。すべてのアイテムがスタック上に適切な順序で揃ったなら、このメソッドは、スーパークラスのNEW:メソッドを呼び出します。
Window+クラスのNEW:メソッドによって遂行されるアクションの一つには、そのcontViewにNEW:を送ることがあります。その結果、そのウィンドウ内のすべてのビューに対してもNEW:を送ることになります。というのは、ViewクラスのNEW:のアクションには、そのすべての子ビューにNEW:を送ることが含まれているからです。
ウィンドウとそのすべてのビューが始動したなら、各ビューは自分自身で描画を行います。前に見たように、ビューは、DRAW:メッセージを受けたときに自分自身を描画します。では、このプロセスを開始するには何をすれば良いのでしょうか?前のレッスンで述べたことをご記憶かもしれませんが、これまでは、あなたは推測できるに過ぎませんでした。その答えは:contViewにDRAW:を送らなければならないということです!
Mac上では、ウィンドウ内の描画は通常はウィンドウがアップデートされたときに起ります。grDemoウィンドウをスクリーンの下端までドラッグして行って、一部分がスクリーンの外にはみ出すようにしてください。それからマウスボタンを放してください。そして、また、ウィンドウをスクリーンの中央までドラッグしてみてください。ウィンドウ内のすべてが再び見えるようにするためには、アップデートが必要です。Macシステムはそのウィンドウがアップデートされるべきことを認識し、そのウィンドウを所有しているアプリケーションにアップデートイベントを送ります。今の段階では細かいことを気にする必要はありませんが、Mopsはこのアップデートイベントを捉えて、そのウィンドウに DRAW:メッセージを送ります。
このようにしてcontViewはDRAW:メッセージを受け取り、それがビュー全体の描画を引き起すのです。Window+クラスのDRAW:メソッドは、基本的にはそのcontViewにDRAW:を送るだけです。簡単ですね!
dWind
grDemoのプログラムの続きですが、grWindクラスの定義のすぐ後に、わたしたちのウィンドウであるdWindが定義されています。これは、たった今定義したgrWindクラスのオブジェクトです。
新しい定義である@DPARMSは、ビューdPane内の図像の描画を準備します。この定義は、続く四つの定義の各々のために必要な作業を一つのワードでできるようにするショートカットになっています。
最初にすべきことは、それ以前にビュー領域にあるものを何であれすべて消去すること、で、それから、このビュー周りの境界線として長方形を描かなければなりません。ここで、図像描画はdPaneに送られるDRAW:メッセージの結果として行われること、そして、この時点では長方形tempRectはそのビューの境界線に設定されていることに留意してください。これは、作業を易しくしてくれます。— 単に、Clear: tempRect draw: tempRectと書けば良いのです。
@DPARMSのその他の仕事は、各コントロールの現在の指示値を取り出すことです。
引き続き定義されている四つのワードは見慣れたものだと思います。これらの定義はTurtleで定義されたspiral, spin, lj, dragon曲線の拡張です。しかし、ここでは、引数は三つ取り、これらの数値を描画デバイス(場合に応じて、PenまたはPolyですが)に設置し、それにしたがって図像を描画するように変形されています。
これらの定義には、 : と ; ではなくて、:a と ;a が用いられていることにお気づきでしょう。これを用いたのは、これらのワードがアクションハンドラーだからです — それらのアドレスは後でメニューオブジェクトに格納され、ユーザーがメニューを選択したときに起るアクションを定義しています。後のセクションで、モジュールを議論するときに、モジュール内のアクションハンドラーは :a と ;a で宣言される必要があることを見るでしょう。これは厳密には、今のわたしたちの定義があるメイン辞書については必要ではありません。しかし、害はありません。ですから、アクションハンドラーにはいつも :a と ;a を使うのというのは、良い考えであると言えます。そうすれば、あなたのプログラムは、より明瞭になり、後でコードをモジュール内に移すようなときには、作業が簡単にもなるでしょう。
【この:aと;aはPowerMopsでは普通のコロンと同義です。ですから、68k Mopsで同じコードを用いるという予定がなければ、普通のコロンとセミコロンでもかまいません。もっとも、何がアクションハンドラーかをはっきりさせるメリットはあります。】
各描画のタイプによってパラメターの範囲が変わってくるので、!rangeの定義では各コントロールのための最大値を設定することになります。これは、メニューからどのタイプの図像を選ぶかに依存します。最小値はいつも1です。(コントロールについては少し後でもっと詳しく論じます。)
コードの次の三行では、'About Curves'メニューアイテムの選択に反応してスクリーン上に現れるメッセージテキストが、二つの文字列定数AB1,AB2に割り当てられています。それに続く三行では、その選択が行われたときに起るべきことが定義されています。 そこではTimesフォントの14ポイントが選ばれ(Timesのフォント番号は20です)、カーソルの位置が28,40に置かれ、二つの文字列がスクリーン上に'Type'されています。それから、ワードWaitClickが呼ばれます。これはユーザーがマウスをクリックするか、キーを押すかするまで待ちます。それから、UPDATE:メッセージがdWindに送られ、そのことによって、わたしたちが'Type'した文字列は消去されて、再描画が行われます。
次に、ペンBicとポリゴンAnnaが、ともに、描画領域長方形の中心点の位置を告げられます。重要なのは、与えられる座標が、ビューdPaneの左上角から相対的なものであるということです。というのは、dPaneがDRAW:メッセージを受けるときは、いつも原点はその角になるからです。
コントロール
Macintosh環境では、コントロールは、スクリーンオブジェクトであって、マウスからの働きかけに反応して、その結果直ちにアクションを引き起したり、あるいはその後の操作の機能を変更したりするものです。「直ちにアクション」を起こす種類のコントロールの良い例は、サウンドコントロールパネルの音量コントロールにある上下つまみです。マウスでつまみを調節することで、Macによってスピーカーから出される音の音量が直ちに調節されます。
これと似たように、ダイアローグボックスの'OK'ボタンをクリックするときには、直ちにアクションが起るコントロールを操作していることになります。'事後アクション'コントロールとは、ゴミ箱にドラッグするときのための書類のロックやアンロックを設定するGet Infoダイアローグの中のチェックボックスのようなものをいいます。空っぽのチェックボックスにマウスポインタを合わせてクリックすると、ボックスがチェックされ、その書類はロックされますが、これに反応して特別なアクションは起りません。もう一度クリックすると、チェックは消え、それ以降はその書類をゴミ箱に捨てることができます。
Mopsでは、コントロールはビューのサブクラスです。ですから、それらは、前のレッスンで見たように、位置揃え(justification)と境界(bounds)の数値を通じて大きさと位置決めが行われ、DRAW:メッセージが送られたときに描画されます。しかし、これと並んで、コントロールはMacシステムとたくさんの相互作用を行っています。
スクロールバーは最も広く用いられるものの一つです。それは五つの部分から成っていて、それぞれがプログラムの中で異なった反応を持っています。その五つの部分とは:
- アップアロー
- ベージアップ領域
- つまみ
- ページダウン領域
- ダウンアロー
です。各領域は、必要に応じて反応するようにプログラムされています。
Macintoshツールボックスが予め定めている多くのオブジェクトと同様、コントロールは、コントロール定義ID (control definition ID :CDEF)と呼ばれる専用の特定番号を持っています。これが、そのコントロールはどのような機能を持ち、どのような概観をしているのかをMacに知らせるのです。四つの標準的なコントロールのタイプとその定義IDは次のようになります:
標準ボタン |
= |
0 |
チェックボックス |
= |
1 |
ラヂオボタン |
= |
2 |
スクロールバー |
= |
16 |
Carbon ノート:Carbonにおいては、コントロール定義IDを用いてコントロール要素を生成することは非推奨となっています。ですから、Carbon PowerMopsではコントロール定義IDを用いていません。さらには、コントロール定義ID(CDEF)は、Carbonにおける構造体データであるコントロールIDとは別物であることにも注意してください。
すべてのコントロールは、マウスとの相互作用に基づくアクションを特定する必要があります。スクロールバーは5つの区分された部分を持ち、それぞれの部分について別々のアクションを特定する必要があります。アクションとは、マウスによってそのコントロール部分がアクティブにされたときに引き続いて起る、命令の集合に他なりません。Mopsプログラムでは、アクション、というよりむしろ、アクション定義のアドレスが、コントロールオブジェクトのインスタンス変数として格納されます。さらに、各コントロール部分は互いに異なるID番号をもっているので、ツールボックスは与えられたマウス操作に与えられたアクションを結びつけることができます。Macintoshの既定義のコントロールのためのIDは、次のようになります:
ボタン |
= |
10 |
チェックボックスまたはラヂオボタン |
= |
11 |
スクロールバーのUpアロー |
= |
20 |
スクロールバーのDownアロー |
= |
21 |
スクロールバーのPage Up領域 |
= |
22 |
スクロールバーのPage Up領域 |
= |
23 |
スクロールバーのつまみ |
= |
129 |
GrDemoコントロール
grDemoにおける特殊なスクロールバーコントロールは、そのインスタンス変数を、そのスーパークラスであるViewおよびControl(68k Mops)またはROOTCTL(PowerMops)から継承しています。利用可能なインスタンス変数のリストの中には、定義ID (PowerMopsにはありません)、五つの可能なスクロールバーアクションのアドレスのためのX-array、アクションの対応する部分番号のためのOrdered-Colがあります。
68k MopsではVScrollオブジェクトが生成されるときはいつも、そのCLASSINIT:メソッドがコントロールID番号16をID Ivarに格納することによって、自動的にそれをスクロールバーにします。すべてのビューは、それが表示されるべきときにNEW:メッセージを受けますが、それをこのオブジェクトが受けたときには、それ自身が現れるような適切なシステムに対する呼出しを行います。【PowerMopsでは、コントロールのタイプ毎に別々に用意された関数の呼出しが行われます。】このような呼出しはDRAW:メッセージを受け取ったときも必ず行われます。VScrollクラスの定義は、Toobox classesフォルダー内の、ファイル6Ctl(68k Mops)またはzCtl(PowerMops)にあります。
スクロールバーアクション
デモプログラムを続けて見ていくと、各スクロールバーの各部分をクリックしたときに起るアクションが、五つの定義として並んでいます。各アクションハンドラのフォーマットは、増分の値を除けば皆非常に似ています。しかし、これらの要素の鍵となるのは、それらが、Value変数 ThisCTL を用いたMopsの特殊な構成に依存していることです。
ThisCTLは, 本質的には、最も最近にアクティブにされたコントロールのアドレスを追跡するものです。ですから、もしあなたが、わたしたちの三つのスクロールバーの二番目のもののページアップ部分をクリックしたならば、ThisCTLは、あなたがアクティブにしたのは第二のスクロールバーであったことを記憶します。アクションハンドラーの定義においては、このとき、get: [ ThisCTL ] が第二のスクロールバーの以前の値を取り出します。このget:メソッドのオブジェクトは、ランタイムに動的に決定されます。これは、第二部で説明されるであろう、動的束縛(late binding)のテクニックです。第二のスクロールバーの値が10減らされた後、ウィンドウにアップデートメッセージが送られる前に、put: [ ThisCTL ]によって、その値が第二のスクロールバーのインスタンス変数に格納されます。この動的束縛メカニズムの重要な点は、わたしたちが各スクロールバー毎に五つのアクションハンドラーを定義したり、そのコードを最小に抑えようとしてアルゴリズムを工夫したりする必要が無くなることです。ThisCTLは、最少限のコードによる十分な制御の柔軟性を与えてくれるのです。
doThumbの定義は特別で、スクロールバーの全値域に合わせてつまみの相対的な位置に基づき値を自動的に計算します。doPgUpとdoPgDnはスクロールバーの値を、10だけ(それぞれ)増やし、または減らします。そして、doLnUpとdoLnDnは、それぞれの方向に図形を1だけ調整します。
アクションハンドラーの定義後の行では、grDemoが最初に起動されたときに描画される図像のタイプとして、ljの定義(リサージュ図形を描画するものです)のアドレスがdWindに組み込まれます。記号 ' (ティック)は、エグゼキューショントークンあるいはxtを返します。これはそのワードを後で実行するために保存し利用することができる数値です。ljはこのプログラムの少し前で定義されたものですが、この場合、そのxtはsetDraw: dWindメッセージのパラメターとして渡されます。dWindのクラス定義を調べれば、setrdraw:メソッドはグラフィックスルーティン(lj,spinなど)のxtをdWindのdrawインスタンス変数に格納していることがわかります。
ちょっとした余談:Mopsでは、他の多くのForthでも同じですが、このエグゼキューショントークンは、実際に、そのワードのためにコンパイルされた実行可能コードのアドレスになっています。しかし、これはすべてのForthで成り立つことではありません。それで、ANSIスタンダードは、現在では、より一般的な言い方である、'execution token'を用いているのです。
次に、五つのコントロールアクションのxtは、各スクロールバーのアクションインスタンス変数に格納されます。ここでシンタックスである、xts{ ... }は、各アクションハンドラーワードのxtを入力するための簡便法です。各定義のアドレスは、そのスクロールバーのアクションivarに引数として渡されます。
最終更新:2018年12月10日 10:09