KotlinとTornadoFXで流れるHello world

# KotlinとTornadoFXで流れるHello world

## はじめに

JavaFXは、いわゆるPC上のデスクトップで動くかっこいいGUIを作るのが容易なGUIライブラリです。Javaのライブラリなので、Java SE（Java Platform, Standard Edition）が動作する環境で利用可能です。これは、Linux、MacOS、WindowsといったOSが動くPCとなります。また、Javaプログラムの特徴である、コンパイル結果のバイナリ（Javaバイトコード）がOSの違いによらず共通なので、どれかのOS上で作ったプログラムが他のOSでそのまま実行可能となります。

プログラミング言語としてのJavaは、オブジェクト指向プログラミングの機能を主としています。最近は関数型プログラミングの機能など、他の先進的なプログラミング言語の機能を取り入れつつありますが、先進的なプログラミング言語が持つ機能に比べると不十分な点があります。
しかし、環境としてのJavaは、豊富な標準ライブラリ、膨大なサードパーティライブラリやツールが存在し、Javaのエコシステムは非常に強固で他の言語のそれを凌駕しています。

ここで、ソースコード上はJavaとは別のプログラミング言語ですが、コンパイル結果はJavaバイトコードとなって、Java実行環境上で動かすことができる、JVM言語というプログラミング言語群が存在します。代表的なJVM言語には、Scala、Groovy、Clojure、およびKotlinといった新しい言語、並びにJRubyおよびJythonといった既存のプログラミング言語をJava上で動かすようにしたものがあります。

これらJVM言語からは、豊富なJavaの標準ライブラリやサードパーティライブラリを利用できます。したがって、JVM言語を使って先進のプログラミングをしつつ、Javaの豊富な資産を活用するというおいしいどこ取りができます。

JVM言語の一つKotlinは、汎用プログラミング言語としてのJavaの特徴を引き継ぎつつ、より簡潔に、より生産的に、より安全にプログラミングできる機能を持ったプログラミング言語です。また、Javaとの相互運用性を重視しており、KotlinからJavaを呼び出すだけでなく、JavaからKotlinを呼び出すこともできるように工夫されています。

このKotlinからJavaFXを利用するにあたり、直接JavaFXのAPIを叩く使い方では、Kotlinならではの機能（より簡潔／生産的／安全）が十分生かせません。そこで、JavaFXを徹底的にKotlin流儀に仕立て直したライブラリであるTornadoFXが開発されました。

本記事は、いわゆるHello world的なプログラムをJavaFXらしくちょっと動きが伴ったプログラムを作るという次の記事と

* [JavaFXで流れるHello world](https://qiita.com/boochnich/items/0e5a34e8d8a09c9fbfa8)

このプログラムをKotlinで作るという次の記事

* [KotlinとJavaFXで流れるHello world](https://qiita.com/boochnich/items/6b6f0c4e5d7494484391)

に引き続く3部作目として、KotlinからTornadoFXを介してJavaFXを呼び出して作る内容となります。

## KotlinとTornadoFXのプログラミング環境

Kotlinプログラミング環境は、IntelliJ IDEA Community Edition[^1]を使います。

[^1]: IntelliJ IDEAは、JetBrains社が開発・販売しているプログラミングツール（統合開発環境IDE）です。Community Editionは機能が絞られていますが無償で提供されており、Kotlin開発環境もCommunity Editionで標準搭載されています。また、TornadeFXプラグインも存在しています。

### 本記事の執筆・検証環境

| 項目 | 内容 |
|:-:|:-:|
|CPU|AMD PhenomⅡ 1055T（6コア） |
|OS |日本語Windows 10 64bit版 |
|メモリ|16GB |
|グラフィックス | NVIDIA GeForce GTX 760   |
|ディスク |SATA SSD 500GB |
|JDK | Oracle JDK 9.0.4 |
|IntelliJ IDEA | 2017.3.5 Community Edition |
|TornadoFX | 1.7.14 |

IntelliJ IDEAのダウンロードURL
https://www.jetbrains.com/idea/download/

IntelliJ IDEAはPleiadesと呼ばれる日本語化ツールを適用して日本語化しています[^1]。本記事でIntelliJ IDEAの操作手順を記載する際は、日本語化されたメニュー、ボタン、タブ等の名称を使用します。
[IntelliJベースの製品の日本語化マニュアル](https://www.willbrains.jp/page/4)

[^1]: IntelliJ 2020からは、Japanese Language Packプラグインが提供されるようになったので、Pleiadesによる日本語化は非推奨。

Oracle JDK 9.0.4のダウンロードURL
http://www.oracle.com/technetwork/java/javase/downloads/index.html

### TornadoFX プラグインのインストール

IntelliJ IDEAにTornadoFXのプラグインをインストールします。

* [ファイル]メニュー > [設定] で「設定」画面を開き、左側ペインで[プラグイン]を選択、右側ペインの下にある[リポジトリーの参照]ボタンをクリックします。
* 「リポジトリーの参照」画面で、検索欄にTornadoFXを入れ（途中まででも可）、一覧に表示される[TornadoFX]を選択、右側ペインの[インストール]ボタンをクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/8800948c-dca3-d5d6-215b-093092b48911.png)

* [IntelliJ再起動]ボタンをクリックし、IntelliJ IDEAを再起動します。
* IntelliJ IDEA再起動後、[ファイル]メニュー　> [新規] > [プロジェクト]を選択し、「新規プロジェクト」画面を開きます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/31a79f23-51de-7033-b131-de463ecf93d8.png)

左側ペインに[TornadoFX]が存在し、それを選択すると右側ペインにmavenまたはgradleのプロジェクト種類が一覧されています。

### Gradleのインストール

IntelliJ IDEAのGradleプラグインに含まれるGradleは、2018年3月15日現在でバージョンが4.0です。これをJDK 9で使用するとエラーとなります。

```
Could not determine java version from '9.0.4'.
```

JDK 9対応は、Gradle 4.2.1以降なので、この場合は、GradleのwrapperではなくローカルにGradle 4.2.1以降を別途インストールして使用するのがよいでしょう。

### MessageBoardプロジェクトの作成

* [ファイル]メニュー > [新規] > [プロジェクト]を選択し、「新規プロジェクト画面」を開きます。
* 左側ペインで[TornadoFX]を選択し、右側ペインでtornadofx-gradle-projectを選択し、[次へ]ボタンをクリックします。
* [プロジェクト名]欄に"MessageBoard"を入力します。
* [基本パッケージ]欄に"com.torutk.messageboard"を入力します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/a9743f90-88dc-d0f8-884b-7e6c36719185.png)

デフォルトで生成されるディレクトリと設定ファイル、サンプルのソースファイルがあります。またステータス領域（IntelliJ IDEA画面下部）に、Gradleのインポートをするよう通知があります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/605128c1-fe3c-4ce6-4f60-d224942faf27.png)

IntelliJ IDEA画面右下隅の通知アイコンをクリックすると、[イベント・ログ]が開き、メッセージが記載されています。
プロジェクトを新規作成直後は、まだ依存関係で定義したライブラリのダウンロード等が未実施なため、最初にGradleのインポートを実施します。[イベント・ログ]領域のメッセージ中の[Gradleプロジェクトのインポート]をクリックします。クリックすると、「Gradleからモジュールをインポート」画面が開きます[^2]。

[^2]: この画面を[キャンセル]で閉じたら、次にどこからこの画面が呼び出せるかわからず四苦八苦しました。[イベント・ログ]のメッセージ中のリンクはグレーアウトでリンクが無効となっていました。また、[表示]メニュー > [ツール・ウィンドウ]で開くメニュー項目には[Gradle]が含まれていません。一度IntelliJ IDEAを閉じて再度起動すると、[イベント・ログ]メッセージのリンクが有効になっていたので、やっと再度画面を開くことができました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/d157ea68-95be-239e-222e-4c28c955a776.png)

* [自動インポートを使用する]にチェックを付けます。
  * build.gradleを変更すると自動的にチェックと必要であればダウンロードが走ります。大きなプロジェクトでは外した方が無難かも。
* [ローカルのgradleディストリビューションを使用する]を選択、gradleがインストールされたディレクトリを指定します。
  * [デフォルトの gradle ラッパーを使用する]は選択不可能でした。

[OK]ボタンをクリックすると、設定が始まります。
 
なお、インターネット接続にファイアウォールがある環境では、プロキシサーバーの設定が必要になるかと思いますが、本記事では説明を割愛します[^3]。

[^3]: 本記事は自宅でプロキシサーバーがない環境で書いています。

### 雛形で生成されたプログラムの実行

TornadoFXプラグインで生成したプロジェクトには、デモコードとしてMyApp、Styles、MainViewの3つのソースファイルが含まれています。これをビルド・実行することでプログラミング環境が正しく構築できたかの確認とします。

[実行]メニューを開くと、[実行]、[デバッグ]や[カバレッジで実行]は無効化されています。最初に、起動設定をする必要があります。一番簡単な方法は、プログラム実行のエントリポイントであるApp継承クラスのファイル（デモコードではMyApp）を右クリックし[実行]を選択します。

うまく実行できると、次の画面が表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/d299059a-e3fb-4fc3-2d14-2134b0c150eb.png)

次からは、[実行]メニューを開くと、[実行]、[デバッグ]や[カバレッジで実行]が有効化されています。また、[実行]メニュー > [起動構成の実行]を選択すると、選択肢に[MyApp]が追加されています。

## 最初のTornadoFXプログラミング

TornadoFXのプロジェクトを生成すると、デモコードが生成されますが、それらは捨てて、次の2つのクラスを作成します。

| クラス名 | 内容 |
|:-:|:-:|
| MessageBoardApp | JavaFXのApplicationクラスをラップしたTornadoFXのAppを継承するクラス |
| MessageBoardView  | シーングラフを示す、TornadoFXのViewを継承するクラス |
 
デモコードでは、パッケージがappとviewとに分かれていましたが、今回は同じパッケージに配置します。

```Kotlin:MessageBoardApp.kt
package com.torutk.messageboard

import tornadofx.App

class MessageBoardApp: App(MessageBoardView::class)
```

MessageBoardAppは、JavaFXのApplicationクラスを継承するTornadoFXのAppクラスを継承し、ビュー（シーングラフ）を定義するMessageBoardViewクラスをプライマリコンストラクタの引数で受け取ります。
コードの実装がまったくないのが特徴です。JavaFXのApplication継承クラスでは、startメソッドをオーバーライドし、StageとSceneをごそごそいじるお決まりのコードを記述していました。TornadoFXではこのお決まりのコードの記述を不要としています。

```Kotlin:MessageBoardView.kt
package com.torutk.messageboard

import javafx.scene.Group
import javafx.scene.text.Text
import tornadofx.*

class MessageBoardView : View() {
    override val root = Group()

    init {
        val message = Text("Hello, Kotlin. This is TornadoFX.")
        message.layoutY = 50.0
        root += message
    }
}
```

MessageBoardViewは、TornadoFXのViewクラスを継承し、ルートノードとしてJavaFXのGroupを生成、Groupの子ノードとしてtextを生成しシーングラフを構成しています。
ルートノードはプロパティとしてViewクラスに定義されるのでoverrideでオーバーライドし、子ノードを定義します。
rootプロパティのオーバーライドでは、Groupクラスのインスタンスを生成し代入しています。

initブロックは、初期化ブロックです。プライマリコンストラクタはプロパティの定義しかできないので、プライマリコンストラクタでインスタンス化する際、initブロックに記述されたコードが実行されます。initブロックの中ではプロパティ参照が可能です。

このinitブロックの中で、Textのインスタンス化、位置設定（layoutY）、そしてルートノードへ子ノードとして追加する処理をしています。ルートノードへの子ノード追加では、演算子オーバーロードを使っています。

では、実行してみましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/1a2bb92b-f33c-476e-1974-d147aa005a1a.png)

## TornadoFXっぽい書き方

### ビルダーパターンを適用し、ルートノードの中にTextを定義

JavaFXのコントロールを生成する関数が用意されています。これらはラムダ式を引数の最後に取ります。

次のコードは、ルートノードにGroupを生成するビルダー group関数を定義し、その中でTextを生成するビルダー text関数を使用しています。

```Kotlin:MessageBoardView.kt
class MessageBoardView : View() {
    override val root = group {
        text("Hello, Kotlin. This is TornadoFX.") {
            layoutY = 50.0
        }
    }
}
```

このように、シーングラフの階層構造を、ネスト構造で表現することができます。

### Textにアニメーションを付与

流れるメッセージは、JavaFXのTranslateTransitionを使用します。TornadoFXでは、拡張関数のmoveでTranslateTransitionを使ったアニメーションを記述します。

```Kotlin:MessageBoardView.kt
class MessageBoardView : View() {
    override val root = group {
        text("Hello, Kotlin. This is TornadoFX.") {
            layoutY = 50.0
            move(Duration.seconds(8.0), Point2D(-layoutBounds.width, 0.0), Interpolator.LINEAR, play = true) {
                fromX = layoutBounds.width
                cycleCount = Animation.INDEFINITE
            }
        }
    }
}
```

moveの引数で、持続時間、移動先、補間方法、アニメーションをすぐに実行するかを指定します。moveの引数では移動元、繰り返し回数の指定が用意されていなかったので、ラムダ式でそれらを指定しています。

実行します。

![run-1.gif](https://qiita-image-store.s3.amazonaws.com/0/100353/36c289c6-27dd-5318-64e1-c30dec9fe121.gif)


### Textに見栄えを指定

TornadoFXでは、フォントや色などの見栄えをKotlinのDSLで実現したCSSで定義しますが、今回は流れるアニメーションの都合でCSSではなくJavaFXのAPIで定義します。

```Kotlin:MessageBoardView.kt
class MessageBoardView : View() {
    override val root = group {
        text("Hello, Kotlin. This is TornadoFX.") {
            font = Font.font("Serif", FontWeight.SEMI_BOLD, 32.0)
            fill = Color.DARKMAGENTA
            layoutY = 50.0
            move(Duration.seconds(8.0), Point2D(-layoutBounds.width, 0.0), Interpolator.LINEAR, play = true) {
                fromX = layoutBounds.width
                cycleCount = Animation.INDEFINITE
            }
        }
    }
}
```

実行します。

![run-2.gif](https://qiita-image-store.s3.amazonaws.com/0/100353/ced89a08-702f-dfe2-869a-e76a823da53a.gif)