Reputation: 539
I am trying to learn Kotlin and TornadoFX, and I am working from this repository
The idea currently is that the app will show three rows of 7 buttons. Each button represents a letter which is either known or unknown. If the letter is known, it is displayed on the button. If the letter is unknown, the button displays the index (0-20).
I did this by creating (using Java thinking I'm sure) a global array with 21 elements. Each element starts out null. I defined this in the main app, using the companion construct:
class InteractiveClientApp : App(MainView::class) {
companion object {
var knownLetters = arrayOfNulls<String>(21)
}
Then in the App constructor, I initialized a few random elements with values, just to see if this would work (it doesn't).
override fun init() {
knownLetters[4] = "T"
knownLetters[9] = "G"
knownLetters[17] = "Z"
}
Then in the LettersGridView I use forEachIndexed on the knownLetters array, so I would have access to the element from the array and the index for it.
import jcn.deduce.client.InteractiveClientApp.Companion.knownLetters
class LettersGridView : View() {
override val root = gridpane {
knownLetters.forEachIndexed { index, element ->
if (index == 0 || index % 7 == 0) {
row {
button(element?.toString() ?: index.toString()) {
useMaxWidth = true
gridpaneConstraints {
marginBottom = 10.0
}
}
}
} else {
button(element?.toString() ?: index.toString()) {
useMaxWidth = true
gridpaneConstraints {
marginBottom = 10.0
}
}
}
}
}
}
Whats actually happening is three buttons appear instead of the expected 21, and the value on the button is always the index, never a letter value. Also the indexes shown are 20, 7 and 14. Not the three I used when setting elements in the array. So I am missing something there.
Also I think I am not understanding this bit correctly:
button(element?.toString() ?: index.toString()) {
useMaxWidth = true
gridpaneConstraints {
marginBottom = 10.0
}
}
What I am trying to say there is "If the element value is not null, use the element value, otherwise use the string value of index. This isn't working, because the buttons only ever have indexes on them, never letters.
I notice if I leave off the .toString() on element, I get an error that button expects String, not string?. Which seems somewhat similar to Java and String vs Optional < String >. However, when I add the toString(), I get an IDE warning that the toString() is redundant.
If I take off the trailing ? altogether, I get a clean compile, but still only three buttons render, and their labels are index, not element.
So I am pretty sure I got lost somewhere along the way, can anyone explain why my program isn't working?
Also, when I am debugging the app, I always wind up with two processes. I don't understand why, but this is what IntelliJ looks like:
Is this normal?
Upvotes: 1
Views: 146
Reputation: 166
Your init function is working, you can confirm this by changing the initialisation of entry 20, 7 or 14 to a letter and you should see a letter appear when you next run it.
As for your main issue, the reason you are seeing 20, 7 and then 14 is because in this section:
if (index == 0 || index % 7 == 0) {
row {
button(element?.toString() ?: index.toString()) {
useMaxWidth = true
gridpaneConstraints {
marginBottom = 10.0
}
}
}
}
You are adding a row with a single button, these buttons will be 0, 7 and 14 (since they are all == 0 % 7). This means you'll only ever add three rows, each with one button on. You might be confused as to why it says 20 instead of 0... Well this is because the next section:
else {
button(element?.toString() ?: index.toString()) {
useMaxWidth = true
gridpaneConstraints {
marginBottom = 10.0
}
}
}
Is also adding buttons, but not onto any row (notice how these buttons aren't inside a row { } lambda). This means all these buttons will get added to the gridpane on top of eachother, including that first 0 button. The last button to be added is 20, hence why you see 20, it is covering the 0!
Here's an example of how to approach this problem:
val rowSize = 7
val rows = knownLetters.toList().chunked(rowSize)
rows.forEachIndexed { rowIndex, elements ->
row {
elements.forEachIndexed { buttonIndex, element ->
val displayIndex = rowSize * rowIndex + buttonIndex
button("${element ?: displayIndex}") {
useMaxWidth = true
gridpaneConstraints {
marginBottom = 10.0
}
}
}
}
}
This takes the Kotlin libraries' "chunked" method to divide your 21 size array into three lists of size 7. You can then loop through (you do have to piece back together the display index with this approach) creating a new row for each list (3 lists makes 3 rows), whilst creating your buttons in a nested loop inside the row's lambda.
The key thing to take away here is that not all your buttons are being added within a row { } lambda.
As for the double process issue, I do not have this issue if I run the app using a main method like so:
fun main(args: Array<String>) {
launch<InteractiveClientApp>(args)
}
Hope this helps!
Upvotes: 3