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