[orx-panel] Add WatchObjectDiv

This commit is contained in:
Edwin Jakobs
2020-07-15 14:31:43 +02:00
parent 3a6e787a46
commit 31dd1a6fa2
4 changed files with 172 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ sourceSets {
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion")
demoImplementation("org.openrndr:openrndr-dialogs:$openrndrVersion")
demoImplementation("com.google.code.gson:gson:$gsonVersion")

View File

@@ -0,0 +1,75 @@
import org.openrndr.application
import org.openrndr.extensions.SingleScreenshot
import org.openrndr.panel.controlManager
import org.openrndr.panel.elements.*
import org.openrndr.panel.style.*
fun main() = application {
configure {
width = 900
height = 720
}
// A very simple state
class State {
var x = 0
var y = 0
var z = 0
}
program {
// -- this block is for automation purposes only
if (System.getProperty("takeScreenshot") == "true") {
extend(SingleScreenshot()) {
this.outputFile = System.getProperty("screenshotPath")
}
}
val programState = State()
val cm = controlManager {
layout {
styleSheet(has class_ "matrix") {
this.width = 100.percent
}
styleSheet(has class_ "row") {
this.display = Display.FLEX
this.flexDirection = FlexDirection.Row
this.width = 100.percent
child(has type "slider") {
this.width = 80.px
}
}
slider {
label = "x"
precision = 0
bind(programState::x)
}
slider {
label = "y"
precision = 0
bind(programState::y)
}
watchObjectDiv("matrix", watchObject = object {
// for primitive types we have to use property references
val x = programState::x
val y = programState::y
}) {
for (y in 0 until watchObject.y.get()) {
div("row") {
for (x in 0 until watchObject.x.get()) {
button() {
label = "$x, $y"
}
}
}
}
}
}
}
extend(cm)
}
}

View File

@@ -0,0 +1,77 @@
package org.openrndr.panel.elements
import kotlinx.coroutines.Job
import kotlinx.coroutines.yield
import org.openrndr.draw.Drawer
import org.openrndr.launch
import org.openrndr.panel.elements.*
import org.openrndr.panel.hash.watchHash
import kotlin.reflect.KMutableProperty0
class WatchObjectDiv<T:Any>(
val watchObject: T,
private val builder: WatchObjectDiv<T>.(T) -> Unit
) : Div(),
DisposableElement {
override var disposed: Boolean = false
private var objectStateHash = watchHash(watchObject)
private var watchJob: Job? = null
override fun dispose() {
super.dispose()
for (child in children) {
child.parent = null
(child as? DisposableElement)?.dispose()
}
children.clear()
}
fun regenerate(force: Boolean = false) {
var regenerate = force
if (watchHash(watchObject) != objectStateHash) {
regenerate = true
}
if (regenerate) {
for (child in children) {
child.parent = null
(child as? DisposableElement)?.dispose()
}
objectStateHash = watchHash(watchObject)
children.clear()
builder(watchObject)
requestRedraw()
}
}
fun checkJob() {
if (watchJob == null) {
watchJob = (root() as? Body)?.controlManager?.program?.launch {
while (!disposed) {
regenerate()
yield()
}
}
}
}
override fun draw(drawer: Drawer) {
checkJob()
super.draw(drawer)
}
}
fun <T : Any> Element.watchObjectDiv(
vararg classes: String,
watchObject: T,
builder: WatchObjectDiv<T>.(T) -> Unit
) : WatchObjectDiv<T> {
val wd = WatchObjectDiv(watchObject, builder)
wd.classes.addAll(classes.map { ElementClass(it) })
this.append(wd)
wd.regenerate(true)
wd.checkJob()
return wd
}

View File

@@ -0,0 +1,19 @@
package org.openrndr.panel.hash
import kotlin.reflect.KProperty0
import kotlin.reflect.full.declaredMemberProperties
fun watchHash(toHash: Any): Int {
var hash = 0
for (property in toHash::class.declaredMemberProperties) {
val v = (property.getter)(toHash)
if (v is KProperty0<*>) {
val pv = v.get()
hash = 31 * hash + (pv?.hashCode() ?: 0)
} else {
hash = 31 * hash + (v?.hashCode() ?: 0)
}
}
return hash
}