要約: | - 各アプレット ウィンドウは View および FocusManager に関連付けられています。
- FocusManager は、View のキーボード フォーカスを管理します。
- キーボード フォーカスは、ユーザーのキーストロークが送られる場所を決定します。
- 複数の KeyEvent は、押されたあるいは離された 1 つのキーまたは複数のキーの組み合わせを表現します。
|
Curl® 言語で記述されたアプレットが動作している場合、アプレットはキーボード イベントを処理して、キーボードで入力するユーザーに応答します。キーボード イベントは、オペレーティング システムによりアプレット ウィンドウに送られます。各ウィンドウは
View オブジェクトに関連づけられ、各
View オブジェクトには、すべてのキーボードおよびポインタ イベントに関係する、オブジェクト自身の
FocusManager があります。これは
キーボード フォーカスを管理するものです。キーボード フォーカスではユーザーのキーストロークの送信先が決定されます。
アプレットが複数の
View を表示する場合、アクティブ ウィンドウの
View のみがキーボード イベントを受け取ります。さらにキーボード イベントは、
View により
View 内のオブジェクトに送出されます。ただし、これはオブジェクトがキーボード フォーカスを持っている場合に限ります。
次のポップアップ ダイアログを見てください。3 つのテキスト フィールドが含まれています。(既定では、ポップアップ ダイアログは新しい
View に表示されます。) このダイアログを表示した直後にキーボードから入力する場合、キーボードの入力は自動的に 2 番目のテキスト フィールドに送信されます。つまり、キーボード フォーカスを持つオブジェクトは
View にあることを示しています。
例:
キーボード フォーカスを持つ TextField |
 |
{value
let tf1:TextField = {TextField width=3.0in}
let tf2:TextField = {TextField width=3.5in}
let tf3:TextField = {TextField width=4.0in}
let vbox:VBox={VBox
tf1,
tf2,
tf3
}
let dialog:Dialog={Dialog vbox,
{on DialogShow do
|| the 2nd TextField requests the keyboard focus
{tf2.request-key-focus}
}
}
{CommandButton label="Click and Type",
{on Action do
{dialog.show
modal?=false
}
}
}
}
| |
ウィンドウのタイトル バーがハイライト表示されることで示されるように、ウィンドウがアクティブの場合は、オペレーティング システムはそのウィンドウがホスト フォーカスを持っていると認識します。これは、すべてのキーボード イベントがそのウィンドウに送られるということを意味します。
Curl® 実行環境 は、オペレーティング システムによりウィンドウに送られるキーボードの動作を、同じウィンドウに関連付けられた
View で発生する
KeyEvent に変換します。
ホスト フォーカスがオペレーティング システムによるキーボード イベントの送信先であるのとは対象的に、各
View は
キーボード フォーカスを追跡します。キーボード フォーカスは、各
View で最高 1 つのグラフィック オブジェクトに必ず属しています。オブジェクトがキーボード フォーカスを保持している間は、その
View がオペレーティング システムから受け取るすべての
KeyEvent のターゲットになります。
グラフィック オブジェクトは、
FocusIn イベントがオブジェクトで発生するときにキーボード フォーカスを得ます。別のオブジェクトが
KeyEvent のターゲットになるときは、
FocusOut イベントがその前のターゲットで発生してキーボード フォーカスを失います。
各
View には
FocusManager があり、
View のキーボード フォーカスを管理します。
View のフォーカス マネージャは
focus-manager オプションにより指定されます。各フォーカス マネージャはキーボードのフォーカス ターゲットを保持します。フォーカス ターゲットとは、フォーカス マネージャがオペレーティング システムから
View 経由で受け取って送信する、任意の
KeyEvent のターゲットのことです。
PointerEvent、FocusEvent および KeyEvent の送信方法の主な相違点を認識することが大切です。FocusEvent および KeyEvent がフォーカス マネージャを通して送出されるのに対し、PointerEvent はグラフィック階層を通して転送されます。
たとえば、Curl アプレットで 2 つの
View が開いていて、それぞれの
View は
FocusManager を持っているとします。キーボード イベントがオペレーティング システムから送出される場合、現在の Curl アプレットの
GuiManager を介して、キーボード フォーカスを持つ
View に送出されます。次に
View の
FocusManager を介して、その
View のキーボード フォーカスを持つオブジェクトに送出されます。
フォーカス マネージャは、グラフィック階層で現在のキーボード フォーカスを持つオブジェクトを決定するだけでなく、フォーカス マネージャ階層内で現在選択されているオブジェクトを追跡し、切り取り、コピー、貼り付けなどの操作を処理します。
また
FocusManager は、アクティブなメニュー バーになる
MenuBar の情報を記録します。これにより、ユーザーが
Alt キーを押すとアクティブ メニューがポップ アップ表示されます。
オブジェクトがキーボード フォーカスを得ると、オブジェクトはその
View のすべてのキーボード イベントのターゲットになります。キーボード イベントとは、
KeyEvent の任意のサブクラスのインスタンスで以下が含まれます。
キー イベントは次の 2 つに大別されます。
- Raw キー イベント :押されたまたは解放された物理キーを示すイベント
- Cooked キー イベント :1 つのキーまたはキーの組み合わせを押して生成される値を表わすイベント
Raw キー イベントはキーボードの物理キーが押されたまたは解放されたことを伝え、普通はビデオ ゲームなどの、キーの使用で生成される値よりも物理キーが押されたまたは離されたというアクションに重点が置かれるようなアプリケーションで使用されます。
次の例では、各
RawKeyPress イベントに対して数値を表示することにより、キーが押されたことに応答する
Frame を示しています。
Frame をクリックしてキーボード フォーカスを与えます (テキストを直接クリックしないでください)。キーを押すたびに、最後に押したキーのキー コードを表すキー名が表示されます。
Home キーを押して終了するまでこれが続きます。
例:
RawKeyPress の Keycode を表示 |
 |
|| To display the key code as a Dynamic object
{let d:Dynamic = {Dynamic "(none)"}}
{Frame
width=2in,
height=1in,
background={Color.from-string "#ab29df"},
valign="center",
halign="center",
|| Display the key code value
{text Keycode: {value d}},
|| PointerPress event handler
{on p:PointerPress at f:Frame do
{f.request-key-focus}
set f.background = "pink"
|| Consume the PointerPress event so that it is
|| not fired further up the graphic hierarchy
{p.consume}
},
|| Event handler for RawKeyPress
{on rkp:RawKeyPress do
|| Sets the Dynamic object's value to the key code
|| that identifies the physical key pressed
set d.value = {Keycode.name-for-keycode rkp.keycode}
{if rkp.keycode == Keycode.home then
{popup-message "You've hit the winning key!"}}
}
}
| |
これに対して、単一 Cooked キー イベントは、ユーザーが 1 つ以上の修飾キーと非修飾キーを組み合わせて押すことにより発生します。ユーザーが、Shift キーを押しながら、または CapsLock キーが押された状態で、a キーを押すと、値 'A' を持つ単一 Cooked キー イベントのみが生成されます。
Shift キーなどの修飾キーまたは CapsLock キーなどのロック キーを押すだけでは、Cooked キー イベントが発生しないことに注意してください。
また、Cooked キー イベントではユーザーが実行した操作に関する重要な情報を保存してその値を生成します。たとえば、Cooked キー イベントはイベント時に Shift キーが押されたかどうかは記録しても、それが左シフト キーか右シフト キーかは記録しません。
Cooked キー イベントに関連する値は、
char 型として
KeyPress.value プロパティに保存されます。この値の表現方法は、標準または特殊の出力表現によって異なります。
- 標準値の出力表現とは印刷可能な文字です。標準値の例は、'T'、'8' および '*' で、このイベントは char 型の値を持ちます。標準値を持つ Cooked キー イベントは標準のキー プレス イベントであり、その normal? プロパティは true を返します。
- 特殊な値では出力表現がありません。したがってその値は、KeyPressValue.delete、KeyPressValue.f5、または KeyPressValue.up などのキー定数値として表現されます。特殊な値を持つ Cooked キー イベントは特殊なキー プレス イベントであり、その normal?? プロパティは false を返します。
キー定数値は、次の構文で表現されます。
KeyPressValue. key-name
『API リファレンス』で KeyPressValue クラスを検索し、そのフィールドのドキュメントを参照して、有効なキー定数値を確認します。
たとえば、Cooked キー イベントの値が Shift+e を押して生成された値であるかどうかを調べるには、次のイベント ハンドラ コードを書き込みます。
{on kp:KeyPress do
{if kp.value == 'E' then
...
}
}
また、Cooked キー イベントの値が Home を押して生成された値であるかどうかを調べるには、次のイベント ハンドラ コードを書き込みます。
{on kp:KeyPress do
{if kp.value == KeyPressValue.home then
...
}
}
Cooked キー イベントの value 属性だけが、アプリケーションで重要なプロパティではありません。たとえば、9 キーまたは Ctrl+9 キー組み合わせのいずれの場合も、同じキーの値 '9' を生成しますが、通常 2 つのイベントはまったく異なるものとして扱われます。
このため、
KeyPress クラスではクエリ可能な他のプロパティが保持されます。この戻り値は、次の質問の回答に相当します。
- キー プレス イベントは標準または特殊であるか。
- キー プレス イベントは自動生成からトリガされたかどうか。つまり、イベントが繰り返し発生するようにユーザーはキーを押さえた状態にしたのかどうか。
- ユーザーはキーパッド (テン キーボード) を使用したかどうか。
- ユーザーは修飾キー (Shift、Ctrl、Alt、または/および Meta) を押したかどうか。
- アプリケーションは Shift 修飾キーを意識するべきかどうか (あるいは、KeyPress.value を計算する目的だけで使用されたかどうか)。
Cooked キー イベントが生成されたときに対応する修飾キーが押されていた場合、次のプロパティ値は true になります。
使用された修飾キーを示すプロパティのほかに、KeyPress イベントをトリガした動作に関する情報を示すキー プレス プロパティがあります。以下のプロパティはすべて bool 型の値を持ちます。
- KeyPress.normal?: キー値が標準 (真の場合) または特殊 (偽の場合) であるかを示します。
- KeyPress.shifty? :Shift キーがアプリケーションに潜在的に適用されるかどうか、またはキーが押されたときにはすでに KeyPress.value に組み込まれているかどうかを示します。
- true :Shift キーが押された場合、これがキー値に組み込まれていないため、アプリケーションに対して潜在的に適用されます。
- false :Shift キーが押された場合、すでにキー値の生成に使用されているため、アプリケーションに適用されません。
- KeyPress.unmodified? :このプロパティが true になるのは、Ctrl、Alt、Meta などの修飾キーが押されず、Shift 修飾キーが押されたときには KeyPress.value にすでに組み込まれていて適用されない場合です。スペースバーは特別である点に注意してください。Shift 修飾キーでは変更は起きませんが、Ctrl、Alt、Meta では、標準として変更されます。
- KeyPress.insertable? :KeyPress.value が文字列に挿入されるかどうかを示します。normal? と ummodified? の両方が true である場合、insertable? は true になります。
- KeyPress.keypad? :キーボードのキーパッド セクションのキーの 1 つを使用してキー値が生成されたかどうかを示します。
- KeyPress.repeat? :キー値が自動繰り返し処理から生成されたかどうかを示します。
たとえば、
a または
Ctrl+a を押しても、操作の値は両方とも
'a' になります。しかし、
Ctrl+a を押す操作で生成された
'a' を
TextField に挿入することはないはずです。
Ctrl+a では、normal? は true ですが、unmodified? は false になります。したがって、insertable? も同様に false になります。
このセクションでは、さまざまな Cooked キー イベントの
KeyPress 値と属性を例を使用して説明します。
この例では、赤い四角形をクリックするとキーボード フォーカスが要求されます。キー フォーカスを得ると、FocusIn イベントが発生します。対応するイベント ハンドラがこれを緑に変えます。次に任意のキーを押すと、結果の KeyPress イベント値、修飾キー プロパティおよび追加プロパティが表示されます。
たとえば、ここで
a キーを押すと、通常は値
'a' が生成されます。しかし、
Shift または
CapsLock のいずれか (両方ではない) がその時にアクティブである場合は、値
'A' が生成されます。値が標準であり、特殊なキー定数ではないので、
KeyPress.normal? はいずれの場合でも真です。
KeyPress.shifty? は、両方とも偽になります。これは、
Shift キーが押されている場合では、値
'A' の生成に組み込まれているためです。
別の例として、
Home キーをキーパッドで押すと、通常はキー定数値
KeyPressValue.home が生成されます。従って、
KeyPress.normal? は偽です。
KeyPress.shifty? は
true になります。これは、
Shift+Home を押しても
KeyPress 値は変わらないためです。キー プレス イベントでは
Shift を使わずに異なる
KeyPress 値が計算されます。ただし、
NumLock が押されている場合、結果の
KeyPress 値は
'7' になります。この場合
KeyPress.normal? は真 (
KeyPress 値が標準であるため) で、
KeyPress.shifty? は偽です。その理由は、
Shift を押すと、異なる
KeyPress 値、すなわち
KeyPressValue.homeを計算するために使用されるからです。
例:
キー プレス値およびプロパティの表示 |
 |
{value
let label:TextDisplay =
{TextDisplay
font-size = 9pt,
font-family = "monospace",
value="Click in the rectangle below to get the focus...."
}
let rect:RectangleGraphic =
{RectangleGraphic
fill-color = "red",
width = 6in,
height = 1in,
{on e:PointerPress at r:RectangleGraphic do
|| Checks that the left mouse button was clicked
{if e.button == left-button then
|| The RectangleGraphic requests the keyboard focus
|| for itself
{r.request-key-focus}
|| Consumes the event to prevent further firing of
|| the PointerPress event up the graphic hierarchy
{e.consume}
}
},
|| When the RectangleGraphic gets the keyboard focus,
|| FocusIn is fired at it
{on e:FocusIn at r:RectangleGraphic do
|| Changes the color of the rectangle
set r.fill-color = "green"
|| Changes its text
set label.value =
"Press keys and see how this display changes...."
{e.consume}
},
|| When the RectangleGraphic loses focus,
|| FocusOut is fired at it
{on e:FocusOut at r:RectangleGraphic do
|| Changes the color back to red
set r.fill-color = "red"
set label.value =
"Click in the rectangle below to get the focus...."
{e.consume}
},
|| This event handler handles cooked key events
{on e:KeyPress do
|| Sets the text to show the key value and properties
set label.value =
|| name-for-char converts key values to a String
|| This is necessary for undisplayable key
|| constant values (KeyPressValue.*)
|| format displays the String
"Key Value="&
{format "%-6s", {KeyPressValue.name-for-char e.value}}&
|| Displays the properties if they are true
" Flags="&
{if e.normal? then "(Normal)" else ""}&
{if e.shifty? then "(Shifty)" else ""}&
{if e.keypad? then "(Keypad)" else ""}&
{if e.repeat? then "(Repeat)" else ""}&
{if e.unmodified? then "(Unmodified)" else ""}&
{if e.insertable? then "(Insertable)" else ""}&
|| Displays the modifiers if they are true
" Modifiers="&
{if e.alt? then "(Alt)" else ""}&
{if e.ctrl? then "(Ctrl)" else ""}&
{if e.shift? then "(Shift)" else ""}
{e.consume}
}
}
{VBox
label,
{Fill height=0.2in},
rect
}
}
| |
次のステップでは、ユーザーからのキー イベントのキャプチャと応答を実行するアプレットを作成する場合の全般的な手順を示します。
- オブジェクトを作成します。
- キーボード フォーカスを取得する手段を含むオブジェクトを作成します。
- 予想されるさまざまな KeyPress イベント (RawKeyPress が適切な場合はこのイベント) のイベント ハンドラを書きます。
イベント ハンドラが、不適切な
KeyPress イベントに応答しないことが大切です。
たとえば、上向き矢印が押されると応答するというイベント ハンドラでは、修飾キーが使用されているときには上向き矢印が押されても応答しないはずです。この場合、unmodified? プロパティが役に立ちます。
あるいは、文字や数値、その他の印刷可能な文字に応答するイベント ハンドラでは、矢印キーやファンクション キーなど特殊なキーが押されても、または、任意の修飾キー (Shift は可能であれば除く) が使用されている状態で標準キーが押されても、これに応答しないようにする必要があります。この場合、insertable? プロパティが役に立ちます。
以下の 2 つのセクションでは、ユーザーのキーボード入力を分析するアプレットの例を示します。
次の例では、ユーザーがテキストを入力して編集すると四角形の下に表示されます。
- 挿入可能なテキストがあるかどうかを調べ (KeyPress.insertable? プロパティを使用)、存在する場合は TextDisplay.value を使用して四角形の下にあるテキストに KeyPress.value を追加します。
- Backspace キーがあるかどうかを調べ (KeyPressValue.backspace を使用)、存在する場合はテキストの最後の文字を削除します。さらに、
- Esc キーがあるかどうかを調べ (KeyPressValue.esc を使用)、存在する場合はテキストを消去します。
例:
簡単なテキスト エディタ |
 |
{value
|| The instructions
let instruct:TextDisplay =
{TextDisplay
font-size = 9pt,
font-family="monospace",
value="Click in the rectangle below to get the focus...."}
|| The editable text
let txt:TextDisplay =
{TextDisplay
font-size = 9pt,
font-family = "monospace",
value = ""
}
|| The rectangle that requests and gets the keyboard focus
|| after which, it gets all keyboard events
let rect:RectangleGraphic =
{RectangleGraphic
fill-color="red",
width=6in,
height=1in,
|| Respond to pointer clicks
{on e:PointerPress at r:RectangleGraphic do
{if e.button == left-button then
{r.request-key-focus}
{e.consume}
}
},
|| Respond to getting the focus
{on e:FocusIn at r:RectangleGraphic do
set r.fill-color="green"
set instruct.value =
"Press keys to edit the text below the rectangle...."
{e.consume}
},
|| Respond to losing the focus
{on e:FocusOut at r:RectangleGraphic do
set r.fill-color="red"
set instruct.value =
"Click in the rectangle below to get the focus...."
{e.consume}
},
|| Respond to KeyPress events
{on e:KeyPress do
{if e.insertable? then
|| The user has pressed a normal key without
|| pressing any significant modifier keys, so the
|| KeyPress is "insertable".
|| Append the value to the txt
set txt.value = txt.value & e.value
elseif (e.value == KeyPressValue.backspace and
e.unmodified?) then
|| The user has pressed the backspace key
|| without significant modifier keys.
let str:String = txt.value
|| Clear the text, replacing it with String
|| less one character
{if str.size > 0 then
set txt.value = {str.substr 0, str.size - 1}
}
elseif (e.value == KeyPressValue.esc and
e.unmodified?) then
|| The user has pressed the escape key
|| without any significant modifier keys.
|| Clear the text entirely
set txt.value = ""
}
{e.consume}
}
}
{VBox
instruct,
{Fill height=0.2in},
rect,
{Fill height=0.2in},
txt
}
}
| |
このセクションでは、
KeyPress イベント ハンドラを使用して、キー プレス値
KeyPressValue.home のさまざまな生成方法がどのように検出されるかを示します。
例:
shifty?の使用 |
 |
{let d-txt:Dynamic=
{Dynamic "Click the Frame to get the keyboard focus."}}
{spaced-hbox
valign="center",
{Frame
width=1in,
height=1in,
background="cyan",
{on pp:PointerPress at fr:Frame do
{fr.request-key-focus}
{pp.consume}},
{on FocusIn at fr:Frame do
set fr.background = "blue"
set d-txt.value="Press Shift+Home using different Home keys."},
{on FocusOut at fr:Frame do
set fr.background = "cyan"
set d-txt.value=
"Click the Frame to regain the keyboard focus."},
|| The event handler that responds to cooked key events
{on kp:KeyPress do
|| Checks if the Home key on the keypad is used
{if kp.value == KeyPressValue.home and kp.keypad? then
|| Checks if the Shift key is used
{if kp.shift? then
|| Checks if the Shift key was relevant to the app
{if kp.shifty? then
set d-txt.value =
"You used Shift+Home on the keypad, but " &
"did not use NumLock."
else
|| This runs when Shift key was needed to compute
|| the key value
set d-txt.value =
"You used Shift+Home on the keypad, and " &
"used NumLock too."
}
else
|| This runs when Shift was not pressed
set d-txt.value = "You used Home on the keypad. " &
"Now try Shift+Home."
}
elseif kp.value == '7' and kp.keypad? then
set d-txt.value = "The key value is not KeyPressValue.home; " &
"it's '7'. NumLock is on."
elseif kp.value == KeyPressValue.home then
{if kp.shift? then
set d-txt.value = "You used Shift+Home on the special pad. " &
"Now use Home on the keypad."
else
set d-txt.value = "You used Home on the special pad. " &
"Now use Home on the keypad."
}
else
set d-txt.value="Press Shift+Home using different " &
"Home keys."
}
}
},
{TextFlowBox {value d-txt}}
}
| |
Copyright © 1998-2019 SCSK Corporation.
All rights reserved.
Curl, the Curl logo, Surge, and the Surge logo are trademarks of SCSK Corporation.
that are registered in the United States. Surge
Lab, the Surge Lab logo, and the Surge Lab Visual Layout Editor (VLE)
logo are trademarks of SCSK Corporation.