Scala の Swing サポート#2 イベント処理

イベントの扱い

SimpleSwingApplication を使用した簡単なイベント処理の例です。Frame にマウスクリック回数を表示する Label を配置します。

import swing._ 

object Counter extends SimpleSwingApplication {
  val label = new Label {
      text = "0"
      listenTo(mouse.clicks)
      reactions += { 
        case e: event.MouseClicked => 
          text = (Integer.parseInt(text) +1).toString
      }
  }
  
  def top = new MainFrame {
    title = "Counter" 
    contents = label
  }
}


上記コードの実行結果は以下のようになります。

マウスクリックに応じて Label のカウンターがカウントアップします。


リスンするイベントを以下で登録しています。

  listenTo(mouse.clicks)


listenTo は SwingApplication extends Reactor の Reactorトレイトで定義されています。

trait Reactor {
  val reactions: Reactions = new Reactions.Impl
  def listenTo(ps: Publisher*) = for (p <- ps) p.subscribe(reactions)
  ・・

listenTo メソッドに Publisher としてイベントの発生元となるオブジェクトを登録します。listenTo メソッドでは、イベントの発生元となる Publisher と、イベントの結果の動作をサブスクライブとして関連付けています。
上記で登録している Publisher は、Component クラスにて以下のように定義されている mouse.clicks オブジェクトです。

  object mouse {
    val clicks: Publisher = new Publisher {
      peer.addMouseListener(new MouseListener {
        def mouseClicked(e: java.awt.event.MouseEvent) { 
          publish(new MouseClicked(e))
        }
        ・・・
      })
    }
  }


イベントの結果としての動作は以下のコードで登録しています。

    reactions += { 
      case e: event.MouseClicked => 
        text = (Integer.parseInt(text) +1).toStrin
    }

mouse.clicks にて発生する event.MouseClicked イベント時の動作を reactions に追加しています。

コンポーネントのイベント処理

次に摂氏/華氏変換の例を見てみます。

import swing._ 

object Converter extends SimpleSwingApplication {
  
  def newField = new TextField { columns = 5 }
  val celsius = newField 
  val fahrenheit = newField 

  def top = new MainFrame { 
    title = "摂氏(℃)/華氏(F)" 
    contents = new FlowPanel(
      celsius, new Label("℃ = "), fahrenheit, new Label("F")) 
  }
  
  listenTo(celsius, fahrenheit) 
  reactions += {
    case event.EditDone(`celsius`) => 
      val c = Integer.parseInt(celsius.text) 
      fahrenheit.text = (c * 9 / 5 + 32).toString
    case event.EditDone(`fahrenheit`) => 
      val f = Integer.parseInt(fahrenheit.text) 
      celsius.text = ((f - 32) * 5 / 9).toString 
  }
}


実行結果は以下のようになります。

テキスト入力時


最初の例と同様に listenTo にテキストフィールドを Publisher として登録しています。

listenTo(celsius, fahrenheit) 


イベントに対する反応は以下のようにテキストフィールドの発行する EditDone に応答する摂氏/華氏変換を行いテキストフィールドに設定しています。

  reactions += {
    case event.EditDone(`celsius`) => 
      val c = Integer.parseInt(celsius.text) 
      fahrenheit.text = (c * 9 / 5 + 32).toString
    case event.EditDone(`fahrenheit`) => 
      val f = Integer.parseInt(fahrenheit.text) 
      celsius.text = ((f - 32) * 5 / 9).toString 


EditDone は以下のようなケースクラスです。

case class EditDone(override val source: TextField) extends ValueChanged(source)

アクション