There must be text things in JavaFX/TornadoFX. Everyone has them. How hard can it be? Wow, I hate being ignorant. But I love learning!

There are worse things than being ignorant, and in my life, I’ve been many of them. I’m sorry now. Anyway, the nice thing about ignorance is that it is correctable, and it’s often even fun to get rid of it. Well, to lessen it: I’m pretty sure I have an unlimited supply of ignorance.

My plan for the morning, and cut me a break here, it’s Saturday, how much can you really expect, where was I oh yes my plan is to push further on my “UI”, which when it is complete should consist of a text input line at the bottom of a window, and a scrolling text panel above it containing the game’s output. I’m sure most of you could just whip that right out, and I’m a bit disappointed that no one tweeted the solution to me yesterday or overnight, but I guess today it comes down to just me and the Internet.

Where Are We?

I don’t mean some astronomical coordinates, I just mean what code do we have that we might be able to transform into a window we want.

I may have mentioned in the past that I think people learning programming go through an early phase where, given a program that works, they can hammer it into a different program that works, but cannot really write a program from scratch yet. That’s certainly where I am with windows, and here’s my application so far:

var myLabel: Label by singleAssign()

class MainView: View() {
    override val root: Parent = vbox {
        minWidth = 400.0
        minHeight = 200.0
        myLabel = label("Hello Ron")
        button("Press me") {
            action { someonePressedMe() }
        }
    }
}

fun someonePressedMe() {
    println("pressed")
    myLabel.text = "I saw that!"
}

class HelloApplication : App(MainView::class) {
}

fun main() {
    launch<HelloApplication>()
}

Beginning at the beginning, let me say that I don’t understand any details of what by singleAssign() means. I’ll make a point of reading about it soon.

Our window has a vbox, a thing that stacks children vertically, containing a label and a button. The button has an action, which is a call to the function someonePressedMe, which changes the text of the label.

So, what I want, instead of that label, is some kind of text displaying thing that can be multiple lines. There seems to be a thing called TextArea that might do the job. Let’s try one of those instead of the label.

var myText: TextArea by singleAssign()

class MainView: View() {
    override val root: Parent = vbox {
        minWidth = 400.0
        minHeight = 200.0
        myText = textarea("Hello Ron")
        button("Press me") {
            action { someonePressedMe() }
        }
    }
}

I just tried typing textarea in the middle of the View, and the various error messages led me to declare it. I changed the name on my own, so I deserve some credit for that. The good news is that I get a decent-looking window and the text changes when the button is pressed.

window saying Hello Ron
window saying I saw that!

So that’s good. Let me see whether I can add text to the textarea, and see what happens if I add a lot.

I “enhance” the program:

var count = 1
var textContents = "Hello Ron"

class MainView: View() {
    override val root: Parent = vbox {
        minWidth = 400.0
        minHeight = 200.0
        myText = textarea(textContents)
        button("Press me") {
            action { someonePressedMe() }
        }
    }
}

fun someonePressedMe() {
    println("pressed")
    textContents = textContents+"\nI saw that again $count"
    myText.text = textContents
    count++
}

The good news is that now, every time I press the button, the program adds a line to the textarea. The bad news is that even after it fills the window, it doesn’t automatically scroll to the bottom. Still, this is great progress.

window showing multiple I saw that lines

I’ll bet the text thing knows how to do stuff. Wow, don’t press mytext. unless you have some time to spend. IDEA shows me a list of a thousand things you can say to a text area!

A little Internet searching and I find that if you appendText, the cursor will be set to the end of the pane. So my next cut is this:

fun someonePressedMe() {
    var newLine = "\nI saw that again $count"
    myText.appendText(newLine)
    count++
}

And that works just exactly as I had in mind. When I append past the end, I see the new lines, not the old ones:

window scrolled to bottom

However, it is the case that I can type into the thing:

window with my typing in it

I’ll search for read only textarea. setEditable(false) Sure, why not.

IDEA, Kotlin, and I do not agree on how to do this and we decide to do it as they suggest:

        myText = textarea(textContents) {
            isEditable = false
        }

Super. Now my window is read-only. When I try to type into it, nothing happens, just as I planned all along.

Let’s commit this and reflect. Commit: Window has text area, button writes text into it, scrolls to end.

Reflection

What have I learned here? A tiny bit, but important bits.

  • I can create a window with a scrolling text area, as needed for the game’s ongoing report. This is a text game, after all.
  • The text area automatically scrolls to the bottom, which is what we want.
  • The text area is read-only.

Now, there’s a clear next step, which is to change the button to a text input field, possibly the widget named TextField. When you type a command into that, I’ll want to echo the command to the text area, followed by the game’s output.

Actually that’s two steps at least, first, echoing what you type, second running the game and appending its output. Maybe we can make it more than two steps. If we can, that’ll be good. Many More Much Smaller Steps.

I could press on and do that now. It’ll be quite easy, surely no harder than what we just did.

But in the interest of keeping my workday short, and keeping the article short, we’ll stop here, with yet another small win racked up on the scoreboard.

But what’s left to learn? Probably quite a lot. While this connection between the button and the window and the text all works, it’s also all tangled together in this one file. Since it’s only a few lines, it probably doesn’t matter, but there’s surely a better style of doing these things, a style that may not pay off much right now, but that is more like what we want for a larger program. I’ll leave that to my betters for now.

Small learning steps are good steps.

My learning is very thin. I’m beginning to find my way around IDEA’s prompts and its builder style, which is actually pretty nice. I’ve just learned a couple of the parameters of the various widgets, and I’ll surely need to learn more, the better to control the window size and layout. But though I only know a very few things, I am beginning to learn where things are, so that when I don’t know how to do a thing, I’m better and finding it in IDEA, and better at finding it on the Internet. Both of these simple skills are quite valuable.

In the olden days, I’d have had to buy a book on JavaFX or TornadoFX, probably both, and paged through the books to find the isEditable trick or whatever I needed. It took much longer in those days, and it cost more as well. I have probably a half-ton of technical books that my heirs will have to get rid of. At $20 a pound, that’s a lot of herring.

But every day, in an hour or so, I manage to make actual progress, and to learn a bit. Keep that up and in only a few decades, I might be pretty good at this. If I last that long.

Seriously, though, all it takes is a lot of small wins and pretty soon you’ve got a real program, doing what you set out to do.

Small steps, small wins. It adds up.

See you next time!