ポインタ イベント

要約:
  • グラフィカル ユーザー インターフェイス (GUI) は、ユーザーからの情報をマウス ポインタ、マウス ボタンの挙動およびタッチデバイスへの操作を通して取得します。
PointerEvent は、次のクラスの共通のスーパークラスです。
これらのイベントのほかに、1 つのコンテナ内の異なるグラフィック オブジェクト間をポインタが横切ることで発生するポインタ イベントのタイプがあります。これらは PointerCrossing クラスに含まれ、ポインタがグラフィック オブジェクトの領域を出入りする際にトリガされるイベント (PointerEnter および PointerLeave) と、ポインタが親グラフィック オブジェクトが占有する領域内にありながら、子オブジェクトに出入りする際にトリガされるイベント、つまり、階層的なポインタ交差イベント PointerLeafEnterPointerLeafLeave に区別されます。
クラス階層の一部を次に示します。
GuiWindowEvent
GuiInputEvent
PointerEnvelopeEvent
KeyEvent
RawKeyEvent
KeyPress
PointerEvent
PointerNonevent
PointerScroll
ContextMenuEvent
DragStarted
DragPointerEvent
Drop
DragOver
DragPointerNonevent
PointerMotion
PointerButtonEvent
PointerPress
PointerRelease
GestureEvent
GestureBegin
GestureEnd
GesturePan
GestureMagnify
GestureRotate
GestureSwipe
GestureTap
GestureTouch
PointerCrossing
PointerEnter
PointerLeave
PointerLeafEnter
PointerLeafLeave

ポインタ イベントの発生

概念上は、グラフィック オブジェクトがある点 (x,y) にピクセルを描くと、その点でグラフィック オブジェクトがイベントを処理するといえます。(実際には、Graphic のサブクラスが Graphic.contains-point? をオーバーライドしてこの問題に対処します。)これは、一般的にマウス ボタンを押すとマウス ポインタの位置に表示されるオブジェクトに PointerEvent が送られることを意味します。ただし、ローカル オプション Graphic.opaque-to-events?true の場合、PointerEvent (または他の任意のイベント) は、それがオブジェクトの透過部分で発生した場合でも処理されます。
次の例では、画像を背景として Canvas が表示されています。キャンバス内には、目、鼻、口の上に目に見えない 4 つの Frame があります。Canvas には、マウスの位置を左上端から mm (ミリメートル) の単位でレポートするイベント ハンドラがあります。透明な各 Frame はイベントに対して不透明になり、マウスの位置をレポートするためのイベント ハンドラがアタッチされています。

例: 透過オブジェクト上でのポインタ イベントの発生
{let f1:Frame = {Frame background="beige", width=2cm, height=.5cm}}
{let f2:Frame = {Frame background="beige", width=2cm, height=.5cm}}
{let c:Canvas =
    {Canvas background={url "curl://install/docs/default/images/adria.jpg"},
        width=2cm, height=2cm,
        {on e:PointerMotion do
            {f1.add {format "(%.1f, %.1f)", e.x/1mm, e.y/1mm}, replace?=true}
        }
    }
}
{value
    {c.add x=6mm, y=8mm,
        {Frame width=3mm, height=3mm, opaque-to-events?=true,
            {on e:PointerMotion do {f2.add "left eye", replace?=true}}
        }
    }
    {c.add x=10mm, y=8mm,
        {Frame width=3mm, height=3mm, opaque-to-events?=true,
            {on e:PointerMotion do {f2.add "right eye", replace?=true}}
        }
    }
    {c.add x=8mm, y=10mm,
        {Frame width=3mm, height=3mm, opaque-to-events?=true,
            {on e:PointerMotion do {f2.add "nose", replace?=true}}
        }
    }
    {c.add x=7mm, y=12mm,
        {Frame width=5mm, height=3mm, opaque-to-events?=true,
            {on e:PointerMotion do {f2.add "mouth", replace?=true}}
        }
    }
    {VBox f1, f2, c}
}
ある位置 (x,y) で PointerEvent が発生した場合、(x,y) の位置を含む、一番内側で一番上にある可視グラフィック オブジェクトに送られます。このイベント ターゲットにダイナミック イベント ハンドラが存在する場合、イベント ハンドラはオブジェクトに最後に加えられたイベントから実行し始めます。次に、任意のスタティック イベント ハンドラが実行されます。イベントを消費するハンドラがなければ、グラフィカル コンテナをターゲットとしてプロセスが繰り返されます。これは、イベントが消費される (GuiEvent.consume を呼び出すことにより) か、または階層のルートに達するまで、グラフィック階層の上位に向けて続けられます。
次の例では、3 重にネストされた Frame を取り上げます。マウス ボタンをクリックするたびに、イベント ハンドラがクリックしたオブジェクトの色をレポートします。
注意: 一番内側の Frame は空で背景はなく、濃い黄色の境界があります。中の Frame には背景として画像が置かれています。黄色 (一番内側) のフレームの内部に空の空間があるため、そこに画像が見えています。この中間のフレームは、画像の任意の表示範囲でイベントを処理します。opaque-to-events?true に設定する行を非コメント化し、Execute ボタンを押すと、一番内側の Frame のこのオプションを変更したことによる効果を確認することができます。黄色の Frame が、画像が見えている部分を含めて内部のすべてのイベントを処理します。また、一番内側のフレームの上の e.consume 呼び出しを削除してその効果を確認することもできます。

例: ネストされたフレームの中のポインタ イベント
{let r:HBox = {HBox spacing=3pt, "Events:"}}
{value r}{br}
{Frame margin=.125in, background="red",
    {Frame margin=.125in,
        background={url "curl://install/docs/default/images/adria.jpg"},
        {Frame width=.6in, height=.6in,
            || opaque-to-events?=true,
            border-width=.125in, border-color="yellow",
            {on e:PointerPress do
                {r.add "Yellow"}
                {e.consume}
            }
        },
        {on e:PointerPress do
            {r.add "Picture"}
            {e.consume}
        }
    },
    {on e:PointerPress do
        {r.add "Red"}
        {e.consume}
    }
}
最初のオブジェクトへのポインタ イベントを取得するためのプロセスは、イベント転送と呼ばれています。このポインタ イベントは最初のオブジェクトで発生します。ポインタ グラブがアクティブでなければ、イベント座標が内部に存在する View の一番上のグラフィック オブジェクトから、PointerEvent はグラフィック階層を下位方向に転送されていきます。この Graphic を起点として、イベントはグラフィック階層の下位に転送され、そのときに次の条件を満たすすべてのグラフィック オブジェクトを通過します。
この条件を満たすと同時に同じ条件を満たす子を持たないオブジェクトに到達すると イベントがそのターゲット オブジェクトで発生します。これが一番奥の最上位にある可視の Graphic です。
イベント転送は、PointerEnvelopeEvent クラスを使用して実装されます。PointerEvent が処理されるたびに PointerEnvelopeEvent が作成されます。PointerEnvelopeEvent 内には PointerEvent が存在し、上記で説明したように伝送されます。各グラフィカル コンテナは、適切な場合に PointerEnvelopeEvent をその子の 1 つに転送します。それ以外の場合は、エンベロープが開封されて中のイベントが発生します。

可視性に影響を及ぼすプロパティ

Graphic の可視性に影響を及ぼすいくつかのオプションがあります。Graphic.visible? オプションは既定値が true で、おそらくもっとも分かりやすいオプションです。このオプションが false の場合、オブジェクトは配置されますがペイントされません。したがって PointerEvent のターゲットにはなりません。
グラフィック オブジェクトは、その背景を描く必要がありません。これはローカル オプション Graphic.background により決定されます。グラフィック オブジェクトに背景色または背景パターンを明示的に設定しない場合、既定で背景は透明になり、背景パターンが割り当てらた場合はそれを表示するオブジェクトのどの領域にも、オブジェクトによるペイントは行われません。結果として、グラフィックの親の背景色など、他のオブジェクトの一部が透けて表示されることがあります。
ローカル ブール値オプション Graphic.opaque-to-events? は、オブジェクトを視覚的に透明の状態にしてオブジェクトで発生するイベントに応答させる場合のために提供されています。このオプションの既定値は false です。これは、オブジェクトの透明部分でポインタ イベントが発生した場合、既定ではオブジェクトがイベントを無視することを意味します。オブジェクトで発生するイベントに対してオブジェクトの透明部分で応答できるようにする場合は、その opaque-to-events? オプションを true に設定する必要があります。
次のセクションでは、ポインタ イベントがターゲット オブジェクトで消費されているか、またはターゲット オブジェクトのグラフィカルな親で再び発生するかを確認できる例を示します。

ポインタのモーション イベント

PointerMotion は、ポインタ クリックとは対照的に、特定のオブジェクト全体におけるポインタの働きに関係するイベントを表わす PointerEvent のサブクラスです。
オブジェクト上でポインタを動かすと、PointerMotion イベントがそのオブジェクトでインスタンス化され発生します。PointerMotionPointerEvent サブクラスなので、このイベントは消費されるまでグラフィック階層の上位に転送されて他のオブジェクトで発生します。
次の例では、RectangleGraphic は、Frame の境界領域で囲われ、2 つのオブジェクトはそれぞれの色を変更するための、PointerMotion イベントのイベントハンドラを持っています。
ポインタを RectangleGraphic 上で移動させると RectangleGraphic の色の塗りつぶしと、Frame の境界の色の変更が両方とも起こることに注意してください。これは、PointerMotion イベントがグラフィック階層を上に移動しながら、RectangleGraphic で最初に発生し続いて Frame で再び発生するからです。
ただし、RectangleGraphic 内でポインタを移動している間に Ctrl キーを押すと、RectangleGraphic に属するイベント ハンドラがイベントを消費します。この場合、イベントはグラフィカルな親である Frame で発生せず、Frame にアタッチされているイベント ハンドラは実行されません。したがって、Frame の色は変化しません。

例: ポインタのモーション イベント
{value
    let rect:RectangleGraphic =
        {RectangleGraphic width=64pt, height=64pt,
                   || attach an event handler to the RectangleGraphic
                   {on e:PointerMotion at r:RectangleGraphic do
                       let x-value:int = {max 0, {min 255, 4 * e.x/1pt}} asa int
                       let y-value:int = {max 0, {min 255, 4 * (-e.y/1pt)}} asa int
                       || Change fill color of RectangleGraphic
                       set r.fill-color =
                           {Color.from-string
                               {format "#%02x%02x00", x-value, y-value}}
                       || Checks if the Ctrl key is pressed
                       || If so, it consumes the event
                       {if e.ctrl? then {e.consume}}}}
    let frame:Frame =
        {Frame
            border-width=18pt,
            rect,
            || attach an event handler to the Frame
            {on e:PointerMotion at f:Frame do
                let x-value:int = {max 0, {min 255, 4 * e.x/1pt}} asa int
                let y-value:int = {max 0, {min 255, 4 * (-e.y/1pt)}} asa int
                || Change border color of Frame
                set f.border-color =
                    {Color.from-string
                        {format "#00%02x%02x", x-value, y-value}}}}
    frame}

ポインタのボタン イベント

PointerPress および PointerRelease はマウス ボタンの押しと離しをレポートします。PointerButtonEvent はこれらのクラスのスーパークラスになります。
PointerButtonEvent には、イベント ハンドラ内でクエリできる 2 つの新しいイベント属性が導入されています。
pbe:PointerButtonEvent.button:int
pbe:PointerButtonEvent.click-count:int
PointerButtonEventbutton 属性には、状態が変化したマウス ボタンの数が含まれます。通常、左マウス ボタンの押しや離しによって発生するポインタのボタン イベントには 1button 属性を持ち、2 または 3 個のボタンを持つマウスの右ボタンを使用すると通常 3button 属性になり、さらに button 2 は 3 つボタンマウスの中のボタンを参照することに注意してください。 定値 left-buttonright-button、そして middle-button をそれぞれの値に使用することもできます。
click-count を調べてシングル クリック イベントと複数回のクリック イベントを区別することができます。PointerPress イベントで、この属性は現在の 1 回を含めてポインタ クリックが発生した回数を数えます。時間的に十分近い場合は複数回のクリック操作の一部とみなされます。したがって、ダブル クリックの操作では 2 つの PointerPress イベントが発生し、最初のイベントは 1 の click-count、2 番目のイベントは 2 の click-count を持ちます。ただし PointerRelease イベントに関する click-count 属性は常に 0 です。
次の例の RectangleGraphic の横幅は、左マウス ボタン (またはマウス ボタン番号 1) を押すという条件で、シングル クリックすると 2 インチ (5.08 センチ) になり、ダブルクリックすると 1 インチ (2.54 センチ) になります。ダブルクリックの操作では、最初にシングルクリック イベント、次にダブルクリック イベントが生成される様子に注意してください。

例: シングル、ダブルのポインタ ボタン クリック
{RectangleGraphic width=1in, height=0.5in,
    {on e:PointerPress at r:RectangleGraphic do
        {if e.button == left-button then
            {if e.click-count == 1 then
                set r.width = 2in
             else
                set r.width = 1in
            }
        }
    }
}

ジェスチャー イベント

GestureEvent は、「タッチ」 「マルチタッチ」と呼ばれるような タッチインタフェース に対する色々なジェスチャーのスーパークラスとして機能します。
GestureEvent は適切なハードウェアとオペレーティング システムのサポートがなければ生成されません。オペレーティング システムのジェスチャーに対する標準サポートは、マウスのサポートに比べ歴史が浅く、そのため各オペレーティング システムやバージョンの違いによるサポートする範囲の違いが存在します。
GestureEvent は連続して発生することが多く、特に GestureRotateGestureMagnify のような値が変化するジェスチャーに対して、ユーザが期待した値になるまで操作を続けるような場面でよく見られます。基本的に、全ての GestureEventGestureBegin に続けて発生し、その後 GestureEnd で終わります。ただし、 GestureBeginGestureEnd 自身を除きます。その他の色々な GestureEventGestureBeginGestureEnd の間に発生しますが、全てがそのような挙動ではありません。例えば GestureBegin - GestureMagnify - GestureMagnify - GestureMagnify - GestureRotate - GestureEnd のような流れをとることもあります。
ジェスチャーイベントは PointerMotionPointerPress のようなポインターイベントと混じる場合がよくあることに注意してください。このことにより、アプリケーションはジェスチャー サポートなしでの動作が可能になります。しかしながら、このようにイベントが混じることで、ジェスチャーイベントのみ考慮していた開発者にとって思ってもみないような結果を生み出すことがあります。例えば、ドラッグ操作が非ジェスチャーの PointerEvent によって発生する場合があります。ジェスチャーイベントを起こしたいグラフィック オブジェクトに対しては、GestureBegin 上でポインタをグラブさせるよう実装するとよい結果が得られることが多いでしょう。
下記の例では、Frame 内部の上でのジェスチャーが続いている間背景が緑に変化し、そのジェスチャーの型の名前が表示されます。新しいジェスチャーが発生すると、前の型の名前はクリアされます。

例: ジェスチャー イベント
{Frame
    background = "yellow",
    border-width = 18pt,
    {Frame
        width = 4in,
        height = 72pt,
        border-width = 1pt,
        border-color = "black",
        opaque-to-events? = true,
        {on e:GestureBegin at f:Frame do
            {e.continue-implicit-pointer-grab f}
            {f.clear}
            set f.background = "green"
        },
        {on e:GestureEvent at f:Frame do
            {if not (e isa GestureBegin) and not (e isa GestureEnd) then
                {f.add replace? = true, {String e}}
            }
        },
        {on GestureEnd at f:Frame do
            {unset f.background}
        }
    }
}
一般的に、個別のコンタクトを高レベルのジェスチャーイベントに変換する方法は各オペレーティング システムに依存しますが、それらの操作は厳密にどのユーザーの操作がそのイベントを生成するかといった詳細を要求しません。プラットフォームの中にはタッチデバイスのどこでコンタクトが発生したかの通知をを受け取ることができます。例えばマルチタッチデバイスでの指の位置情報等がそれに当たります。詳細は TouchInputMode を参照してください。

ポインタの交差イベント

要約:
  • グラフィカル ユーザー インターフェイス (GUI) は、ポインタがグラフィカルな境界を横切る際にユーザーから情報を得ます。
GuiWindowEvent のサブクラス PointerCrossing は、ポインタが境界を横切っているとき、すなわち、グラフィック オブジェクトで覆われた画面の領域に入るときまたは出ていくときにポインタの動きをレポートするイベントのスーパークラスです。これらのイベントには次の 2 種類があります。階層的およびリーフ状交差イベントです。クラス階層の一部を次に示します。
GuiInputEvent
PointerCrossing
PointerEnter
PointerLeave
PointerLeafEnter
PointerLeafLeave

階層交差イベント

階層交差イベントは、PointerEnter および PointerLeave です。これらのイベントは、g で覆われた領域にポインタが出入りするときにオブジェクト g で発生します。
g がグラフィカルな子 c を持ち、ポインタが c で覆われた領域に入る場合、PointerLeaveg で発生しません。これは、g の子で覆われた領域は g で覆われていると見なされるためです。
したがって、次の図でポインタが領域 1 から領域 2 へ移動する場合、PointerEnterg で発生します。ポインタが領域 2 から領域 3 へ移動する場合、ポインタの交差イベントは g で発生しません。
領域 1
g
領域 2
c
領域 3

リーフ交差イベント

リーフ交差イベントは PointerLeafEnter および PointerLeafLeave で表わされます。これらのイベントは、g で覆われているが g の子孫には覆われていない領域にポインタが出入りするときにオブジェクト g で発生します。
g がグラフィカルな子 cを持ち、ポインタが c で覆われた領域 (上図の領域 2 から 3 まで) に入る場合、gPointerLeafLeave が発生します。ポインタが出た領域が子に覆われていないからです。
ポインタが c で覆われた領域を出て、c で覆われていない g 上の領域 (上図の領域 3 から 2 まで) に入る場合、PointerLeafEnterg で発生します。ポインタが入った領域が子に覆われていないからです。
一般的に、リーフ交差イベントは階層交差イベントほど有用ではありません。

ポインタ交差の要約および例

次の表は、ポインタがグラフィカルな親から子へ移動するときに発生するポインタの交差イベントの要約を示しています。
ポインタ交差PointerCrossing g で発生するイベントPointerCrossing c で発生するイベント
領域 1 から領域 2PointerEnter, PointerLeafEnterなし
領域 2 から領域 3PointerLeafLeavePointerEnter, PointerLeafEnter
領域 3 から領域 2PointerLeafEnterPointerLeave, PointerLeafLeave
領域 2 から領域 1PointerLeave, PointerLeafLeaveなし
任意のオブジェクト g で発生する階層交差イベントは常にペアで発生し、PointerLeave は常に PointerEnter に続き、PointerEnter は常に PointerLeave に先行します。リーフ交差イベントも同様にペアで発生します。
次の例では、色付きの FrameRectangleGraphic の上にポインタを移動すると、Frame の右にあるテキスト凡例が変化して、発生するさまざまなタイプの PointerCrossing イベントが表示されます。

例: ポインタの交差イベント
{let frame-inside-status:Visual = {text-part not\ }}
{let frame-leaf-status:Visual = {text-part not\ }}

{let test-frame:Frame =
    {Frame
        border-width=12pt,
        border-color="cyan",
        {RectangleGraphic width=36pt, height=18pt},
        {on PointerEnter do
            {frame-inside-status.clear}
            {frame-inside-status.add ""}
        },
        {on PointerLeave do
            {frame-inside-status.clear}
            {frame-inside-status.add "not "}
        },
        {on PointerLeafEnter do
            {frame-leaf-status.clear}
            {frame-leaf-status.add ""}
        },
        {on PointerLeafLeave do
            {frame-leaf-status.clear}
            {frame-leaf-status.add "not "}
        }
    }
}


{HBox
    spacing=12pt,
    valign="center",
    test-frame,
    {VBox spacing=3pt,
        {text We are {value frame-inside-status}somewhere inside the Frame},
        {text We are {value frame-leaf-status}directly over the Frame}
    }
}

ポインタ グラブ

要約:
  • ポインタ グラブとは、ポインタが解除されるまでグラブ オブジェクトと呼ばれる特別なオブジェクトがすべての PointerEvent 発生のターゲットになるという状態を指します。
  • システムでは、マウスを使った暗黙的な方法、または特別な Gui Manager のメソッドを呼び出す明示的な方法でポインタをグラブできます。
通常は、ユーザーが画面上のある位置をクリックすると、表示されているオブジェクトで PointerEvent が発生します。これを起点として、グラフィック階層の上位に向けて PointerEvent が発生していきます。このルールの例外は、ポインタ グラブが有効な場合です。ポインタ グラブとは、グラブ オブジェクトと呼ばれる特別なオブジェクトがポインタのグラブが終了するまですべての PointerEvent 発生のターゲットになるという状態を示します。
Curl® 言語には、グラブ オブジェクトのポインタをグラブする方法として、暗黙的と明示的な 2 つのポインタ グラブがあります。
これら 2 つの組み込み型のポインタ グラブのメカニズムにより、ターゲットになるオブジェクトに対して適切なタイミングでポインタがグラブされます。同様に、ポインタ グラブも適宜に終了します。
たとえば Scrollbar の上でマウス ボタンを押し、レイアウト境界を超えてポインタをドラッグすると、まるでポインタが Scrollbar の上にあるかのようにページがスクロールを続けます。これは、Scrollbar のポインタがグラブされているからです。マウス ボタンを離すと、システムが自動的にポインタ グラブを終了するため、スクロールの動作は終了して Scrollbar はグラブ オブジェクトでなくなります。Scrollbar で使用された組み込み型のポインタ グラブ メカニズムは、暗黙的なメカニズムです。
特定のグラフィック オブジェクトに関して、ユーザーがマウス ボタンを離してもグラブ オブジェクトのターゲットであることを自動的に終了したくない場合があります。むしろ、ユーザーが特定のオブジェクト上でマウス ボタンを押したときにグラブを解除する必要がある場合があります。メニューなどを含めて、このような場合は明示的なポインタ グラブメカニズムを代わりに組み込みます。
注意: ポインタ グラブ メカニズムはプラットフォーム (オペレーティング システム) によって多少異なります。したがって、システムがオブジェクトのポインタをグラブすると、後続のすべての PointerEvent が通常のターゲットではなくグラブ オブジェクトで発生するという記述は常に正しいとは限りません。
たとえば、Microsoft® Windows® では、ポインタがグラブ オブジェクトのウィンドウのスレッドとは異なるスレッドを持つウィンドウ上にある場合、ポインタ イベントはグラブ オブジェクトではなく通常のターゲットで発生します。一方 X Windows では、ポインタ イベントは常にグラブ オブジェクトで発生します。
次のセクションでは、暗黙的および明示的なポインタ グラブの詳細について説明します。

暗黙的なポインタ グラブ

グラフィック オブジェクトで暗黙的なポインタ グラブが使用されるのは次の場合です。
それらの各メソッドでは、グラブ オブジェクトは引数として指定されます。
独自のグラフィック クラスを記述しない限り、on-pointer-presson-drag-started 、および on-gesture-begin のスタティック イベント ハンドラを変更できません。ただし、メソッドを呼び出すダイナミック イベント ハンドラを記述してグラブ オブジェクトにアタッチすることができます。
暗黙的なポインタ グラブに至るまでに発生する典型的な一連のイベントを次に説明します。これらは暗黙的なポインタ グラブ保留段階を構成し、暗黙的なポインタ グラブが起こる前提条件となります。
ユーザー アクションシステムの動作
1.ユーザーはアプレット ウィンドウのポイント (x, y) でマウス ボタンを押し、そのままにします。オペレーティング システムでは、PointerPress イベントがユーザーが見ることのできる (x, y) 上の最上位のオブジェクトで発生します。(以下、このオブジェクトを Topmost (最上位) と呼びます。)
2.ユーザーはマウスをドラッグするか (通常)、または別のマウス ボタンを押します。
GuiManagerDragStarted または PointerPress イベントを Topmost で発生させます。
DragStarted または PointerPress に対する Topmost のイベント ハンドラが起動して、イベントの continue-implicit-pointer-grab メソッドを呼び出します。
GuiManagerTopmost のポインタをグラブします。
先に説明したように、暗黙的なポインタ グラブが実際に発生するためには、グラブ オブジェクトを引数として指定し、PointerPressDragStarted 、または GestureBegin イベントのイベント ハンドラの 1 つで continue-implicit-pointer-grab を呼び出す必要があります。
これにより、(GuiManager 経由で) 指定されたグラブ オブジェクトへのポインタがグラブされ、すべての後続する PointerPress イベント (ユーザー操作の結果として発生) が通常のターゲットではなくグラブ オブジェクトで発生することになります。グラブ オブジェクトを最上位のオブジェクトに指定できますが、その必要はないという点に注意してください。
たとえば、Scrollbar.on-pointer-press のコードの理論の一部を次に示します。これは PointerPress イベントのスタティック イベント ハンドラで、ユーザーがマウス ボタンを押したとき (上で説明) に Scrollbar が最上位で表示されているオブジェクトである場合に実行されます。
{method public {on-pointer-press e:PointerPress}:void

    || other code ...

    {e.consume}
    || Initiates implicit pointer grabbing, naming itself as the grab object
    {e.continue-implicit-pointer-grab self}

    || other code ...

    {super.on-pointer-press e}
}
暗黙的なポインタ グラブ保留の段階に続き、上記の continue-implicit-pointer-grab メソッドを Topmost.on-drag-started イベント ハンドラで呼び出す必要があります。一連のイベントは次のように続きます。
ユーザー アクション (続く)システムの動作 (続く)
3.ユーザーは (マウス ボタンを押したまま) ポインタを移動します。すべての PointerEvent イベントが Topmost で発生します。
4.ユーザーはマウス ボタンを離します。GuiManager はポインタを解除し、その結果 Topmost はグラブ オブジェクトでなくなり、ポインタ グラブが終了します。
GrabRelease イベントが Topmost (元のグラブ オブジェクト) で発生します。
暗黙的なポインタ グラブのコードがオブジェクトの PointerPressDragStarted 、または GestureBegin イベント ハンドラに存在しない場合、最上位のオブジェクトはそれ自体を含め、いかなるオブジェクトもグラブ オブジェクトにすることができず、暗黙的なポインタ グラブは起こりません。
オブジェクトが暗黙的なポインタ グラブを介してグラブされた場合、ユーザーがすべてのマウス ボタンを離すか、進行中のタッチ ジェスチャーを終了させると、自動的に PointerEvent のターゲットではなくなり、その時点で暗黙的なポインタ グラブが終了します。ただし、該当するイベント ハンドラの PointerPressDragStarted 、または GestureBegin イベント上で release-implicit-pointer-grab メソッドを呼び出すことにより、暗黙的なポインタ グラブを "手動で" で終了させることもできます。
一般的に、ユーザーがマウス ボタンを離すにグラブ オブジェクトがポインタを解除する必要がなければ、release-implicit-pointer-grab を呼び出す必要性はありません。
また、ポインタ グラブが終了すると GrabRelease イベントが "元の" グラブ オブジェクトで発生します。GuiManager がポインタを解除するたびに特定のコードを実行する場合は、イベント ハンドラをオブジェクトにアタッチして GrabRelease を処理することができます。

暗黙的なポインタ グラブの例

RectangleGraphic を含む Frame の例を次に示します。ポインタ グラブが起こらない場合は、次のように動作します。
Frame には次のダイナミック イベント ハンドラがアタッチされています。
{on e:PointerPress at f:Frame do
    {e.continue-implicit-pointer-grab f}
}
ユーザーが Frame 上で (RectangleGraphic で覆われている任意の位置を含めて) マウス ボタンを押すと、GuiManagerFrame をグラブ オブジェクトに指定して暗黙的なポインタ グラブを開始します。続いて、RectangleGraphic で発生するはずの PointerEvent イベント (PointerMotion はこのイベントのサブクラス) は、変わって Frame で発生します。したがって、RectangleGraphic の色はユーザーがマウス ボタンを離すまで "凍結" しています。
さらに、Frame には GrabRelease のダイナミック イベント ハンドラがアタッチされています。このイベント ハンドラはポインタ グラブが解除されるときに実行され、GuiManager は元のグラブ オブジェクトである FrameGrabRelease イベントを発生します。

例: 暗黙的なポインタ グラブ
{value
    let rect:RectangleGraphic =
        {RectangleGraphic
            width=64pt,
            height=64pt,
            || The RectangleGraphic color changes as the pointer moves
            || over the RectangleGraphic because PointerMotion fires at rect
            {on e:PointerMotion at r:RectangleGraphic do
                let x-value:int = {max 0, {min 255, 4 * e.x/1pt}} asa int
                let y-value:int = {max 0, {min 255, 4 * (-e.y/1pt)}} asa int
                set r.fill-color =
                    {Color.from-string
                        {format "#%02x%02x00", x-value, y-value}}
                {if e.ctrl? then {e.consume}}
            }
        }
    let frame:Frame =
        {Frame
            border-width=18pt,
            rect,
            || The border color of the Frame changes as the pointer moves
            || over it because PointerMotion fires at frame
            {on e:PointerMotion at f:Frame do
                let x-value:int = {max 0, {min 255, 4 * e.x/1pt}} asa int
                let y-value:int = {max 0, {min 255, 4 * (-e.y/1pt)}} asa int
                set f.border-color =
                    {Color.from-string
                        {format "#00%02x%02x", x-value, y-value}}
            },
            || When user presses mouse button over frame,
            || implicit pointer grabbing begins with frame
            || as the grab object
            {on e:PointerPress at f:Frame do
                {e.continue-implicit-pointer-grab f}
            },
            || When the implicit pointer grab ends,
            || a message pops up, stating so
            {on e:GrabRelease at f:Frame do
                {popup-message {bold The Frame has ungrabbed the pointer!}}
            }
        }
    frame
}

明示的なポインタ グラブ

明示的なポインタ グラブは高度な機能で通常は使用されません。Curl 言語のグラフィック クラスの中で、メニューとコンボ ボックスのコントロールがこのメカニズムを使用します。これは、ユーザーがすべてのマウス ボタンを離しても表示メニューを画面に残す必要があるからです。(暗黙的なポインタ グラブ メカニズムが使用された場合はユーザーがすべてのマウス ボタンを離すと GuiManager が自動的にポインタ グラブを解除することを思い出してください。)
重要: 明示的なポインタ グラブを開始する場合、ポインタ グラブの必要がなくなったら必ず終了しなければなりません。終了しない場合、画面上の他のオブジェクトが PointerEvent を受け取れないためワークステーション全体が使用できなくなることがあります。
グラブ オブジェクトを引数に指定してメソッド GuiManager.grab-pointer を呼び出し、明示的なポインタ グラブを開始します。
Curl 言語で記述されたアプレットを表示するたびに、1 つの Curl® ランタイム セッションを開始することになります。シングル セッションが 1 つ以上のウィンドウを開く場合があります。これらのさまざまなウィンドウは、複数のランタイム プロセスで管理されます。
ランタイムごとに 1 つの Gui Manager が存在し、GuiWindowEvent の伝達を制御してユーザーの動作を解釈します。Gui Manager は GuiManager クラスで表わされます。したがって、Gui Manager は grab-pointer メソッドを直接呼び出すことによって明示的なポインタ グラブを開始します。
my-grab-object の明示的なポインタ グラブを開始するコード行を例示します。
{{get-gui-manager}.grab-pointer my-grab-object}
明示的なポインタ グラブを終了するには次の 3 つの方法があります。
暗黙的なメカニズムを使用してポインタがグラブされた場合でも、release-pointer-grab によって同じようにポインタ グラブが終了します。
暗黙的なポインタ グラブの終了と同じように、明示的なポインタ グラブが終了すると GrabRelease イベントが元のグラブ オブジェクトで発生します。

暗黙的から明示的ポインタ グラブへの移行

GuiManager.grab-pointer を呼び出し、暗黙的なポインタ グラブによってグラブ オブジェクトになったオブジェクトを引数として指定すると、グラブ オブジェクトは明示的なポインタ グラブのグラブ オブジェクトになります。
これは、ユーザーがすべてのマウス ボタンを離してもポインタ グラブが解除されず、グラブ オブジェクトの状態であることを意味します。ポインタ グラブを終了するには、明示的なポインタ グラブを終了するためのメソッドの GuiManager.release-pointer-grab を呼び出します。
次の例では、ユーザーがすべてのマウス ボタンを離したときに暗黙的なポインタ グラブがどのように終了するかを示します。

例: 暗黙的なポインタ グラブの終了
{Frame
    width=1in, height=1in,
    background="green",
    {on pp:PointerPress at f:Frame do
        {pp.continue-implicit-pointer-grab f}
        set f.background="pink"
    },
    {on gr:GrabRelease at f:Frame do
        {set f.background="green"}
    }
}
次の例では、ユーザーがポインタをドラッグすると暗黙的なグラブを明示的なグラブに変換し、Alarm を作成します。作成されたアラームは、3 秒間待機してから明示的なポインタ グラブを終了します。

例: 暗黙的なグラブから明示的なグラブへの変換
{value let frame:Frame=
    {Frame
        width=1in, height=1in,
        background="green",
        {on pp:PointerPress at f:Frame do
            {pp.continue-implicit-pointer-grab f}
            set f.background="pink"
        },
        {on ds:DragStarted at f:Frame do
            {{get-gui-manager}.grab-pointer f}
            {Alarm
                delay=3s,
                {proc {}:void
                    {{get-gui-manager}.release-pointer-grab frame}
                }
            }
        },
        {on gr:GrabRelease at f:Frame do
            {set f.background="green"}
        }

    }
    frame
}