SwingApplication終了時の処理

f:id:kxbmap:20101203002929p:image
scala.swingのSwingApplicationにはquit()とshutdown()というメソッドがあります。
quit()はshutdown()とSystem.exit(0)を呼んでくれるので、shutdown()をオーバーライドして終了時の処理を書けばいいんだよねー。

object QuitNotCalledApplication extends SimpleSwingApplication {
  def top = new MainFrame
  override def shutdown() { println("shutdown...") }
}

と思いきやこれではquit()は呼ばれてくれません。何故でしょう。
MainFrameの定義を見ると↓のようになってます。

class MainFrame extends Frame {
  override def closeOperation { System.exit(0) }
}

閉じるボタンが押されたらSystem.exit(0)しちゃう訳です。
MainFrameはSwingApplicationのことなんて知らないのでそりゃそうですよね。

という訳でquit()を呼んでくれるFrameを自前で作ります。

class MyFrame(app: SwingApplication) extends Frame {
  require(app != null)
  override def closeOperation() { app.quit() }
}

requireでappがnullでないことを保証しています。
閉じるボタンが押されたらquit()を呼びます。

これを使って先ほどのコードを修正。

object QuitCalledApplication extends SimpleSwingApplication {
  def top = new MyFrame(this)
  override def shutdown() { println("shutdown...") }
}

これで閉じるボタンを押したときにshutdown処理が走るようになります。
(しかし何か不恰好な気がする。いい感じのやり方ないかな……)

sbt-idea (plugin and processor)

sbtのよさげなプラグインがリリースされてたので使ってみる。
http://implicit.ly/sbt-idea-010
sbtのプロジェクト定義からIDEAのプロジェクトとかモジュールを生成してくれるんですって。わーい。

sbtプロジェクト作成

ここはいつも通りですね。Scala2.8.1.final出たので2.8.1使いましょう。

$ sbt
Project does not exist, create new project? (y/N/s) y
Name: MyProject
Organization: example
Version [1.0]:
Scala version [2.7.7]: 2.8.1
sbt version [0.7.4]:
...
[info] Building project MyProject 1.0 against Scala 2.8.1
[info]    using sbt.DefaultProject with sbt 0.7.4 and Scala 2.7.7

Plugins.scala

プロジェクトフォルダの project/plugins/Plugins.scala を編集(作成)します。

import sbt._
class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
  val sbtIdeaRepo = "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
  val sbtIdea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1.0"
}

Project.scala

project/build/Project.scala も編集。

import sbt._
class MyProject(info: ProjectInfo) extends DefaultProject(info) with IdeaProject {
  // ...
}

IdeaProjectをミックスインします。
マルチモジュールなプロジェクトを作成したい場合、すべてのサブプロジェクトにIdeaProjectをミックスインする必要があります。

idea.properties

project/idea.propertiesを編集すると、作成されるIDEAプロジェクトの設定を調整できます。
デフォルトで問題ないと思いますが、IDEAに名前変えてjdk設定してるのでそこだけ変更。(デフォルトだと1.6)

project.jdk.name=1.6.0_22

他にもjava.language.level(デフォルトJDK_1_6)やらproject.output.pathやらあります。

sbt update

sbt updateを実行します。

$ sbt update
[info] Recompiling plugin definition...
[info]    Source analysis: 1 new/modified, 0 indirectly invalidated, 0 removed.
...
[success] Build completed successfully.

pluginをDLしてきます。

sbt idea

sbt idea実行。

$ sbt idea
[info] Building project MyProject 1.0 against Scala 2.8.1
[info]    using MyProject with sbt 0.7.4 and Scala 2.7.7
[info]
[info] == idea ==
[info] Created D:\Project\MyProject\.idea
[info] Created D:\Project\MyProject/project/sbt_project_definition.iml
[info] Created D:\Project\MyProject/MyProject.iml
[info] == idea ==
[success] Successful.
...

おー、IDEAのプロジェクトファイルが作成されました。

IDEAで確認してみる

Open Project
f:id:kxbmap:20101114081514p:image
開けます。フォルダの設定とかもちゃんとやってくれてます。
外部ライブラリがすごいことになってるけどなんだろう。

Animatorその1

全然Animatorじゃないけど。PartialFunctionで状態遷移だけ。

import scala.actors._
import javax.media.opengl.GLAutoDrawable

class Animator extends Actor {
  private val drawables = scala.collection.mutable.Set[GLAutoDrawable]()
  private var action = stoped orElse pfallways

  def act() {
    loop {
      react(action)
    }
  }

  lazy val stoped : PartialFunction[Any, Unit] = {
    case Start => action = started orElse pfallways
  }

  lazy val started : PartialFunction[Any, Unit] = {
    case Stop => action = stoped orElse pfallways
    case Pause => action = paused orElse pfallways
  }

  lazy val paused : PartialFunction[Any, Unit]  = {
    case Stop => action = stoped orElse pfallways
    case Resume => action = started orElse pfallways
  }

  lazy val pfallways = pfdrawable orElse pfexit

  lazy val pfdrawable : PartialFunction[Any, Unit] = {
    case Add(drawable) => drawables += drawable
    case Remove(drawable) => drawables -= drawable
  }

  lazy val pfexit : PartialFunction[Any, Unit] = {
    case Exit => exit()
  }
}

名前考えるのがだるい。最後とかpfつけときゃいいやって思考になってて意味不明です。
これに色々肉付けしていって、ラッパーにGLAnimatorControl実装してー(def start()とか被るので)、とか考えてたけど、GLAnimatorControlにgetThreadとかあるわな。
詳しく見てないけど、描画中のスレッドを外部から取得できるようにするってActorとしてまずいんじゃなイカ。
そこんとこどーなのと思いつつ、あんまり触る時間が取れてない。

NEWTでウィンドウ作成

JOGLのdemosやチュートリアルから引っ張ってきてScalaの書式にしただけだけども。

import javax.media.opengl.{ GLAutoDrawable, GLProfile, GLCapabilities, GLEventListener }
import com.jogamp.newt.event.{ WindowAdapter, WindowEvent }
import com.jogamp.newt.opengl.GLWindow
import com.jogamp.opengl.util.FPSAnimator

object NewtTest {
  def main(args : Array[String]){
    GLProfile.initSingleton()
    run()
  }

  def run() {
    try {
      val glp = GLProfile.getDefault
      val caps = new GLCapabilities(glp)

      val window = GLWindow.create(caps)
      window.setUndecorated(false)
      window.setSize(800, 600)

      val animator = new FPSAnimator(window, 60)
      window.addWindowListener(new WindowAdapter(){
        override def windowDestroyNotify(e : WindowEvent){
          animator.stop()
          System.exit(0)
        }
      })
      window.addGLEventListener(listener)

      window.setVisible(true)
      window.setTitle("NEWT Window Test")
      animator.start()
    } catch {
      case e =>
        e.printStackTrace()
        System.exit(1)
    }
  }

  object listener extends GLEventListener {
    def init(drawable : GLAutoDrawable){}
    def dispose(drawable : GLAutoDrawable){}
    def display(drawable : GLAutoDrawable){}
    def reshape(drawable : GLAutoDrawable, x : Int, y : Int, w : Int, h : Int){}
  }
}

setVisibleやった後じゃないとsetTitleが効かないのは何故だろう?

次はActorの練習にAnimatorを自前で書いてみたいな。