1 /* 2 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package examples 6 7 import javafx.application.* 8 import javafx.scene.* 9 import javafx.scene.control.* 10 import javafx.scene.layout.* 11 import javafx.scene.paint.* 12 import javafx.scene.shape.* 13 import javafx.stage.* 14 import kotlinx.coroutines.* 15 import kotlinx.coroutines.javafx.* 16 import java.text.* 17 import java.util.* 18 import kotlin.coroutines.* 19 mainnull20fun main(args: Array<String>) { 21 Application.launch(FxTestApp::class.java, *args) 22 } 23 lognull24fun log(msg: String) = println("${SimpleDateFormat("yyyyMMdd-HHmmss.sss").format(Date())} [${Thread.currentThread().name}] $msg") 25 26 class FxTestApp : Application(), CoroutineScope { 27 val buttons = FlowPane().apply { 28 children += Button("Rect").apply { 29 setOnAction { doRect() } 30 } 31 children += Button("Circle").apply { 32 setOnAction { doCircle() } 33 } 34 children += Button("Clear").apply { 35 setOnAction { doClear() } 36 } 37 } 38 39 val root = Pane().apply { 40 children += buttons 41 } 42 43 val scene = Scene(root, 600.0, 400.0) 44 45 override fun start(stage: Stage) { 46 stage.title = "Hello world!" 47 stage.scene = scene 48 stage.show() 49 } 50 51 val random = Random() 52 var animationIndex = 0 53 var job = Job() 54 override val coroutineContext: CoroutineContext 55 get() = Dispatchers.JavaFx + job 56 57 private fun animation(node: Node, block: suspend CoroutineScope.() -> Unit) { 58 root.children += node 59 launch(block = block).also { 60 it.invokeOnCompletion { root.children -= node } 61 } 62 } 63 64 fun doRect() { 65 val node = Rectangle(20.0, 20.0).apply { 66 fill = Color.RED 67 } 68 val index = ++animationIndex 69 val speed = 5.0 70 animation(node) { 71 log("Started new 'rect' coroutine #$index") 72 var vx = speed 73 var vy = speed 74 var counter = 0 75 while (true) { 76 awaitPulse() 77 node.x += vx 78 node.y += vy 79 val xRange = 0.0 .. scene.width - node.width 80 val yRange = 0.0 .. scene.height - node.height 81 if (node.x !in xRange ) { 82 node.x = node.x.coerceIn(xRange) 83 vx = -vx 84 } 85 if (node.y !in yRange) { 86 node.y = node.y.coerceIn(yRange) 87 vy = -vy 88 } 89 if (counter++ > 100) { 90 counter = 0 91 delay(1000) // pause a bit 92 log("Delayed #$index for a while, resume and turn") 93 val t = vx 94 vx = vy 95 vy = -t 96 } 97 } 98 } 99 } 100 101 fun doCircle() { 102 val node = Circle(20.0).apply { 103 fill = Color.BLUE 104 } 105 val index = ++animationIndex 106 val acceleration = 0.1 107 val maxSpeed = 5.0 108 animation(node) { 109 log("Started new 'circle' coroutine #$index") 110 var sx = random.nextDouble() * maxSpeed 111 var sy = random.nextDouble() * maxSpeed 112 while (true) { 113 awaitPulse() 114 val dx = root.width / 2 - node.translateX 115 val dy = root.height / 2 - node.translateY 116 val dn = Math.sqrt(dx * dx + dy * dy) 117 sx += dx / dn * acceleration 118 sy += dy / dn * acceleration 119 val sn = Math.sqrt(sx * sx + sy * sy) 120 val trim = sn.coerceAtMost(maxSpeed) 121 sx = sx / sn * trim 122 sy = sy / sn * trim 123 node.translateX += sx 124 node.translateY += sy 125 } 126 } 127 } 128 129 fun doClear() { 130 job.cancel() 131 job = Job() 132 } 133 }