トラバースキー詳細(続き)
更に、Control の translateTraversal を見てみる。
が、こいつは、派生クラスでオーバーライドされているようなので、
コンボ・コントロールから、辿って行く。
(Combo.java) boolean translateTraversal (MSG msg) { /* * When the combo box is dropped down, allow return * to select an item in the list and escape to close * the combo box. */ System.out.println("Combo translateTraversal"); switch ((int)/*64*/(msg.wParam)) { case OS.VK_RETURN: case OS.VK_ESCAPE: if ((style & SWT.DROP_DOWN) != 0) { if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) { return false; } } } return super.translateTraversal (msg); }
コメントにもあるように、ドロップダウン状態であれば、確定なりキャンセルなりしないといけないので、
トラバース処理はキャンセルするようだ。(falseを返す => 通常の DispatchMessage で処理される)
そうでなければ、更に親クラスの translateTraversal を呼ぶ。
Combo の親は、Composite なので、順に見ていく。
(Composite.java) boolean translateTraversal (MSG msg) { if ((state & CANVAS) != 0 ) { if ((style & SWT.EMBEDDED) != 0) return false; switch ((int)/*64*/msg.wParam) { case OS.VK_UP: case OS.VK_LEFT: case OS.VK_DOWN: case OS.VK_RIGHT: case OS.VK_PRIOR: case OS.VK_NEXT: int uiState = (int)/*64*/OS.SendMessage (msg.hwnd, OS.WM_QUERYUISTATE, 0, 0); if ((uiState & OS.UISF_HIDEFOCUS) != 0) { OS.SendMessage (msg.hwnd, OS.WM_UPDATEUISTATE, OS.MAKEWPARAM (OS.UIS_CLEAR, OS.UISF_HIDEFOCUS), 0); } break; } } return super.translateTraversal (msg); }
Composite では、フォーカスインジケータ(多分、点線の枠の事)の制御を必要に応じて行っているだけみたい。
translateTraversal をオーバーライドしているのは、この2つだけのようであり、
ようやく、Control の translateTraversal に辿りついた。(と言っても、2つだけだったのね)
長いので、前半は省略して、後半の大事そうなところだけ抜粋。
Event event = new Event (); event.doit = doit; event.detail = detail; display.lastKey = lastKey; display.lastAscii = lastAscii; display.lastVirtual = lastVirtual; display.lastNull = display.lastDead = false; if (!setKeyState (event, SWT.Traverse, msg.wParam, msg.lParam)) return false; Shell shell = getShell (); Control control = this; do { if (control.traverse (event)) { OS.SendMessage (hwnd, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); return true; } if (!event.doit && control.hooks (SWT.Traverse)) return false; if (control == shell) return false; control = control.parent; } while (all && control != null); return false;
コントロール自身から初めて、順に親を辿りながら、各コントロールの traverse を呼び出していく。
traverse は、基本的に Control にしか実装されていないようだ。
各コントロールが、このメソッドをオーバーライドする事は、今のところ無いようだ。
(Control.java) boolean traverse (Event event) { /* * It is possible (but unlikely), that application * code could have disposed the widget in the traverse * event. If this happens, return true to stop further * event processing. */ sendEvent (SWT.Traverse, event); if (isDisposed ()) return true; if (!event.doit) return false; switch (event.detail) { case SWT.TRAVERSE_NONE: return true; case SWT.TRAVERSE_ESCAPE: return traverseEscape (); case SWT.TRAVERSE_RETURN: return traverseReturn (); case SWT.TRAVERSE_TAB_NEXT: return traverseGroup (true); case SWT.TRAVERSE_TAB_PREVIOUS: return traverseGroup (false); case SWT.TRAVERSE_ARROW_NEXT: return traverseItem (true); case SWT.TRAVERSE_ARROW_PREVIOUS: return traverseItem (false); case SWT.TRAVERSE_MNEMONIC: return traverseMnemonic (event.character); case SWT.TRAVERSE_PAGE_NEXT: return traversePage (true); case SWT.TRAVERSE_PAGE_PREVIOUS: return traversePage (false); } return false; }
処理の最初で、sendEvent (SWT.Traverse, event); を呼び出している。
これは、Control の親クラスである、Widget の メソッドである。
本体は、これだ。
void sendEvent (int eventType, Event event, boolean send) { if (eventTable == null && !display.filters (eventType)) { return; } if (event == null) event = new Event (); event.type = eventType; event.display = display; event.widget = this; if (event.time == 0) { event.time = display.getLastEventTime (); } if (send) { sendEvent (event); } else { display.postEvent (event); } }
eventTable は、リスナーが登録されているオブジェクトである。
display.filters (eventType)は、親 Display が、指定イベントをフィルタしているかどうかをチェックしている。
要するに、イベントを送る先がなければ何もしない、あれば送るという事だ。
元に戻って、
sendEvent (SWT.Traverse, event); if (isDisposed ()) return true; if (!event.doit) return false;
ふむ。
sendEvent の結果、自分自身が Dispose されるかもしれないという事か?
その場合は、イベント処理済みとなる。
sendEvent の先で、doit が false に設定された場合、ここで終了。
switch (event.detail) { case SWT.TRAVERSE_NONE: return true; case SWT.TRAVERSE_ESCAPE: return traverseEscape (); case SWT.TRAVERSE_RETURN: return traverseReturn (); case SWT.TRAVERSE_TAB_NEXT: return traverseGroup (true); case SWT.TRAVERSE_TAB_PREVIOUS: return traverseGroup (false); case SWT.TRAVERSE_ARROW_NEXT: return traverseItem (true); case SWT.TRAVERSE_ARROW_PREVIOUS: return traverseItem (false); case SWT.TRAVERSE_MNEMONIC: return traverseMnemonic (event.character); case SWT.TRAVERSE_PAGE_NEXT: return traversePage (true); case SWT.TRAVERSE_PAGE_PREVIOUS: return traversePage (false); } return false;
その先は、detail の内容により、処理を振り分け。
どうやら、この先で呼ばれる、各メソッドを、必要に応じてオーバーライドする方針のようだ。
ちなみに、traverse メソッドが複数存在して混乱するが、
public な traverse メソッドは、上位階層(アプリ層など)から traverse 処理を実行させる物だ。
メソッドのオーバーロードも、便利なんだけど、やり過ぎると混乱するよなぁ...
さて、Combo コントロールでは、traverseEscape と traverseReturn がオーバーライドされているようだが、
それ以外は、基本的に、Control クラスの実装のみのようである。
結局、translateTraversal は、
if (control.traverse (event)) { OS.SendMessage (hwnd, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); return true; } if (!event.doit && control.hooks (SWT.Traverse)) return false; if (control == shell) return false; control = control.parent;
各コントロールで、内部イベント送信→トラバーサル処理を行い、
その処理の中で無効化された、もしくは、トラバース処理がフック対象であれば、中断。
必要な限り、上記の処理を、親コントロールを対象に繰り返す。
という事になる。