It seems this isn’t directly possible supported. The init block for textElement
(and probably most others) doesn’t get saved:
inline fun <reified T : TextElement> Element.textElement(classes: Array<out String>, init: T.() -> String): T {
val te = T::class.java.newInstance()
te.classes.addAll(classes.map { ElementClass(it) })
te.text(te.init()) // init being used, but not saved anywhere
append(te)
return te
}
Which I believe to be a design decision, as I also found the following methods on TextElement
:
TextElement.replaceText(text: String)
TextElement.bind(property: KMutableProperty0<String>)
Bind just calls replaceText
whenever the provided property updates, and replaceText
casts its first child to a TextNode
and sets the text directly, as can be seen below:
(children.first() as? TextNode)?.text = text
requestRedraw()
Unfortunately, you can’t bind
on the spot, since the underlying function needs to access program
to bind to updates, resulting in a UninitializedPropertyAccessException
.
Which is kind of a funny, as if you set the property yourself- everything works fine:
var temp = "Hello World!"
fun main() = application {
program {
extend(ControlManager()) {
program = this@program
layout {
p {
bind(::temp) // works fine
temp
}
}
}
}
}
As such, with a little bit of Kotlin magic, and taking some bits from the existing openrndr source:
context(Program)
inline fun <reified T : TextElement> Element.textElement(classes: Array<out String>, crossinline init: T.() -> String): T {
val te = T::class.java.newInstance()
te.classes.addAll(classes.map { ElementClass(it) })
var currentText = te.init()
te.text(currentText)
append(te)
launch {
while(true) {
val newText = te.init()
if(currentText != newText) {
currentText = newText
te.replaceText(newText)
}
yield()
}
}
return te
}
context(Program)
fun Element.p(vararg classes: String, init: P.() -> String): P = textElement(classes, init)
Tada! Which allows you to do things like this:
fun main() = application {
program {
val shapes = mutableListOf<Shape>()
extend(ControlManager()) {
layout {
p { "Shapes on screen: ${shapes.size}" }
}
}
}
}
And the init block will properly update the text.