ScalaFXでJavaFX 2のFXMLLoaderを使う

ScalaFXでJavaFX 2のFXMLLoaderを使うの自体は簡単です。
後はコントローラーをどのように書くかですね2種類の方法があります。
まず方法1はJavaFXで書く
方法2はScalaFXで書くです
方法2についてはProScalaFXにサンプルがあります。
実行するとこんな感じです。

ProScalaFXのサンプルと同じようにコントローラーをScalaFXで書いてSystem.getPropertyで得られる値を一部GridPaneの表に表示する簡単なアプリを作ってみます。
今回もIntellij IDEA 12.1.6 ceを使います。
1. FileメニューよりNew Project...を選択。
プロジェクト名(GetProperty)とScala Homeを記入

2. Fileメニューから”Project Structure...”を選択
Project Settingsの中のLibrariesを選択

3. 左上の"+" をクリック
scalafx_2.10-1.0.0-M6.jarを追加する。
最新のjarはここにあります。
http://search.maven.org/#search%7Cga%7C1%7Cscalafx

4. srcフォルダをcontrolキー+クリック又はマウスの右クリックでNew=>Scala Classを作成します。
作成するのは次の2つのクラスです。
GetProperty.scala
GetPropertyController.scala


GetProperty.scala

/**
 * Created with Intellij IDEA.
 * User: hshino
 * Date: 13/10/31
 * Time: 18:30
 * To change this template use File | Settings | File Templates.
 */
import java.io.IOException
import javafx.{fxml => jfxf}
import javafx.{scene => jfxs}
import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.Scene

/** Example of using FXMLLoader from ScalaFX.
  */
object GetProperty extends JFXApp {

  val resource = getClass.getResource("GetProperty.fxml")
  if (resource == null) {
    throw new IOException("Cannot load resource: GetProperty.fxml")
  }

  val root: jfxs.Parent = jfxf.FXMLLoader.load(resource)

  stage = new PrimaryStage() {
    title = "FXML GetProperty Demo"
    scene = new Scene(root)
  }

}

GetPropertyController.scala

/**
 * Created with Intellij IDEA.
 * User: hshino
 * Date: 13/10/31
 * Time: 18:32
 * To change this template use File | Settings | File Templates.
 */
import java.net.URL
import java.util
import javafx.scene.{control => jfxsc}
import javafx.{event => jfxe}
import javafx.{fxml => jfxf}
import scalafx.scene.control.TextField

class GetPropertyController extends jfxf.Initializable {

  @jfxf.FXML private var tfDelegate1: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate2: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate3: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate4: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate5: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate6: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate7: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate8: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate9: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate10: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate11: jfxsc.TextField = null
  @jfxf.FXML private var tfDelegate12: jfxsc.TextField = null
  private var tf1: TextField = _
  private var tf2: TextField = _
  private var tf3: TextField = _
  private var tf4: TextField = _
  private var tf5: TextField = _
  private var tf6: TextField = _
  private var tf7: TextField = _
  private var tf8: TextField = _
  private var tf9: TextField = _
  private var tf10: TextField = _
  private var tf11: TextField = _
  private var tf12: TextField = _


  @jfxf.FXML
  private def handleButtonAction(event: jfxe.ActionEvent) {
    System.out.println("You clicked me!")

    tf1.setText(System.getProperty("java.vendor"))
    tf2.setText(System.getProperty("java.vendor.url"))
    tf3.setText(System.getProperty("java.home") )
    tf4.setText(System.getProperty("java.awt.graphicsenv"))
    tf5.setText(System.getProperty("java.class.version"))
    tf6.setText(System.getProperty("sun.boot.class.path"))
    tf7.setText(System.getProperty("java.version"))
    tf8.setText(System.getProperty("javafx.runtime.version"))
    tf9.setText(System.getProperty("java.runtime.version"))
    tf10.setText(System.getProperty( "os.name"))
    tf11.setText(System.getProperty( "os.arch" ))
    tf12.setText(System.getProperty("os.version" ))
  }


  def initialize(url: URL, rb: util.ResourceBundle) {
    tf1 = new TextField(tfDelegate1)
    tf2 = new TextField(tfDelegate2)
    tf3 = new TextField(tfDelegate3)
    tf4 = new TextField(tfDelegate4)
    tf5 = new TextField(tfDelegate5)
    tf6 = new TextField(tfDelegate6)
    tf7 = new TextField(tfDelegate7)
    tf8 = new TextField(tfDelegate8)
    tf9 = new TextField(tfDelegate9)
    tf10 = new TextField(tfDelegate10)
    tf11 = new TextField(tfDelegate11)
    tf12 = new TextField(tfDelegate12)
  }
}

5. fxmlファイルはSceneBuilder 1.1で作りIDEAのプロジェクトのフォルダに保存します。


GetProperty.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<AnchorPane id="anchorPane" prefHeight="500.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="GetPropertyController">
  <children>
    <VBox padding="$x2" prefHeight="500.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
      <children>
        <Button mnemonicParsing="false" onAction="#handleButtonAction" text="Click me!" textFill="RED">
          <font>
            <Font size="16.0" fx:id="x1" />
          </font>
        </Button>
        <GridPane prefHeight="382.0" prefWidth="565.0">
          <children>
            <Label alignment="CENTER" contentDisplay="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="java.vendor" textAlignment="LEFT" textFill="#0029ff" textOverrun="ELLIPSIS" underline="false" wrapText="false" GridPane.columnIndex="0" GridPane.rowIndex="0" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="java.vendor.url" textFill="#33ffe7" GridPane.columnIndex="0" GridPane.rowIndex="1" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="java.home" textFill="#ff9533" GridPane.columnIndex="0" GridPane.rowIndex="2" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="java.awt.graphicsenv" textFill="#ff66af" GridPane.columnIndex="0" GridPane.rowIndex="3" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="java.class.version" textFill="#00cc83" GridPane.columnIndex="0" GridPane.rowIndex="4" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="sun.boot.class.path" textFill="#0c9900" GridPane.columnIndex="0" GridPane.rowIndex="5" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="java.version" textFill="#1400ff" GridPane.columnIndex="0" GridPane.rowIndex="6" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="javafx.version" textFill="#66edff" GridPane.columnIndex="0" GridPane.rowIndex="7" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="java.runtime.version" textFill="#ffb800" GridPane.columnIndex="0" GridPane.rowIndex="8" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="os.name" textFill="#ff00b8" GridPane.columnIndex="0" GridPane.rowIndex="9" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="os.arch" textFill="#00e0ff" GridPane.columnIndex="0" GridPane.rowIndex="10" />
            <Label alignment="CENTER" font="$x1" prefHeight="28.0" prefWidth="280.0" text="os.version" textFill="#00cc21" GridPane.columnIndex="0" GridPane.rowIndex="11" />
            <TextField fx:id="tfDelegate1" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="0" />
            <TextField fx:id="tfDelegate2" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
            <TextField fx:id="tfDelegate3" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
            <TextField fx:id="tfDelegate4" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="3" />
            <TextField fx:id="tfDelegate5" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="4" />
            <TextField fx:id="tfDelegate6" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="5" />
            <TextField fx:id="tfDelegate7" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="6" />
            <TextField fx:id="tfDelegate8" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="7" />
            <TextField fx:id="tfDelegate9" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="8" />
            <TextField fx:id="tfDelegate10" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="9" />
            <TextField fx:id="tfDelegate11" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="10" />
            <TextField fx:id="tfDelegate12" prefHeight="32.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowIndex="11" />
          </children>
          <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          </columnConstraints>
          <rowConstraints>
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          </rowConstraints>
          <VBox.margin>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" fx:id="x2" />
          </VBox.margin>
        </GridPane>
      </children>
    </VBox>
  </children>
</AnchorPane>

6. 後はRunメニューからRunさせてください。
Click me!ボタンをクリックするとPropertyを表示します。

Java Mission Controlを使ってみる

1. まず適当なJavaアプリをFlightRecorderのオプションを指定してFlightRecordingします。
時間は60秒間で保存するファイル名はmyrecording.jfrです。
拡張子を.jfrとする事で後でJava Mission Controlに読み込ませ表示させます。
この場合のメインクラス名はFileChooserAndTabPaneです。
ターミナルで以下のようにコマンドを打ちます。

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:StartFlightRecording=duration=60s,filename=myrecording.jfr FileChooserAndTabPane

2. ファイルが作成されます。

3. Java Mission Controlを起動します。
ターミナルで以下のようにコマンドを打ちます。

/Library/Java/JavaVirtualMachines/jdk1.7.0_40.jdk/Contents/Home/bin/jmc

4. 起動したらFileメニューから”ファイルを開く”を選択して手順2で作成されたmyrecording.jfrを読み込み。

5. Fileメニューから”接続”を選択すると現在起動中のJavaアプリに接続できます。
6. 接続するJVMを選択してNext ボタンをクリックする。

7. ”JMXコンソールを開始します”を選択してFinishボタンをクリックする。

8. 表示される

Java Mission Control解説動画
ヘルプメニューでJava Mission Controlヘルプを選択するとSafariで表示されます。

Marcus Hirtさんのブログ
その他詳しい情報はこのページで
Java Platform, Standard Edition Java Flight Recorder Runtime Guide

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には参考になるコードが沢山あります。


ScalaFX 1.0 Milestone 5 が出ました

前回のM4からはだいぶ間があきましたがScalaFX 8と同時開発のようですからやむを得ないという感じでしょうか。
ScalaFX Downloads
M5の変更点はこちらが詳しいです。
What is New in ScalaFX 1.0 Milestone 5

PieChartが加わりましたね。
PieChartDemo.scala

import scalafx.application.JFXApp
import scalafx.collections.ObservableBuffer
import scalafx.scene.Scene
import scalafx.scene.chart.PieChart

object PieChartDemo extends JFXApp {

  val fruitsData = Seq(("みかん", 27), ("パイナップル", 11), ("りんご", 28), ("ぶどう", 30), ("梨", 15))
  stage = new JFXApp.PrimaryStage {
    title = "PieChart Demo by ScalaFX M5 "
    scene = new Scene {
      root = new PieChart() {
        title = "Pie Chart"
        clockwise = false
        data = ObservableBuffer(fruitsData.map {case (x, y) => PieChart.Data(x, y)})
      }
    }
  }
}

KotlinでJavaFX 2.2のContextMenuを利用する

JavaFX 2.2のContextMenuとはPopupMenuの事です。
今回はシンプルなボタンをクリックすると”Hello Kotlin World!”とラベルにテキストをセットするものにExitというメニューでJavaFXプログラムを終了させるContextMenuをマウスの右クリックで表示させます。
プログラムにはFXMLを使いコントローラークラスもKotlinで書きます。
コントローラークラスにはKotlinらしく関数リテラルも使ってみます。
コードは以下のようになります。

JavaFX_FXML_TEST.kt

package javafx_fxml_test

import javafx.application.Application
import javafx.fxml.FXMLLoader
import javafx.scene.Parent
import javafx.scene.Scene
import javafx.stage.Stage
import javafx.application.Platform

fun main(args: Array<String>) = Application.launch(JavaFX_FXML_TEST().javaClass, args.makeString(""))

public class JavaFX_FXML_TEST : Application() {

    override public fun init():Unit{

        println("javafx.version = " + System.getProperty("javafx.version"))
        println("init()はPlatform.isFxApplicationThread() が " + Platform.isFxApplicationThread())
    }


    override public fun start(p0: Stage?) : Unit {

        val root : Parent?  = FXMLLoader.load(getClass().getResource("/Sample.fxml"))

        val scene: Scene  = Scene(root)

        p0?.setScene(scene)
        p0?.show()
    }


}

SampleController.kt

package javafx_fxml_test

import java.net.URL
import java.util.ResourceBundle
import javafx.event.ActionEvent
import javafx.fxml.FXML
import javafx.fxml.Initializable
import javafx.scene.control.Label
import javafx.scene.layout.AnchorPane
import javafx.scene.input.MouseEvent
import javafx.scene.input.MouseButton
import javafx.scene.control.ContextMenu
import javafx.scene.control.MenuItem

public class SampleController() : Initializable {

  FXML
  private var label : Label? = null
  FXML
  private var anchorPane : AnchorPane? = null
  
  FXML
  private fun handleButtonAction(event : ActionEvent?) : Unit {
    println("You clicked me!" +"\n" + event)
    label?.setText("Hello Kotlin World!")
  }
  
  override
  public fun initialize(p0: URL?, p1 : ResourceBundle?) : Unit {
      val cm = ContextMenu()
      val cmItem1 = MenuItem("Exit")
      cmItem1.setOnAction{
         System.exit(0)
      }

      cm.getItems()?.add(cmItem1)

      val handler = {(p0:MouseEvent?) -> if (p0?.getButton() == MouseButton.SECONDARY) {

          cm.show(anchorPane, p0?.getScreenX() as Double, p0?.getScreenY() as Double)
          println("MOUSE_CLICKED x = " + p0?.getScreenX() + " y = " + p0?.getScreenY())
      }
      }

      anchorPane?.addEventHandler(MouseEvent.MOUSE_CLICKED, handler)

  }
  
}

Sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" fx:id="anchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml" fx:controller="javafx_fxml_test.SampleController">
    <children>
        <Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
        <Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
    </children>
</AnchorPane>