目次へ    最終更新 : 2003/9/7

第6章 ウィンドウとダイアログ



 大抵のGUIでは、ユーザーが面と向かう主なトップレベルの対話オブジェクトは、ウィンドウ(window)ダイアログ(dialog)である。このことは、オブジェクトI/Oライブラリにも当て嵌まる。ウィンドウとダイアログは、同一のコントロール集合と多くのライブラリ関数を共有している。ダイアログは、特化したウィンドウと見なされる。プラットフォーム如何によっては、ダイアログは通常、特別な拡張ユーザーインターフェースを提供する。もう1つの相違は、ダイアログをモーダルに開くことができるということである。このモードでは、ユーザーは、プログラムが継続実行される前に、完全なダイアログ処理を強いられる。ウィンドウとダイアログの類似性を強調する為、その代数型定義はほとんど同一である(モジュールStdWindowDefで見つけることができる)。

 詳細に掘り下げる前に、まず、セクション6.1では、ウィンドウとダイアログと基本用語を紹介し、セクション6.2では、それらの属性を議論する。ウィンドウとダイアログの開閉は、セクション6.3で処理する。次に、セクション6.4と6.5では、ウィンドウとダイアログを作り上げている部品の処理方法を議論する。キーボート入力とマウス入力の処理は、セクション6.7で示す。最後に、セクション6.8でのモーダルダイアログの処理を以って本章の終わりとする。


6.1 基本用語

 ウィンドウ(window)の主目的は、ユーザーに、型Pictureのオブジェクトとして、グラフィカルに表現されるドキュメントを表示することである。Pictureは、第5章で議論した。マウスとキーボードを使用することによって、ユーザーは文書を操作できる。ウィンドウのコントロールは、更なる操作機能を加えることができる。ダイアログ(dialog)の主目的は、ある動作を実行する為に情報を渡すことに関する構造化された方法をユーザーに提示することである。この構造化された通信は、コントロールによって実現される。

6.1.1 ウィンドウとダイアログの構造

 ユーザーの視点からすると、ウィンドウとダイアログは'硬い'オブジェクトに見えるが(図6.1)、様々な観点からウィンドウを見ると分かり易い。

図6.1: ユーザーの視点から見たウィンドウ

 ウィンドウは3つの層からなる。図6.2参照。下層である文書層(document layer)は、描画された文書であるPictureによって成形される。中間層であるコントロール層(control layer)は、ウィンドウの全てのコントロールを持つ。上層であるウィンドウフレーム(window frame)は、典型的には、タイトルバーと、ウィンドウを閉じたりリサイズしたりするウィンドウコンポーネントからなる。ウィンドウフレームはどんなサイズでもよく、画面のサイズとプラットフォーム依存な最小サイズにしか制限されない。ウィンドウフレームは、文書層のクリッピング領域として機能する。ウィンドウは通常、ユーザーが文書層の現在の外観を変更するのを助けるスクロールバーを持っている。可視的な文書層の現在の部分は、ビューフレーム(view frame)と呼ばれる。文書層Pictureのデフォルト描画領域は、両軸において0から230までである。これは、一般的にみて、文書を描くには大きい。ウィンドウは、そのビュー領域(view domain)を設定することによって、ウィンドウの表示可能領域を制限できる。

図6.2: ウィンドウに対する様々な見方


 ウィンドウとは対照的に、ダイアログはたった2つの層からしか成り立っていない。つまり、コントロール層と、ダイアログフレーム(dialog frame)とも呼ばれるウィンドウフレームである。文書層の代わりに、ダイアログはプラットフォーム依存な背景を持つ。プログラムは、その背景を描画することも操作することもできない。ダイアログフレームはリサイズできないので、通常はコントロール層全体を表示するのに十分な大きさを持っている。

 ウィンドウとダイアログに対しては、プログラマは、ビューフレーム、コントロール層と文書層を完全に制御できる。セクション6.4では、文書層の操作方法を議論している。コントロール層の操作はセクション6.5で議論する。ウィンドウとダイアログフレームは、そのプラットフォームによって制御される。セクション6.6参照。

6.1.2 スタック順序

 一般に、プログラムは、任意数のウィンドウとダイアログを同時に開くことができる。これらの要素は、アプリケーション使用者が見る上から底への順序であるスタック順序(stacking order)で現れる。通常のウィンドウとダイアログに付いては、スタック順序は固定されていない。しかし、モーダルダイアログは常に、最頂に現れる。(いくつかの)モーダルダイアログが開いている間は、開かれているウィンドウとモードレスダイアログは常に、最低のモーダルダイアログの下に現れる。

6.1.3 アクティブウィンドウ又はダイアログ

 ユーザーがプログラムを作動させる場合、ただ1つのウィンドウ又はダイアログが、全てのキーボード入力とマウス入力を受け取る。この要素は、アクティブウィンドウ/ダイアログと呼ばれ、入力フォーカス(input focus)を持つ。モーダルダイアログという例外を除いて、アクティブウィンドウ/ダイアログは、必ずしも最頂のスタック位置を占有しているとは限らない。


6.2 ウィンドウ属性とダイアログ属性

 WindowAttributeは、ウィンドウ属性とダイアログ属性の型である。以下の表は、どの属性がどの要素に有効であるのかを示している。

ウィ ン ド ウ 属 性
ウィンドウとダイアログ両用ウィンドウ専用ダイアログ専用
WindowActivateWindowCursorWindowCancel
WindowCloseWindowHScrollWindowOk
WindowDeactivateWindowKeyboard 
WindowHMarginWindowLock 
WindowIdWindowMouse 
WindowIndexWindowOrigin 
WindowInitWindowPen 
WindowInitActiveWindowSelectState 
WindowItemSpaceWindowViewDomain 
WindowOuterSizeWindowVScroll 
WindowPos  
WindowViewSize  
WindowVMargin  
 

6.2.1 ウィンドウ属性とダイアログ属性

WindowActivateとWindowDeactivate

 これらの属性は、ウィンドウがアクティブウィンドウになる場合(WindowActivate)と、それがもはやアクティブウィンドウでない場合(WindowDeactivate)のそれぞれに関するウィンドウの動作を定義する(6.1.3も参照)。属性が提供されない場合、この情報はプログラムに渡されない。

WindowClose

 この属性は、プラットフォーム依存の閉じる機能をウィンドウ/ダイアログに加えている。関連付けられた関数が、このコントロールが駆動される場合に評価されるだろう。実際、ウィンドウ/ダイアログを閉じることは、この関数の責任である。WindowClose属性が提供されない場合、ウィンドウ/ダイアログをこの方法で閉じることはできない。

WindowHMarginとWindowVMargin

 これらの属性は、ウィンドウ/ダイアログそれぞれの左右及び上下の余白を決める。ウィンドウのデフォルト値は0であるが、ダイアログはプラットフォーム依存である。

WindowId

 この属性は、関連付けられているウィンドウ/ダイアログを特定する。WindowIdが提供されない場合、オブジェクトI/Oシステムの開く関数(open function)は、ウィンドウ/ダイアログの新規Idを生成する。

WindowIndex

 この属性は、ウィンドウ/ダイアログの初期スタック位置を決める(6.1.2も参照)。WindowIndex属性が提供されない場合、ウィンドウ/ダイアログは最前に開かれる。モーダルダイアログは常に最前に開かれる。

WindowInit

 この属性は、ウィンドウ/ダイアログを開いた直後に実行されるべき動作を定義する。これは、プロセス初期化動作と同等である(セクション2.3と11章参照)。WindowInit属性が提供されない場合、追加的な動作は実行されない。

WindowInitActive

 この属性は、その親ウィンドウ/ダイアログが開いている場合に、どのコントロールが初期入力フォーカスを持つべきかを定義する。WindowInitActive属性が提供されない場合、最初にキーボートを感知するコントロールが選択される。

WindowItemSpace

 この属性は、コントロールのレイアウトに更なるオフセットが提供されない場合の、コントロール間のスペースを決める。デフォルト値は、ウィンドウとダイアログについてと同様である。

WindowOuterSizeとWindowViewSize

 この2つの属性は、ウィンドウ/ダイアログの初期サイズを決める。最初の属性は、ウィンドウ/ダイアログフレームサイズによってそのサイズを定義する。第2の属性は、ウィンドウ/ダイアログビューサイズによってそのサイズを定義する。どちらの属性も提供されない場合、システムは、適切なサイズを導出する。ダイアログの場合、そのサイズはコントロール、余白と項目スペースの集合によって決められる。ウィンドウの場合、その画面サイズが選択される。

WindowPos

 この属性は、ウィンドウ/ダイアログの初期位置を決める(セクション7.3も参照)。ItemPosに出現する相対Idsは、他のウィンドウ/ダイアログを参照する。オブジェクトI/Oシステムは常に、現在の画面上に可視的なようにウィンドウ/ダイアログを配置するだろう。ウィンドウにWindowPosが提供されない場合、ウィンドウは画面の左上に配置されるだろう。ダイアログにWindowPosが提供されない場合、ダイアログは、プラットフォーム使用者が面するに快適な位置(典型的には中央)に配置されるだろう。

6.2.2 ウィンドウ属性

WindowCursor

 この属性は、マウスがウィンドウの上にあるが、カーソルの形を換えることのできるコントロール内には無い場合のその形を定義する。属性が提供されていない場合、ウィンドウにマウスを動かしてもその形は変わらない。

WindowHScrollとWindowVScroll

 これらの属性は、ウィンドウに水平スクロールバーと垂直スクロールバーを加える。属性が与えられていない場合、スクロールバーは加えられない。

WindowKeyboardとWindowMouse

 これらの属性によって、ウィンドウは、マウス(WindowMouse)とキーボード(WindowKeyboard)を用いたユーザーアクションに応答することができる。属性が提供されない場合、この情報はプログラムに渡されない。両方の属性ともに、追加的なフィルタを定義していくつかの入力アクションを無視できる。ウィンドウのSelectStateがUnableである場合、どちらの関数も入力を得ることは無い。

WindowLook

 この属性は、ウィンドウの現在のSelectStateとウィンドウのどの部分が表示されるべきなのかについての情報が与えられると、ウィンドウがどのような形になるかを定義する関数を定義する。デフォルトの外観は、ウィンドウをプラットフォーム依存な背景色で塗り潰す。(Look関数は、CustomButtonControl(セクション7.1.3)、CustomControl(セクション7.1.4)とCompoundControl(セクション7.1.11。これは印刷用にも使用できる(13章)に対して類似の役割を果たす。)
 Look関数に加えて、WindowLook属性は論理値引数も持っている。この属性がTrueである場合、オブジェクトI/Oシステムは、ウィンドウの内容が実際のビューフレームの嗜好から独立しているとを想定する。その場合、ウィンドウサイズのスクロールと変更をより効率的に行うことができる。属性がFalseである場合、Look関数は常に、現在のビューフレーム全体に適用される。この属性を使うことに関する更なる情報については、セクション6.4.1も参照。

WindowOrigin

 この属性は、ウィンドウで現在可視的であるViewDomainの矩形部ViewFrameの初期位置を決める。この位置は常に、与えられたViewDomain内にあるかを検証される。WindowOrigin属性が提供されない場合、ViewDomainの左上座標が使用される。

WindowPen

 この属性は、ウィンドウのPicture内に描画するのに使用される初期ペンを決める。WindowPen属性が提供されない場合、デフォルトペン属性が使用される(セクション5.1も参照)。

WindowSelectState

 この属性は、ウィンドウがユーザーのキーボード又はマウス入力を受理する(Able)か否か(Unable)を決める。デフォルト値はAbleである。ウィンドウはアクティブでもよいが、無効であってもよいということに注意。このことは、全入力がウィンドウによって無視されるということを意味するに過ぎない。

WindowViewDomain

 この属性は、文書層の描画座標システムを定義する(6.1.1も参照)。この領域外の描画演算はクリップされる。ViewDomainが提供されない場合、ウィンドウはViewDomain {viewDomainRange & corner1=zero}を取得するだろう。

6.2.3 ダイアログ属性

WindowCancelとWindowOk

 これらの属性は、どのコントロールが動作すべきなのかを指定し、プラットフォームUIの'Cancel'コントロールと'confirm'コントロールに各々合わせるのかを指示する。この属性が提供されない場合、コントロールは選択されない。


6.3 ウィンドウとダイアログの開閉

 ウィンドウとダイアログは、各型構成子クラスWindowsとDialogs(モジュールStdWindow、付録A.47)の適切なインスタンスを使用して開かれる。ウィンドウとダイアログは同じコントロール集合を共有している。型システムによってこれを守る為、ウィンドウとダイアログのインスタンス宣言は、型構成子変数cに関する型構成子クラス制限を付けて提供されており、その型構成子変数はControls型構成子クラスのインスタンスでなければならない。

 ウィンドウとダイアログを閉じる為には、関数closeWindowを使用しなければならない。多くの文脈で、アクティブなウィンドウ又はダイアログを閉じなければならない場合がある。これらの場合の為に、便利な関数closeActiveWindowが提供されている。

 closeActiveWindowを使用することの典型例は、WindowClose属性用のコールバック関数である。全ての状況下でウィンドウとダイアログを閉じることが認められている(インスタンスの為に何も保存される必要がない)場合、以下の属性定義で十分である。

 noLSは、要求された型(.ls,PSt .l) -> (.ls,PSt .l)の関数を取得することに使用されるので注意。


6.4 文書層の処理

 ウィンドウの文書層は、操作されている文書の現在のステータスについて、ユーザーに視覚的なフィードバックを提供するのに使用される。ウィンドウだけが文書層を持つ。以下では、文書層への描画方法を2つ議論する。最初の方法である間接レンダリング(indirect rendering)(セクション6.4.1)はルック関数を使用し、第2の方法である直接レンダリング(direct rendering)(セクション6.4.2)は、文書層に直接描画する。いくつかのプログラミングの実用例をセクション6.4.3で議論する。セクション6.4.4の例で締め括る。

6.4.1 間接レンダリング

 2つのWindowAttribute、つまりWindowViewDomainとWindowLookは、文書層に関して主要な役割を果たす。それらを詳細に見てみよう。以下にあるのはその型定義である(モジュールStdWindowDef)。

 文書層は、Pictureを使用して描かれる。セクション6.2で見たように、Pictureのデフォルトの描画範囲は、両軸で(0,230)である。この範囲は、WindowViewDomain属性によって変更できる。この範囲は、Rectangleとして定義されるViewDomain引数を持つ。

 Rectangleは、新しい描画範囲の対角線上にある2つの角点からなるレコードである。ビュー領域は、StdIOCommonで定義されたviewDomainRangeマクロ間であればどんな値でも持つことができる。

 唯一の不法な値は、ビュー領域の角点について、x座標とy座標が同じであることである。これによって、貴方のアプリケーションは、"Error in rule validate WindowDomain [windowvalidate]: Window has illegal ViewDomain argument"というメッセージで異常終了する。

 ビュー領域は、描画されるべきウィンドウの座標を定義する。ウィンドウが、利用可能な画面より小さいビュー領域を持っている場合、ウィンドウのサイズがそのビュー領域のサイズを越える場合がある。従って、ウィンドウのビューフレームはウィンドウのビュー領域よりも大きい場合がある。余分な領域に描画することも認められている。座標システムは単純に右下に伸びる。ウィンドウは、ビュー領域の最小値より左上にある領域は決して表示しない。

 文書層のPictureは、WindowLook属性を使用して描画される。この属性は、Look関数引数を持つ。これは以下のように定義される。

 視覚的な文書層(の一部)を描くことが必要な場合はいつでも、オブジェクトI/Oシステムは、WindowLook属性のルック関数を適用する。この関数は、ウィンドウの現在のSelectStateと、現在のビューフレームのどの部分が描画されなければならないかの詳細な情報を引数に持つ。この情報は、UpdateStateレコードによって提供される。

 UpdateStateレコードの3つのフィールドは、以下の情報を持つ。
 ウィンドウの現在のSelectStateとUpdateStateが与えられると、ルック関数は文書層の現在のPictureに適用される。この為、ルック関数は、引数Pictureがどうであっても正しく動作するような方法で書かれなければならない。更新領域の矩形の現在の内容については、それらが全く正しいように見えない点を除いて、いかなる前提も設けることはできない。

 Look関数に加えて、WindowLook属性は、Bool引数を持つ。この値がFalseである場合、ウィンドウの描画を完全に制御できる。例えば、ウィンドウのサイズが縮小する場合、Lookに渡されるUpdateStateは、oldFrameにリサイズ前のより大きなビューフレームを格納し、newFrameにリサイズ後の縮小されたビューフレームを格納するだろう。加えて、updAreaは、newFrameと等しい1つの矩形のみからなる。この方法で、イメージの内容を現在のウィンドウビューフレームに依存させることができる。以下のLook関数は、その新規ビューフレームと2つの対角線のサイズで箱を描画する(図6.3参照)。

図6.3: Look関数をウィンドウサイズに従わせる

 WindowLook属性関数の目的は、文書層の現在の状態の外観を記述することである。文書が変化しない場合、この関数は常に正しく文書を描く。しかし、大部分のウィンドウに関しては、文書の状態がそのライフサイクル中変化する。WindowLook属性は、StdWindowの関数setWindowLookとgetWindowLookを使用して変更・取得できる。

 setWindowLookは、新しい(Bool,Look)引数を持つId引数によって指示されたウィンドウが存在し、ダイアログを参照していない場合、そのウィンドウの現在のWindowLook属性を変更する。最初のBool引数がFalseである場合、全てOKである。それがTrueである場合、ルック関数は、上で説明した方法でウィンドウに適用される。

6.4.2 直接レンダリング

 ウィンドウのLook関数を使用するだけではなく、ウィンドウの文書層のPictureに直接描画することもできる。これは、関数appWindowPictureとaccWindowPicture(StdWindow、付録A.47)を使用して行われる。

図6.4: 動作中のビットマッププログラム

 appWindowPictureは、引数である描画関数を、Id引数によって指示されたウィンドウが存在し、ダイアログを参照していない場合に、そのウィンドウの文書層のPictureに適用する。accWindowPictureは、その引数関数がある型の結果を返すことを除いて、同一である。指示したウィンドウが実際に発見され、引数関数が値xを返した場合、accWindowPictureの結果は(Just x)である。他の全ての場合では、Nothingである。

 点滅カーソル又はトラック箱の描画のような視覚的なフィードバックに関しては、この方法は、Look関数を使用する間接的な方法よりも適合的である。セクション9.1.1のタイマ例は、直接レンダリング法が適用されるウィンドウを図示している。

6.4.3 実用

 オブジェクトI/Oシステムは、ウィンドウの内容が描画されるのに必要な全ての場合について、WindowLook関数を使用される。なかんずく、ウィンドウのビューフレーム、サイズ、スタック順序又はselectstateが変化する場合にはそうである。これらの動作に多くの時間がかかる場合、アプリケーションユーザーは非常にいらいらするだろう。従って、描画関数のリストから良いパフォーマンスを得る際にいくぶんかの努力を費やすことには価値がある。

6.4.4 例:ビットマップの表示

 この例では、ユーザーがウィンドウに表示される任意のビットマップを選択できるようにするプログラムを生成する(図6.4参照)。

 最初に行うべき事は、ユーザーに、ビットマップを選択する機会を与えることである。この為に、モジュールStdFileSelectのライブラリ関数selectInputFile(付録A.10)を提供している。この関数を適用すると、プラットフォーム標準のファイル選択ダイアログが開く。ユーザーがファイルを選択した場合、返り値は、選択ファイルのフルパス名を持つ。ファイルが選択されない場合、Nothingが返る。この関数は、他の2つの関数、つまりselectOutputFile(ユーザーに(新規)出力ファイルの選択を認める)とselectDirectory(ユーザーにディレクトリの選択を認める)を含む、FileSelectEnv型構成子クラスに属している。

 プログラムの最初の部分は以下のような外観である(ファイルが選択されない場合、プログラムは単に終了するだけである)。

 選択された入力ファイルのパス名が与えられると、関数openBitmap(StdBitmap、付録A.1)を、ビットマップでの読込みに使用できる(セクション5.4.9も参照)。

 openBitmapは、読込みに成功できた場合ビットマップを返すが、そうでない場合はNothingを返す(この場合、プログラムも終了させることになる)。この部分のプログラムは以下のようになる。

 ビットマップでの読込みが成功すると、StdBitmapの関数getBitmapSizeを使用して、そのサイズを決めることができる。

 ビットマップを表示するウィンドウは、((WindowViewSize bitmapsize)属性を設定して)そのビットマップと同じ初期サイズを持つ。この例では、間接レンダリング法を使用する。これは、(WindowLook True (\_ _ -> drawAt zero bitmap)属性を設定することによって行われる。最後に、ユーザーがウィンドウを閉じることを求めると、そのアプリケーションは単に終了するだけである。これは、(WindowClose (noLS closeProcess))属性を設定することによって行われる。従って、以下のウィンドウ定義を取得する。

 最後のステップは、startIO関数(StdProcess、付録A.27)を適用することによって、ウィンドウを持つ対話プロセスを生成することである。このプロセスの唯一の初期化動作は、ウィンドウを開くことである。ユーザーがプロセスに終了を要求すると、そのアプリケーションはそれに従う。これは、(ProcessClose closeProcess)属性を設定することによって行われる。

 これでプログラムは完成である。ここに完全なプログラムコードを示す。


6.5 コントロール層の処理

 コントロール層は、ウィンドウ又はダイアログのコントロールを持っている。ウィンドウとダイアログは、同じ集合のコントロールを持つことができる。セクション6.3で見たように、各型構成子クラスの型構成子クラスインスタンス宣言によって、これを明示的に行った。

 標準的なControlsインスタンスの集合は、モジュールStdControlClass(付録A.6)で定義される。コントロール層に関する多くの演算は、CompoundControlsの要素であるコントロールに作用する演算と同一である。コントロールとその演算は、7章で詳細に論じる。



6.6 ウィンドウフレームとダイアログフレームの処理

 ウィンドウフレームとダイアログフレームは、ウィンドウとダイアログの'物理的な'境界である。ユーザーは、マウス又はキーボードインターフェースを使用して、それらを掴むことができ、それらをドラッグし、(ウィンドウの場合)サイズを変更し、それらを処理することができる。プログラムは、フレームの外観と機能の双方に非常に限定的な影響しか与えない。ウィンドウ又はダイアログを開く場合、WindowAttributeは重要である。これはセクション6.6.1で議論する。ウィンドウフレーム又はダイアログフレームに関するユーザーアクションは、コールバック関数機構を経由して、プログラムによって処理される。プログラムはフレーム属性も変更できる。これはセクション6.6.2で議論する。

6.6.1 ウィンドウフレーム又はダイアログフレームを開く

 ウィンドウフレームとダイアログフレームに影響するウィンドウ定義とダイアログ定義の属性は勿論、Titleと以下のWindowAttributeである。

WindowOuterSizeとWindowViewSize

 これらの属性は各々、ウィンドウ又はダイアログフレームの好みのサイズと、ウィンドウ又はダイアログビューフレームの好みのサイズを与える。規約によって、ウィンドウ又はダイアログの属性リストが両方の属性を持つ場合、これらのうちの最初のみが有効である。

WindowClose

 この属性が存在している場合、ウィンドウフレーム又はダイアログフレームにプラットフォーム依存なインターフェース要素を提供して、ユーザーはプログラムに、そのウィンドウ又はダイアログを閉じるよう要求できる。

WindowHScrollとWindowVScroll

 ウィンドウの水平スクロール棒と垂直スクロール棒はコントロール層の要素だが(図6.2)、その動作は、ビューフレームのサイズに密接に結びついているので、ウィンドウフレームとダイアログフレームのサイズにも結びついている。

6.6.2 ウィンドウフレームとダイアログフレームの変更

 ウィンドウフレームとダイアログフレームに対する最も明白な変更は、嗜好とサイズの変更である。ウィンドウの場合、アプリケーションユーザーが変更できるが、セクション6.6.1で説明したような属性に依存している。

 プログラムは、ウィンドウとダイアログ両方のフレームのサイズも変更できる。ダイアログに付いては、コントロールを開閉することによってのみ、間接的にこれを行うことができる。ウィンドウに付いては、直接に行うこともできる。プログラムがビューフレーム(そのサイズ又は嗜好)を変更する場合、コントロールのレイアウトは、ユーザーがこの変更を引き起こした場合と同じ方法で変更される。


6.7 キーボード入力とマウス入力の処理

 アプリケーションによって制御されている全てのウィンドウとダイアログの中で、多くとも1つは、キーボード入力とマウス入力を受ける。このウィンドウは、アクティブウィンドウ(active window)である。

 モーダルダイアログが開いている場合を除いて、アプリケーション使用者は常に、新しいアクティブウィンドウになる可視的なウィンドウ又はダイアログを1つ選択できる。2つのWindowAttribute、つまりWindowActivateとWindowDeactivateを加えることによって、ウィンドウとダイアログにこれらのイベントを通知できる。両方の属性とも、そのウィンドウがアクティブになったり非アクティブになったりするとすぐに、オブジェクトI/Oシステムによって適用される関数を引数に持つ。WindowDeactivate属性関数は、WindowActivate関数前に適用されることが保証されている。

 基礎にあるプラットフォームは常に、アプリケーション使用者に、どのウィンドウ又はダイアログが現在アクティブであるのかについて視覚的な手掛りを与える。プログラムは、getActiveWindow関数(付録A.47)を使用して、この情報を取得できる。アクティブウィンドウ又はダイアログが最上のスタック順序位置を持つということを想定するのは正しくないと意識しておかなければならない。一例として、getWindowStackの結果リストの最初の要素からIdを取ることによって、アクティブウィンドウのIdを取得しようと試みる者もいるかもしれないが、これは、最上ウィンドウを返すだけであって、アクティブウィンドウを返す訳ではない。

 プログラムは、ウィンドウとダイアログをアクティブにすることもできる。これは、setActiveWindow関数(付録A.47)によって行われる。モーダルダイアログは常に最前であり、最前のモーダルダイアログはアクティブなので、モーダルダイアログが開いている間は、ウィンドウ又はモードレスダイアログをアクティブにすることはできない。代わりに、setActiveWindowは、最底のモーダルダイアログをアクティブウィンドウにすること無く、それのすぐ背後に、ウィンドウやモードレスダイアログを再び重ねる。

 アクティブウィンドウは、キーボード入力とマウス入力の全てを受ける。このウィンドウがコントロールを持っている場合、入力がこのコントロールの1つに向けられる場合がある。すると、その特定のコントロールは入力フォーカス(input focus)を持つ。どのコントロールも入力フォーカスを持っていない場合、全ての入力はアクティブウィンドウによって処理される。アクティブウィンドウがダイアログである場合、入力への応答は、基礎にあるプラットフォームに完全に定義されている。ウィンドウの場合、プログラムは、キーボード入力についてはWindowKeyboard属性を加えることによって(セクション6.7.1)、マウス入力についてはWindowMouse属性を加えることによって(セクション6.7.2)、その動作をカスタマイズできる。両者の属性ともに、実際のコールバック関数が評価される前に適用されるフィルタ関数(それぞれKeyboardStateFilterとMouseStateFilter)を持つ。このフィルタがTrueである場合にのみコールバック関数が評価される。

6.7.1 キーボード入力

 キーボードセンシティブなあらゆるインターフェースオブジェクトは、第一引数として、型KeyboardStateの値を受けるプロセス状態遷移関数であるKeyboardFunctionを持つ。この値は、1つのキーボードイベンドを表す。キーボードイベントは常に、型KeyStateの値によって以下の順序で特徴付けられる列として生成される。

 キーボードイベントは、ASCII文字(CharKey代替部)か、特殊キー(SpecialKey代替部)のどちらかの入力を持つ。代替部KeyLostは、上の列が何かしらの理由(例えば、別のウィンドウがアクティブになる場合)で解釈された場合はいつでも生成される。

 SpecialKeyは、モジュールStdKey(付録A.15)を経由してインポートされる。なかんずく、SpecialKeyは、ファンクションキー、カーソルキー、ページキーとラインキーを定義する。Modifiers型は、StdIOCommonで定義されている。これは、キーボードのメタキーの状態を参照するレコードである。ASCII文字の中には、これらのメタキーを使用して生成されるものがあるので、CharKey代替部では提供されない。従って、シフト'a'を押すと、単純に値(CharKey 'A' (KeyDown False))が生成される。

 オブジェクトI/Oシステムは、いつでも、1つのキーボード代替部だけしか処理されないということを保証する。ユーザーがキーボードの'a'キーを押しているとする。これは、文字'a'キーダウンイベントである(CharKey 'a' (KeyDown False))を生成し、それから文字'a'反復キーイベント(CharKey 'a' (KeyDown True))の列を生成する。ユーザーが、ここで'b'キーも押すと、オブジェクトI/Oシステムは、2つの仮想イベントを挿入し、プログラムに、ユーザーがまず'a'キーを放して文字'a'キーアップイベント(CharKey 'a' KeyUp)を生成し、それから'b'キーを押して文字'b'キーダウンイベント(CharKey 'b' (KeyDown False))を生成したと信じさせる。これらの後に、文字'b'反復キーイベントが続く。

例:キースポッティング

 キーボード処理の使用を例解する為、最後のキーボード入力が表示されるウィンドウを持つプログラムを生成する。図6.5は、このプログラムのスナップショットを示している。

図6.5: アクションの際のキースポッティングプログラム

 ウィンドウがキーボード入力を追跡できるようにする為には、ウィンドウがWindowKeyboard属性を持たなければならない。あらゆるキーボード入力を監視するつもりなので、属性のキーボードフィルタ関数引数は、あらゆるKeyboardStateを受けなければならない。これは、StdFuncライブラリ関数constを使用して、(const True)によって便利に定義できる。勿論、キーボード関数は、キーボード入力を処理しなければならないので、SelectState属性はAbleである。キーボード関数spottingは、持ち上げ関数noLS1を使用することによって無視できるウィンドウの局所状態とは関連が無い。キーボード関数は、ウィンドウのIdであるwidを引数に持つ。このことによって、WindowKeyboard属性の以下の定義が与えられる。

 キーボード関数spottingは、(セクション6.4.1で議論した)間接レンダリング法を使用して、最後のキーボード入力を表示する。これは、setWindowLook関数(StdWindow、付録A.47)を適用して、新規キーボード入力がウィンドウに届く度に、そのウィンドウのLook関数を変更する。この為、spottingは、ウィンドウのIdを引数に持つ。StdIOCommonでは、関数の型を持たない全ての型定義のインスタンスがtoString用に定義されている。spottingの型は汎化できるので、多重定義関数toStringのインスタンスが存在している第2引数であれば何に対しても動作する。

 ウィンドウのLook関数は、表示されるべき文字列を引数に持つ。これは、現在のビューフレームの文字列を単純に中心に位置させる。ビューフレームのサイズ(型Rectangleの値)は、StdIOBasicの関数rectangleSizeを使用して決定できる。現在のペンで描かれた時の文字列のサイズは、StdPictureの関数getPenFontStringWidthを使用して決めることができる(セクション5.3も参照)。

 この方法でlookを設定することは、ウィンドウの外観がその実際のサイズに合わせて調節されるという利点がある。

 完全なプログラムを得る為になされなければならないことは、ウィンドウのId値を生成し、ウィンドウを開く対話プロセスを起動させることだけである。ここでは、以下に、キースポッティング例の完全なコードを示す。

6.7.2 マウス入力

 マウスセンシティブなあらゆるインターフェースオブジェクトは、第一引数として、型MouseStateの値を受け取るプロセス状態遷移関数であるMouseFunctionを持つ。この値は、1つのマウスイベントを表す。

 Point2型とModifiers型は、それぞれ、モジュールStdIOBasicとモジュールStdIOCommonに定義されている。Point2引数は、特定のMouseFunctionを持つ対話オブジェクトのビュー領域座標を基準としたマウスの位置を示す。Modifiers型構成子は、マウスイベントで押されたキーボードのメタキーの状態を参照するレコードである。マウスイベントは常に、MouseState型構成子の代替部構成子によって特徴付けられる列として生成される。

 MouseDown代替部のInt引数は、マウスがマウスダブルダウン時間内に下げられる回数を示す。マウスダブルダウン時間は、2つの連続したマウスダウンイベントとダブルクリックイベントを区別する、プラットフォーム依存な時間間隔である。このカウントに整数が使用されているが、その最大値は通常3である。カウントiを持つマウスダウンイベントが発生し、新しいマウスダウンイベントがマウスダブルダウン時間内に生成される場合、その次のマウスダウンイベントは、カウントi+1を持つ。その次のマウスダウンイベントがマウスダブルダウン時間内に生成されない場合、その次のマウスダウンイベントはカウント1を持つ。

 MouseLost代替部は、ある理由で上の列が中断される場合(例えば、別のウィンドウがアクティブになる場合)にはいつでも生成される。

 オブジェクトI/Oシステムは、マウスセンシティブなインターフェースオブジェクトのあらゆるMouseFunctionが、上に特徴付けられるマウスイベント列に適用されることを保証する。特定のウィンドウがアクティブで、ユーザーがマウスを押しているとする。これはまず、マウスダウンイベント(MouseDown代替部)を生成し、それにマウスドラッグイベント(MouseDrag代替部)の列が続く。何らかの理由で別のウィンドウがアクティブになっている場合、オブジェクトI/Oシステムは、仮想イベントを挿入する。そして、その仮想イベントは、プログラムに、ユーザーがマウスボタンを離してマウスアップイベント(MouseUp代替部)を持ったことを信じさせるよう強制する。新しいウィンドウもマウスセンシティブである場合、そのMouseFunctionは、新しい仮想イベントに適用され、その仮想イベントは、プログラムに、ユーザーがマウスを再度押してマウスダウンイベント(MouseDown代替部)を持ったことを信じさせるよう強制する。これらの後に再びマウスドラッグイベントが続く。

例:マウススポッティング

 マウス処理の使用を例解する為に、ウィンドウのマウス入力を監視するプログラムを生成する(図6.6参照)。このプログラムは、セクション6.7.1のキースポッティング例とほとんど同一である。唯一の違いは、ウィンドウのタイトルと、WindowKeyboard属性をWindowMouse属性と置換えたことである。ここでは、spotting関数が多重定義されているという事実を利用している。完全にする為、マウススポッティング例をここに示す。

図6.6: 動作中のマウススポッティングプログラム


6.8 モーダルダイアログ

 ウィンドウとダイアログは、多くの共通する側面を持つ。本章の導入部でも述べた重要な相違は、ダイアログだけしかモーダルに開くことができないということである。プログラムがモーダルダイアログを開く場合、ユーザーは、他のあらゆる演算が発生し得る前にダイアログを完全に処理しなければならない。モーダルダイアログが開いている場合、他のモーダルダイアログを開くこともできるが、これらも完全に処理しなければならない。この方法で、モーダルのスタックを作ることができる。このような状況の一例は、プラットフォーム依存な出力ファイル選択子ダイアログ(StdFileSelectの関数selectOutputFileによって開かれる)の動作である。図6.7参照。ユーザーが既存ファイルを選択する場合、そのファイルを上書きしてよいかを尋ねる別のモーダルダイアログが開かれる。ユーザーは、出力ファイル選択子ダイアログを閉じる前に、このダイアログに応答しなければならない。

 型構成子クラスDialogsは、モーダルな方法でダイアログを生成する追加的な関数を持つ。

 openModalDialogとopenDialogの引数は同一である。つまり、ダイアログは型.lsの局所状態を持ち、型(wdef .ls (PSt .l))の定義を持つ。両方の関数とも、ErrorReportを返し、プログラムにダイアログが生成できるかを通知する。これら2つのクラスメンバ関数間の主要な相違は、openDialogは、その引数ダイアログを開き、それが閉じられる場合にのみ終了する。この状況には、closeWindow又はcloseActiveWindow(StdWindow、付録A.47)を適用することによるか、closeProcess(StdProcess、付録A.27)を使用して親の対話プロセスを終了することによってのみ到達できる。モーダルダイアログが閉じられた後は、その最終局所状態値が返される。次セクションで分かるように、これは、閉じたモーダルダイアログから呼出関数に追加的な情報を渡す快適な方法を提供している。

6.8.1 例:通知の拡張

 この例では、通知(notice)用に新しいDialogsクラスインスタンスを作ることによって、モーダルダイアログの使用を示す。通知は、数行のテキストと、通知を閉じる(多くの)ボタンしか持たない非常に単純なダイアログである。通知は、ある状況についてユーザーに簡単に知らせるプログラムで使用できる。図6.7の確認ダイアログは、このような通知の一例である。まず、セクション6.8.1の新規Dialogsインスタンスを持つnoticeモジュールを作る。この新しい対話オブジェクトを使用して、"Hello world"プログラムを書き直すことができる(セクション2.4)。これは、セクション6.8.1で行われる。

図6.7: selectOutputFile関数によって生成される2つのモーダルダイアログ

noticeモジュール

 通知を指定するDialogs型構成子クラスの特殊インスタンスを作りたい。先述のように、通知は、数行のテキストと(多くの)ボタンを持つダイアログである。あらゆるオブジェクトI/O要素についてなされるように、通知は、代数データ型によって定義されるだろう。

 Noticeの第一引数は、テキスト行のリストである。これらは互いに、以下に示す。追加的なNoticeButtonの1つを使用することによって、通知は常に、1つ以上の通知ボタンからなることを確実にできる。この義務的な通知ボタンは、その通知の右に配置される。通知ボタンは、右から左に現れる。通知ボタンは、NoticeButtonによって定義される。これは、タイトルとプログラム定義関数を引数に持つ。この関数は、ボタンが押され、通知が閉じられた後に適用される。

 Notice型構成子は、Dialogs型構成子クラスのインスタンスになる。クラスメンバ関数openDialogとopenModalDialogを使用すれば、プログラムが非モーダルとモーダルの通知の両方を生成できるのが実際には重要であろう。通知の生成を更に簡単化する為、より単純な型を持つ以下の追加的な関数を作りたい。

 この仕様は、以下のような外観を持つnotice定義モジュールでバンドルされる。

 notice実装モジュールを作り上げよう。通知は特化したダイアログなので、notice定義をダイアログ定義に対応付けるだけで十分である。この対応付けは、関数noticeToDialogによってなされる。プログラミングの簡便の為に、テキスト行をテキストコントロールに変換し、通知ボタンをボタンコントロールに変換する。コントロールはまだしっかり説明していないので、ここで何が起きているのかを理解する試みに挫折しないよう願っている。

 各通知テキスト行は、テキストコントロールに対応付けられる。テキストコントロールは、文字列を引数に持ち、位置属性を持つことができる。全テキストは左揃えである。これは、(ControlPos (Left,zero))属性を設定することによって表現される。この対応付けは、リストの内包表記を経由して、文字列textsのあらゆるリストに付いて簡便に表現できる。(ListLSデータ構成子は、7章で説明される理由によって必要である。)

 追加的な項目領域なくして、1つに箱に行をまとめて配置したい(ダイアログのデフォルト項目領域と余白値はプラットフォーム依存である)。好みの値の取得を確実にする為、(0の余白と両方向の3の項目領域を使用して)自分でダイアログ項目領域と余白属性を上書きする。これは、LayoutControlを経由して行われる。テキスト行の最後の対応付けによって、以下のことがもたらされる。

 まず、ある通知ボタンをあるボタンコントロールに対応付ける関数noticebuttonを導入しよう。通知ボタンが選択されると、まず、その通知ボタンは、(closeActiveWindowを使用して)通知を閉じ、それから引数関数に適用しなければならない。この動作は、ControlFunction属性の引数関数によって処理される。noticebuttonの追加的な引数としてボタンコントロールを渡すことによって、配置等のような他の属性がそれに追加できる。

 noticebuttonを使用して、通知ボタンをボタンコントロールに対応付けることができる。義務的な通知ボタンは、ウィンドウ属性WindowOK(セクション6.2.1)を使用して、"confirm"コントロールに対応付けられる。okidはこのIdを参照しているものとする。加えて、義務的なボタンは、ダイアログで右揃えに配置される。従って、okが義務的な通知ボタンならば、以下のボタンコントロールに対応付けられる。

 オプションの通知ボタンは、それ以前の通知ボタンの左に現れる。ここでも、通知ボタンbuttonsのリスト用の対応付けを行う為、リストの内包表記を使用できる。

 Notice値(Notice texts ok buttons)が与えられると、noticeToDialogは、無タイトルのDialogを生成し、式(texts' :+: ok' :+: buttons')を使用して、対応付けられた通知コントロールを糊付けする(コントロールの糊付けは次章でも議論する)。

 対応付け関数noticeToDialogが与えられれば、型構成子クラスDialogsの新規インスタンス宣言を定義するのは取るに足らない仕事である。

 また、便利な関数openNoticeの定義も取るに足らないものになる。

 完成させる為、ここにnotice実装モジュールを示す。

改訂版Hello world

図6.8: 通知を使用したhello worldプログラム

 新規通知インスタンスを使用して、"Hello world"プログラムの別の実装を作ることができる。セクション2.4のオリジナル版に非常に似ているが、ここでは、Dialogの代わりにNoticeを使用する。図6.8は通知を図示する。プログラムコードを以下に示す。


First Uploaded : August 24, 2003
Last Modified : September 7, 2003

Back