# Updating panel elements during runtime

**URL:** <https://openrndr.discourse.group/t/updating-panel-elements-during-runtime/594>\
**Category:** How to?\
**Created:** [August 27, 2023, 9:26pm UTC](https://openrndr.discourse.group/t/updating-panel-elements-during-runtime/594 "2023-08-27T21:26:48Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![daymon](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/daymon/32/626_2.png) [@daymon](https://openrndr.discourse.group/u/daymon)\
**Post date:** [August 27, 2023, 9:26pm UTC](https://openrndr.discourse.group/t/updating-panel-elements-during-runtime/594/1 "2023-08-27T21:26:48Z")

</div>

Hey folks!

How exactly should one go about updating the UI (`org.openrndr.panel`) during runtime?  
For example, given the following:

```auto
extend(ControlManager()) {
  layout {
    p { "Shapes on screen: ${shapes.length}" }
  }
}

```

I can’t seem to find a way to force a re-draw that actually re-computes the text in the `p` element.

My apologies if this is something super straight forward that I missed :')

---

<div class="post-metadata">

**Author:** ![daymon](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/daymon/32/626_2.png) [@daymon](https://openrndr.discourse.group/u/daymon)\
**Post date:** [August 28, 2023, 3:18am UTC](https://openrndr.discourse.group/t/updating-panel-elements-during-runtime/594/2 "2023-08-28T03:18:14Z")

</div>

It seems this isn’t directly ~~possible~~ supported. The init block for `textElement` (and probably most others) doesn’t get saved:

```auto
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`:

```auto
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:

```auto
(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:

```auto
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:

```auto
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:

```auto
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.
