GUI とウィンドウ イベント

要約:
  • GuiEvent は、グラフィカル ユーザー インターフェイス (GUI) における、すべてのイベントの共通のスーパークラスです。
  • GuiEvent は、イベントのすべての処理を実行するハンドラによって消費され、ハンドラは GuiEvent が消費されたかどうかをチェックする必要があります。
  • GuiEventTarget にはスタティック イベント ハンドラがあり、サブクラスは一定の共通する動作を所定クラスのすべてのオブジェクトに提供できます。
GuiEvent は、グラフィカル ユーザー インターフェイス (GUI) に関するすべてのイベントに対する共通のスーパークラスです。GuiEventGuiEventTarget で起動され、キーボードとマウスによるユーザー アクションと GUI オブジェクトの間のやりとりを作成します。GuiWindowEvent は、ウィンドウに関連するイベントのGuiEvent のサブクラスです。すべての GuiEvent にはイベントを消費する共通のメソッド GuiEvent.consume、およびイベントが消費されたかどうかを示すプロパティ GuiEvent.consumed? があります。
GuiEventEvent の主な違いは、一般に次のようになります。

スタティック イベント ハンドラ

スタティック イベント ハンドラは、GuiEventTarget クラスのために定義されたメソッドの特殊なグループです。スタティック ハンドラによって、GuiEventTarget サブクラスは、クラスのすべてのオブジェクトに一定の共通動作を提供できます。スタティック ハンドラはクラス全体で 1 度だけ保持されるので、ダイナミック ハンドラを同様の複数オブジェクトにアタッチする場合と比べてメモリの節約にもなります。
既定では GuiEventTarget クラスのスタティック イベント ハンドラ自体は何もしませんが、特定のサブクラスで共通して起動されるイベントに対する標準イベント処理を提供するために、オーバーライドされているものがあります。たとえば、BasicTextFlowBox クラスには、BasicTextFlowBox.on-pointer-pressBasicTextFlowBox.on-pointer-release などの空でないスタティック イベント ハンドラがあります。これらは、テキスト選択をサポートするためにマウス ボタンが押されたり解放された場合に、すべてのタイプの TextFlowBox が応答する方法を制御します。
スタティック イベント ハンドラは、同一タイプのオブジェクトすべてについて特定イベントに対する共通の応答を決定し、さらにサブクラスでオーバーライド可能な単一メソッドにその動作をローカライズします。
たとえば、独自のグラフィカル サブクラスを作成する場合、イベントのスタティック イベント ハンドラを再定義することにより、新しいクラスの全オブジェクトの GuiEvent に対する応答方法を修正することができます。(これは、特定の単一オブジェクトに対するイベント処理に追加されるものです。単一オブジェクトイベント処理は、個々のダイナミック イベント ハンドラを追加して作成します。)
各スタティック イベント ハンドラはメソッドで、その名前は on- で始まり、その後に処理するイベント クラスの名前が小文字で単語をハイフンで区切った形で続きます。たとえば、on-pointer-enter というメソッドは PointerEnter イベントのスタティック イベント ハンドラです。EventTargetのスタティック イベント ハンドラを対応するイベント クラスと共に次の表に示します。ユーザーが Curl® 言語で記述されたアプレットとやりとりする方法により大きく分類されています。
注意: このリストには、すべての標準 GuiEvent クラスの概要も含まれています。たとえば、スタティック イベント ハンドラ GuiEventTarget.on-focus-eventGuiEvent とサブクラス FocusEvent に対応します。
一般スタティック イベント ハンドラ処理されるイベント
GuiEventTarget.on-selection-eventSelectionEvent
GuiEventTarget.on-selection-changedSelectionChanged
GuiEventTarget.on-window-closeWindowClose
キーボードに関連するスタティック イベント ハンドラ処理されるイベント
GuiEventTarget.on-focus-eventFocusEvent
GuiEventTarget.on-focus-inFocusIn
GuiEventTarget.on-focus-outFocusOut
GuiEventTarget.on-inspectionInspection
GuiEventTarget.on-key-eventKeyEvent
GuiEventTarget.on-raw-key-eventRawKeyEvent
GuiEventTarget.on-raw-key-pressRawKeyPress
GuiEventTarget.on-raw-key-releaseRawKeyRelease
GuiEventTarget.on-key-pressKeyPress
ポインタが解放されたスタティック イベント ハンドラ処理されるイベント
GuiEventTarget.on-pointer-buttonPointerButtonEvent
GuiEventTarget.on-pointer-crossingPointerCrossing
GuiEventTarget.on-pointer-enterPointerEnter
GuiEventTarget.on-pointer-envelope-eventPointerEnvelopeEvent
GuiEventTarget.on-pointer-eventPointerEvent
GuiEventTarget.on-pointer-leavePointerLeave
GuiEventTarget.on-pointer-motionPointerMotion
GuiEventTarget.on-pointer-pressPointerPress
GuiEventTarget.on-pointer-releasePointerRelease
キーボードに関連するスタティック イベント ハンドラ処理されるイベント
GuiEventTarget.on-drag-enterDragEnter
GuiEventTarget.on-drag-leaveDragLeave
GuiEventTarget.on-drag-overDragOver
GuiEventTarget.on-drag-pointerDragPointerEvent
GuiEventTarget.on-drag-startedDragStarted
GuiEventTarget.on-dropDrop
ダイアログに関連するスタティック イベント ハンドラ処理されるイベント
GuiEventTarget.on-commitCommit
GuiEventTarget.on-resetReset

GuiEvent の消費

スタティックおよびダイナミックの両方のイベント ハンドラ持つ目標オブジェクトでイベントが起動されると、スタティック イベント ハンドラの前にダイナミック イベント ハンドラが実行され、最後に追加されたダイナミック イベント ハンドラが最初に実行されます。
ダイナミック イベント ハンドラのすべてが実行を完了すると、イベントで該当するスタティック イベント ハンドラが呼び出されます。スタティック ハンドラはクラス階層と逆の順序で呼び出されます。そのタイプのイベントに最も特有のハンドラが最初に呼び出され、次にイベントのスーパークラスのハンドラ、さらにイベント階層の上位のイベントが順に呼び出されます。イベント クラスの 1 つをサブクラス化してスタティック イベント ハンドラをオーバーライドする場合、この規則に従う必要があります。
イベント ハンドラは、イベントに対して GuiEvent.consume メソッドを呼び出すことによって、GuiEvent消費します。各 GuiEvent ハンドラは、処理しているイベントの GuiEvent.consumed? プロパティを常にチェックし、既に消費されたイベントに反応しないようにする必要があります。ハンドラは、イベントの処理を終了後にそのイベントを消費して、その後のイベント ハンドラが処理が完了したことを確認できるようにすることが重要です。
ポインタ イベント」のセクションでは、目標のダイナミックおよびスタティック イベント ハンドラがすべて呼び出された後に、目標オブジェクトのグラフィカルな親でポインタ イベントが再起動され、上記のシーケンスが新しい目標について繰り返されることを説明します。グラフィカル階層全体の上部に達するまでイベント ハンドラが呼び出されるような連鎖を停止する唯一の方法は、e.consume を呼び出してイベントが消費されるようにすることです。
次の例では、イベントの処理と消費を説明します。e.consume を含む行を取り除くか、別のイベント ハンドラに挿入してみてください。

例: PointerPress イベントの消費
{HBox
    {on PointerPress do
        {popup-message "in the HBox"}
    },
    {Fill
        width = 2cm,
        height = 2cm,
        background = "red",
        {on e:PointerPress do
            {if e.consumed? then
                {popup-message "consumed"}
             else
                {popup-message "handler 3"}
            }
        },
        {on e:PointerPress do
            {if e.consumed? then
                {popup-message "consumed"}
             else
                {popup-message "handler 2"}
            }
            {e.consume}
        },
        {on e:PointerPress do
            {if e.consumed? then
                {popup-message "consumed"}
             else
                {popup-message "handler 1"}
            }
        }
    }
}

GUI ウィンドウと入力イベント

GuiWindowEvent は、ウィンドウとフォーカスを扱うイベント クラスのスーパークラスです。GuiInputEvent は、ポインタとキーボードのイベントを扱うクラスのスーパークラスです。次のクラス階層はウィンドウと入力イベントに関するクラスを示しています。
Event
GuiEvent
GuiWindowEvent
DestroyRequested
DestroyNotify
ViewDeactivateEvent
ViewResizeEvent
ViewMoveEvent
ViewVisibilityEvent
CancelMode
ViewActivateEvent
WindowClose
InputMethodEvent
EndCompositionEvent
StartCompositionEvent
CompositionChangeEvent
CompositionResultEvent
FocusEvent
FocusIn
FocusOut
GuiInputEvent
PointerEvent
PointerCrossing
PointerEnvelopeEvent
KeyEvent
GuiInputEvent はユーザーのアクションが発生した画面上の場所などの情報、およびユーザーが Shift キーまたは Alt キーなどを押したかどうかを追跡する必要があります。そのため、GuiInputEvent オブジェクトには GuiWindowEvent クラスから継承したものの他に次のプロパティがあります。
プロパティタイプ意味
GuiInputEvent.xintイベントが起動された場所の水平オフセット
GuiInputEvent.yintイベントが起動された場所の垂直オフセット
GuiInputEvent.has-coordinates?boolxy の座標は GuiInputEvent にとって重要なので常に true を返します。
GuiInputEvent.state-maskStateMaskイベント時のマウス ボタンの状態を示す [32] フラグと修飾キーのセット。このマスクでもっとも頻繁に要求されるデータは修飾キーに関するもので、次に示すように直接クエリすることができます。
GuiInputEvent.shift?boolイベント時に Shift キーが押されたか
GuiInputEvent.ctrl?boolイベント時に ctrl キーが押されたか
GuiInputEvent.alt?boolイベント時に alt キーが押されたか
上記のリストに記載されているプロパティを使用して、GuiWindowEvent のレポートにより、ボタンの押しなどの活動時に収集された情報にアクセスできます。イベントが処理されるまでに、これらのプロパティで異なる値を持つ他のイベントが発生することがありますが、前のイベントを適切に処理するために元のデータが不可欠な場合があります。
GuiWindowEvent.event-time が提供する event-time カウンタは単調に増加しますが、ときおりオーバーフローすると再びゼロに戻ります。2 つのイベントの間に時間カウンタの折り返しがなかった場合、2 つの異なる GuiWindowEvent を記録した時間値を減算して 2 つの活動が開始された時刻の間隔を識別します。Microsoft® Windows および X Windows の両方で、イベント時間数はミリ秒ごとに増加します。
state-mask は、イベントが起動されたときのマウス ボタンとキーボード修飾キーの状態をレポートします。
xy プロパティで、イベントが起動されたときのポインタの場所を目標オブジェクトの座標空間に示します。つまり、xy は、目標オブジェクトの 内部起点への相対イベント座標のペアです。これらはフラグ GuiWindowEvent.has-coordinates? が TRUE を返すイベントのみに対して適用されます。たとえば、PointerMotion イベントには該当しますが、FocusOut イベントには該当しません。
次の例は、修飾キー プロパティを 2 つ使用して、長方形の上でポインタが押されたときにその色を設定するものです。選択される色は、マウス ボタンを押しながら保持していたキーボードの修飾キーによって異なります。

例: 修飾キー プロパティのチェック
{RectangleGraphic width=2in, height=0.5in,
    {on e:PointerPress at rectangle:RectangleGraphic do
        {if e.shift? then
            set rectangle.fill-color = "red"
         elseif e.ctrl? then
            set rectangle.fill-color = "lime"
         else
            set rectangle.fill-color = "silver"
        }
    }
}
ポインタ イベントは、「ポインタ イベント」のセクションで説明されているように、グラフィカル階層を介してイベント座標を含む最下層の可視オブジェクトに送られます。オブジェクトでポインタ イベントが起動されると、イベント ハンドラがイベントを消費するまでグラフィカル階層の上位へ向かって各オブジェクトが再起動されます。
その一方で、キーボードフォーカスがあるオブジェクトにおいて、キーボードイベントは、 View によって開始されます。各 View には、どのオブジェクトにキーボードフォーカスがあるかを把握する FocusManager が含まれています。 KeyPress されているオブジェクトで KeyPress が消費されない場合、さらに、ファンクションキー (F1、F2等)、Ctrl、 Alt、メニュー変更キーが押された場合は、イベントは、ショートカットまたはメニューアクセスキーである場合に備えて、ブラウザに転送されます。
フォーカスロケーションの詳細に関しては、この章の後半にある、 「フォーカスとキーイベント」 を参照してください。

ウィンドウを閉じるイベント

WindowClose イベントは GuiWindowEvent のサブクラスで、View.close メソッドが開始されたとき、またはホスト ウィンドウ マネージャの「ウィンドウを閉じる」操作がユーザーによって開始された時に View で起動します。WindowClose イベントが イベント ハンドラによって消費されていない場合、ウィンドウが最終的に破棄されます。ただし、イベント ハンドラが WindowClose イベントを消費する場合は、View の破棄は抑えられます。
この機能の一般的な用法の 1 つとして、ユーザーが本当にウィンドウを閉じたいのかどうかを確認するプロンプトの表示が挙げられます。ほかには、ユーザーがウィンドウを閉じるよう要求したとき、ウィンドウを実際に破棄する代わり非表示にする場合です。ユーザーが後でウィンドウの再表示を求めた場合、再作成の代わりに View.show を使用して表示できます。次の例では、この方法によるポップアップ ウィンドウの管理を示します。

例: WindowClose イベントの処理
{value
    let popup:View =
        {View
            {Frame margin=12pt,
                   {text This is a popup window.}
            },
            {on e:WindowClose at v:View do
                {v.hide}
                {e.consume}
            }
        }
    let button:CommandButton =
        {CommandButton label = "Show Popup",
                {on Action do {popup.show}
                }
        }
    button
}