ScalaFXでTreeView

ScalaFXJavaFX 2.2.xのラッパーでScala言語で書かれたUI DSLです。
JavaFX 2.2.xのラッパーとしては開発が進んでいる方です。
最近ScalaFX 1.0 M5とScalaFX 8.0 M1が出ました。
今回はIDEにIntelliJ IDEA 12 Community Editionを使いScalaFX 1.0 M5でTreeViewを作成します。
Scalaはすでにホームディレクトリにインストールしてあるscala-2.10.1を使います。
Pluginのインストールはこのサイトが親切丁寧です。
図解】Scala 2.10 + IntelliJ IDEA 12 で「Hello World」する
すでにscala-2.10.1がインストールしてある場合など若干の変更をすれば良いです。
準備ができていると仮定します。
1. FileメニューよりNew Project...を選択。
 プロジェクト名、Scala Homeを記入

2. Fileメニューから”Project Structure...”を選択

 Project Settingsの中のLibrariesを選択

3. "+" をクリック
4. scalafx_2.10-1.0.0-M5.jarを追加する。

5. srcフォルダをcontrolキー+クリック又はマウスの右クリックでNew=>Scala Classを作成します。

TreeViewSampleの元のコードはオラクルのJavaFXチュートリアルにあるものを使いこれをScalaFXで書き直しました。
13 Tree View
ScalaFXのTreeViewは開発途中ということもあってラップが中途半端な感じです。
このためコンパイル実行が可能なようにコードを少し工夫してあります。

TreeViewSample.scala

/**
 * Created with Intellij IDEA.
 * User: hshino
 * Date: 13/09/14
 * Time: 20:32
 * To change this template use File | Settings | File Templates.
 */

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.Node
import scalafx.scene.Scene
import scalafx.scene.control.TextField
import java.util
import scalafx.scene.control.TreeItem
import scalafx.scene.control.TreeView
import scalafx.scene.image.Image
import scalafx.scene.image.ImageView
import scalafx.scene.input.KeyEvent
import javafx.scene.{control => jfxsc}
import scalafx.scene.paint.Color
import javafx.util.Callback
import javafx.scene.{input => jfxsi}
import scalafx.beans.property.StringProperty
import scalafx.scene.layout.VBox
import scalafx.scene.layout.Priority

  class Employee (name: String , department: String ){

    val name2:StringProperty = new StringProperty(name)
    val department2:StringProperty = new StringProperty(department)

    def getName:String = {
        name2.get()
    }

    def setName(fName:String ):Unit = {
      name2.set(fName)
    }

    def getDepartment:String = {
      department2.get()
    }

    def setDepartment(fName:String ):Unit = {
      department2.set(fName)
    }
  }

  class TextFieldTreeCellImpl extends jfxsc.TreeCell[String] {

    private val tfDelegate: jfxsc.TextField = new jfxsc.TextField
    private var tf: TextField = _

    override
    def startEdit(){
      super.startEdit()

      if (tf == null) {
        createTextField()
      }
      setText(null)
      setGraphic(tf)
      tf.selectAll()
    }

    override
    def cancelEdit(){
      super.cancelEdit()
      setText(getItem)
      setGraphic(getTreeItem.getGraphic)

    }

    override
    def updateItem(item:String , empty: Boolean ) {
      super.updateItem(item, empty)


      if (empty) {
        setText(null)
        setGraphic(null)
      } else {
        if (isEditing) {
          if (tf != null) {
            tf.setText(getString)
          }
          setText(null)
          setGraphic(tf)
        } else {
          setText(getString)
          setGraphic(getTreeItem.getGraphic)

        }
      }
    }

    def createTextField():Unit = {

      tf = new TextField(tfDelegate)
      println("tf:TextField = " + tf)
      tf.setText (getString)
      tf.setEditable(true)
      tf.onKeyReleased = (event: KeyEvent) => {

        if (event.getCode == jfxsi.KeyCode.ENTER) {
          println("KeyCode.ENTER")
          commitEdit(tf.getText)

        } else if (event.getCode == jfxsi.KeyCode.ESCAPE) {
          cancelEdit()
        }

      }
    }

    def  getString: String ={

      if(getItem == null ){
          ""
      }else{
        getItem.toString
      }
    }

  }

  object TreeViewSample extends JFXApp {

    private val tvDelegate: jfxsc.TreeView[String] = new jfxsc.TreeView[String]
    private var tv: TreeView[String] = _

    val rootIcon: Node = {
      new ImageView(new Image(this.getClass.getClassLoader.getResourceAsStream("root.png")))
    }

    private val rootNodeDelegate: jfxsc.TreeItem[String] = new jfxsc.TreeItem[String]
    private var rootNode: TreeItem[String] = _
    rootNode = new TreeItem[String](rootNodeDelegate)

    val depIcon = {
      new Image(this.getClass.getClassLoader.getResourceAsStream("department.png"))
    }

    val employees = util.Arrays.asList(
      new Employee("Ethan Williams", "Sales Department"),
      new Employee("Emma Jones", "Sales Department"),
      new Employee("Michael Brown", "Sales Department"),
      new Employee("Anna Black", "Sales Department"),
      new Employee("Rodger York", "Sales Department"),
      new Employee("Susan Collins", "Sales Department"),
      new Employee("Mike Graham", "IT Support"),
      new Employee("Judy Mayer", "IT Support"),
      new Employee("Gregory Smith", "IT Support"),
      new Employee("Jacob Smith", "Accounts Department"),
      new Employee("Isabella Johnson", "Accounts Department"))

    for (i <- 0 to employees.size -1) {

      val empLeaf:TreeItem[String]  = new TreeItem[String](employees.get(i).getName)
      var found:Boolean  = false
      for ( depNode <- rootNode.getChildren) {

        if (depNode.getValue.contentEquals(employees.get(i).getDepartment)){
          depNode.getChildren.add(empLeaf)

          found = true

        }
      }
      if (!found) {
        val depNode:TreeItem[String]  = new TreeItem[String](
          employees.get(i).getDepartment,
          new ImageView(depIcon)
        )
        rootNode.getChildren.add(depNode)
        depNode.getChildren.add(empLeaf)
      }
    }

    stage = new PrimaryStage {
      title = "Tree View Sample"
      width = 500
      height = 500

      println("rootNode = " + rootNode)
      rootNode.value = "MyCompany Human Resources"
      rootNode.graphic = rootIcon
      rootNode.setExpanded(true)

      tv = new TreeView(tvDelegate)

      tv.setRoot(rootNode)
      tv.setShowRoot(true)
      tv.setEditable(true)
      tv.setPrefSize(500, 500)
      tv.vgrow = Priority.ALWAYS
      tv.hgrow = Priority.ALWAYS
      tv.setCellFactory(new Callback[jfxsc.TreeView[String],jfxsc.TreeCell[String]](){
        override
        def  call( p: jfxsc.TreeView[String]) :jfxsc.TreeCell[String] = {
          new TextFieldTreeCellImpl()
        }
      })


      scene = new Scene {
        fill = Color.LIGHTGRAY

        root = new VBox {
          vgrow = Priority.ALWAYS
          hgrow = Priority.ALWAYS
          content = tv

        }
      }
    }
  }

6. 後はdepartment.pngとroot.pngをsrcフォルダにコピーしてRunメニューからRunさせてください。


department.png

root.png

ProScalaFXには参考になるコードが沢山あります。