package examples import javafx.application.* import javafx.scene.* import javafx.scene.control.* import javafx.scene.layout.* import javafx.scene.paint.* import javafx.scene.shape.* import javafx.stage.* import kotlinx.coroutines.* import kotlinx.coroutines.javafx.* import java.text.* import java.util.* import kotlin.coroutines.* fun main(args: Array) { Application.launch(FxTestApp::class.java, *args) } fun log(msg: String) = println("${SimpleDateFormat("yyyyMMdd-HHmmss.sss").format(Date())} [${Thread.currentThread().name}] $msg") class FxTestApp : Application(), CoroutineScope { 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() var animationIndex = 0 var job = Job() override val coroutineContext: CoroutineContext get() = Dispatchers.JavaFx + job private fun animation(node: Node, block: suspend CoroutineScope.() -> Unit) { root.children += node launch(block = block).also { it.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) { 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) { 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() { job.cancel() job = Job() } }