• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
mainnull16 fun main(args: Array<String>) {
17     Application.launch(FxTestApp::class.java, *args)
18 }
19 
lognull20 fun 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 }