Add Olive.reload and Reloadable class for reloadable state
This commit is contained in:
@@ -36,8 +36,33 @@ Recent versions of `orx-olive` automatically set the `org.openrndr.ignoreShadeSt
|
|||||||
makes OPENRNDR ignore errors in the shade style and return the default shader. To get this behaviour in
|
makes OPENRNDR ignore errors in the shade style and return the default shader. To get this behaviour in
|
||||||
older versions add `-Dorg.openrndr.ignoreShadeStyleErrors=true` to the JVM arguments.
|
older versions add `-Dorg.openrndr.ignoreShadeStyleErrors=true` to the JVM arguments.
|
||||||
|
|
||||||
## Persistent Data
|
## Reloadable State
|
||||||
|
|
||||||
|
Along with the extension comes a mechanism that allows state to be reloaded from a store on script reload.
|
||||||
|
This functionality is offered by the `Reloadable` class.
|
||||||
|
|
||||||
|
An example `live.kts` in which the reloadable state is used:
|
||||||
|
```kotlin
|
||||||
|
@file:Suppress("UNUSED_LAMBDA_EXPRESSION")
|
||||||
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.draw.*
|
||||||
|
|
||||||
|
{ program: PersistentProgram ->
|
||||||
|
program.apply {
|
||||||
|
val a = object : Reloadable() {
|
||||||
|
var x : Double = 0.0
|
||||||
|
}
|
||||||
|
a.reload()
|
||||||
|
|
||||||
|
extend {
|
||||||
|
// do something with a.x here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Keep in mind that `Reloadable` should only be used for singleton classes.
|
||||||
|
|
||||||
|
## Persistent Data
|
||||||
Sometimes you want to keep parts of your application persistent. In the following example
|
Sometimes you want to keep parts of your application persistent. In the following example
|
||||||
we show how you can prepare the host program to contain a persistent camera device.
|
we show how you can prepare the host program to contain a persistent camera device.
|
||||||
|
|
||||||
@@ -61,7 +86,7 @@ fun main() = application{
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The live script `src/main/PersistentCamera.kt` then looks like this:
|
The live script `src/main/PersistentCamera.kts` then looks like this:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
@file:Suppress("UNUSED_LAMBDA_EXPRESSION")
|
@file:Suppress("UNUSED_LAMBDA_EXPRESSION")
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ internal class LoadException(message: String? = null, cause: Throwable? = null)
|
|||||||
|
|
||||||
internal class KtsObjectLoader(classLoader: ClassLoader? = Thread.currentThread().contextClassLoader) {
|
internal class KtsObjectLoader(classLoader: ClassLoader? = Thread.currentThread().contextClassLoader) {
|
||||||
|
|
||||||
val engine = ScriptEngineManager(classLoader).getEngineByExtension("kts")
|
private val engine = ScriptEngineManager(classLoader).getEngineByExtension("kts")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (engine == null) {
|
if (engine == null) {
|
||||||
|
|||||||
@@ -5,15 +5,16 @@ import org.openrndr.Program
|
|||||||
import org.openrndr.draw.Session
|
import org.openrndr.draw.Session
|
||||||
import org.openrndr.events.Event
|
import org.openrndr.events.Event
|
||||||
import org.operndr.extras.filewatcher.stop
|
import org.operndr.extras.filewatcher.stop
|
||||||
|
import org.operndr.extras.filewatcher.triggerChange
|
||||||
import org.operndr.extras.filewatcher.watchFile
|
import org.operndr.extras.filewatcher.watchFile
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
fun <T> Event<T>.saveListeners(store: MutableMap<Event<*>, List<(Any) -> Unit>>) {
|
private fun <T> Event<T>.saveListeners(store: MutableMap<Event<*>, List<(Any) -> Unit>>) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
store[this] = listeners.map { it } as List<(Any) -> Unit>
|
store[this] = listeners.map { it } as List<(Any) -> Unit>
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Event<T>.restoreListeners(store: Map<Event<*>, List<(Any) -> Unit>>) {
|
private fun <T> Event<T>.restoreListeners(store: Map<Event<*>, List<(Any) -> Unit>>) {
|
||||||
listeners.retainAll(store[this] ?: emptyList<T>())
|
listeners.retainAll(store[this] ?: emptyList<T>())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,16 +30,21 @@ class Olive<P : Program> : Extension {
|
|||||||
scriptChange(value)
|
scriptChange(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reloads the active script
|
||||||
|
*/
|
||||||
|
fun reload() {
|
||||||
|
watcher?.triggerChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var watcher: (() -> Unit)? = null
|
||||||
|
|
||||||
override fun setup(program: Program) {
|
override fun setup(program: Program) {
|
||||||
System.setProperty("idea.io.use.fallback", "true")
|
System.setProperty("idea.io.use.fallback", "true")
|
||||||
System.setProperty("org.openrndr.ignoreShadeStyleErrors", "true")
|
System.setProperty("org.openrndr.ignoreShadeStyleErrors", "true")
|
||||||
|
|
||||||
var watcher: (() -> Unit)? = null
|
|
||||||
|
|
||||||
val store = mutableMapOf<Event<*>, List<(Any) -> Unit>>()
|
val store = mutableMapOf<Event<*>, List<(Any) -> Unit>>()
|
||||||
|
|
||||||
val originalExtensions = program.extensions.map { it }
|
val originalExtensions = program.extensions.map { it }
|
||||||
|
|
||||||
val trackedListeners = listOf<Event<*>>(program.mouse.buttonDown,
|
val trackedListeners = listOf<Event<*>>(program.mouse.buttonDown,
|
||||||
program.mouse.buttonUp,
|
program.mouse.buttonUp,
|
||||||
program.mouse.clicked,
|
program.mouse.clicked,
|
||||||
|
|||||||
37
orx-olive/src/main/kotlin/Reloadable.kt
Normal file
37
orx-olive/src/main/kotlin/Reloadable.kt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package org.openrndr.extra.olive
|
||||||
|
|
||||||
|
import kotlin.reflect.KMutableProperty1
|
||||||
|
import kotlin.reflect.KProperty1
|
||||||
|
import kotlin.reflect.full.declaredMemberProperties
|
||||||
|
import kotlin.reflect.jvm.jvmName
|
||||||
|
|
||||||
|
private val store = mutableMapOf<String, Any>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class with which persistent state can be reloaded from inside Olive scripts.
|
||||||
|
*/
|
||||||
|
open class Reloadable {
|
||||||
|
/**
|
||||||
|
* reload property values from store
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun reload() {
|
||||||
|
val existing = store[this::class.jvmName]
|
||||||
|
if (existing != null) {
|
||||||
|
for (p in this::class.declaredMemberProperties) {
|
||||||
|
val e = existing::class.declaredMemberProperties.find { it.name == p.name }
|
||||||
|
if (e != null) {
|
||||||
|
try {
|
||||||
|
val value = (e as KProperty1<Any, Any?>).get(existing)
|
||||||
|
val mp = (p as KMutableProperty1<Any, Any?>)
|
||||||
|
mp.set(this, value as Any)
|
||||||
|
println("reloaded property ${p.name} <- ${value}")
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
println("error while reloading property ${p.name}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
store[this::class.jvmName] = this
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user