[orx-panel] Add WatchObjectDiv
This commit is contained in:
@@ -9,6 +9,7 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||||
demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion")
|
demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion")
|
||||||
demoImplementation("org.openrndr:openrndr-dialogs:$openrndrVersion")
|
demoImplementation("org.openrndr:openrndr-dialogs:$openrndrVersion")
|
||||||
demoImplementation("com.google.code.gson:gson:$gsonVersion")
|
demoImplementation("com.google.code.gson:gson:$gsonVersion")
|
||||||
|
|||||||
75
orx-panel/src/demo/kotlin/DemoWatchObjectDiv01.kt
Normal file
75
orx-panel/src/demo/kotlin/DemoWatchObjectDiv01.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user