blob: 8db651f65488dde8881e725597248bc6e006bdbb [file] [log] [blame]
/*
* Copyright 2016-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package examples
import javafx.application.Application
import javafx.scene.Node
import javafx.scene.Scene
import javafx.scene.control.Button
import javafx.scene.layout.FlowPane
import javafx.scene.layout.Pane
import javafx.scene.paint.Color
import javafx.scene.shape.Circle
import javafx.scene.shape.Rectangle
import javafx.stage.Stage
import kotlinx.coroutines.experimental.CoroutineScope
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.javafx.JavaFx
import kotlinx.coroutines.experimental.launch
import java.text.SimpleDateFormat
import java.util.*
fun main(args: Array<String>) {
Application.launch(FxTestApp::class.java, *args)
}
fun log(msg: String) = println("${SimpleDateFormat("yyyyMMdd-HHmmss.sss").format(Date())} [${Thread.currentThread().name}] $msg")
class FxTestApp : Application() {
val buttons = FlowPane().apply {
children += Button("Rect").apply {
setOnAction { doRect() }
}
children += Button("Circle").apply {
setOnAction { doCircle() }
}
children += Button("Clear").apply {
setOnAction { doClear() }
}
}
val root = Pane().apply {
children += buttons
}
val scene = Scene(root, 600.0, 400.0)
override fun start(stage: Stage) {
stage.title = "Hello world!"
stage.scene = scene
stage.show()
}
val random = Random()
val animations = arrayListOf<Job>()
var animationIndex = 0
private fun animation(node: Node, block: suspend CoroutineScope.() -> Unit) {
root.children += node
val job = launch(JavaFx, block = block)
animations += job
job.invokeOnCompletion { root.children -= node }
}
fun doRect() {
val node = Rectangle(20.0, 20.0).apply {
fill = Color.RED
}
val index = ++animationIndex
val speed = 5.0
animation(node) {
log("Started new 'rect' coroutine #$index")
var vx = speed
var vy = speed
var counter = 0
while (true) {
JavaFx.awaitPulse()
node.x += vx
node.y += vy
val xRange = 0.0 .. scene.width - node.width
val yRange = 0.0 .. scene.height - node.height
if (node.x !in xRange ) {
node.x = node.x.coerceIn(xRange)
vx = -vx
}
if (node.y !in yRange) {
node.y = node.y.coerceIn(yRange)
vy = -vy
}
if (counter++ > 100) {
counter = 0
delay(1000) // pause a bit
log("Delayed #$index for a while, resume and turn")
val t = vx
vx = vy
vy = -t
}
}
}
}
fun doCircle() {
val node = Circle(20.0).apply {
fill = Color.BLUE
}
val index = ++animationIndex
val acceleration = 0.1
val maxSpeed = 5.0
animation(node) {
log("Started new 'circle' coroutine #$index")
var sx = random.nextDouble() * maxSpeed
var sy = random.nextDouble() * maxSpeed
while (true) {
JavaFx.awaitPulse()
val dx = root.width / 2 - node.translateX
val dy = root.height / 2 - node.translateY
val dn = Math.sqrt(dx * dx + dy * dy)
sx += dx / dn * acceleration
sy += dy / dn * acceleration
val sn = Math.sqrt(sx * sx + sy * sy)
val trim = sn.coerceAtMost(maxSpeed)
sx = sx / sn * trim
sy = sy / sn * trim
node.translateX += sx
node.translateY += sy
}
}
}
fun doClear() {
animations.forEach { it.cancel() }
}
}