テキストエディタの作り方:Swingのフォーカスについて

あー、本来ならJavaで書くべきでしょうが、Scalaでjavax.swingを使ってます。m(_ _)m


テキストエディタバイナリエディタを作ろうとしたとき、そのビューに対してキーイベントを 受け付けるようにしなければなりません。
キーイベントに関しては、イベントマスクを外してやる必要があります。
また、TABキーはデフォルトでは、別のコンポーネントにフォーカスが移ってしまうので、 TABキーのイベントは来ません。 TABキーをイベントとして欲しい場合は、フォーカスマネージャの設定を変える必要があります。

import java.awt.{AWTEvent,AWTKeyStroke,KeyboardFocusManager}
class TextView(val model:TextModel) extends JComponent {


    // フォーカスを与える
>   enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.INPUT_METHOD_EVENT_MASK)
>   setFocusable(true)

    // TABでフォーカスが移らないように、デフォルトのフォーカス移動キーを変更。
>   setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, new java.util.HashSet[AWTKeyStroke]())
>   setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, new java.util.HashSet[AWTKeyStroke]())
>   setFocusTraversalKeys(KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, new java.util.HashSet[AWTKeyStroke]())

    // マウスカーソルをIビームに
>   setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR))

    // キーリスナー、マウスリスナーを登録
>   addKeyListener(new EditorKeyListener())

    ...
}

それと、Swingアプリケーションを立ち上げたとき、最初のフォーカスの位置は、 左上のコンポーネントになります。
一般的なアプリならば、ツールバーの左端のボタンになるでしょうか。 望みのビューに最初にフォーカスを与えるには、以下のようにします。

class TextFrame extends JFrame {

    def addTab(title:String, tip:String, model:TextModel) {
        println("addTab(title="+title+")")
        val tab = new TextTab(model)
        tabPane.addTab(title, null, tab, tip)
        tabPane.setSelectedIndex(tabPane.getTabCount() - 1)

        // ビューにフォーカスを移す
>       this.setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
>           override def getInitialComponent(w:java.awt.Window):java.awt.Component = {
>               tab.focusView
>           }
>       })

    }
}

テキスト編集ビューでマウスをクリックしたとき、そのビューにフォーカスを移したい場合があります。 例えば、分割ビューで、下のビューでマウスクリックすると、フォーカスを下に移したい場合です。

class TextMouseListener(view:TextView) extends MouseInputAdapter {
    override def mouseReleased(e:MouseEvent) {
        println(e.getPoint)
        if (e.isPopupTrigger) showPopup(e)
        else if (e.getButton == MouseEvent.BUTTON1) {
            view.pos2 = view.model.getNearPos(e.getPoint)
        }
        // クリックしたビューをフォーカス
        val textTab = SwingUtil.findRootContainer(view, classOf[TextTab]).asInstanceOf[TextTab]
        textTab.focusView = view
        // ビューにフォーカスを移す
>       view.grabFocus()

        view.frame.updateFrame()
        println("pos1="+view.pos1+" pos2="+view.pos2)
    }
}