Add OptionParameter

Closes #78
This commit is contained in:
Edwin Jakobs
2020-04-14 20:43:26 +02:00
parent a0fd51c3c2
commit 591dccda63
6 changed files with 126 additions and 15 deletions

View File

@@ -13,7 +13,7 @@ dependencies {
api project(":orx-panel") api project(":orx-panel")
implementation "org.openrndr:openrndr-dialogs:$openrndrVersion" implementation "org.openrndr:openrndr-dialogs:$openrndrVersion"
implementation "com.google.code.gson:gson:$gsonVersion" implementation "com.google.code.gson:gson:$gsonVersion"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
demoImplementation("org.openrndr:openrndr-core:$openrndrVersion") demoImplementation("org.openrndr:openrndr-core:$openrndrVersion")
demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion") demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion")
demoRuntimeOnly("org.openrndr:openrndr-gl3-natives-$openrndrOS:$openrndrVersion") demoRuntimeOnly("org.openrndr:openrndr-gl3-natives-$openrndrOS:$openrndrVersion")

View File

@@ -0,0 +1,35 @@
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.gui.GUI
import org.openrndr.extra.parameters.*
import org.openrndr.math.Vector2
/**
* A simple demonstration of a GUI for drawing a single circle
*/
enum class SomeOptions {
Default,
DoNothing,
Smile
}
fun main() = application {
program {
val gui = GUI()
val settings = @Description("Settings") object {
@OptionParameter("action")
var option = SomeOptions.Default
}
gui.add(settings)
extend(gui)
extend {
when(settings.option) {
SomeOptions.Default -> drawer.background(ColorRGBa.PINK)
SomeOptions.DoNothing -> drawer.background(ColorRGBa.BLACK)
SomeOptions.Smile -> drawer.background(ColorRGBa.YELLOW)
}
}
}
}

View File

@@ -1,10 +1,7 @@
import org.openrndr.application import org.openrndr.application
import org.openrndr.color.ColorRGBa import org.openrndr.color.ColorRGBa
import org.openrndr.extra.gui.GUI import org.openrndr.extra.gui.GUI
import org.openrndr.extra.parameters.ColorParameter import org.openrndr.extra.parameters.*
import org.openrndr.extra.parameters.Description
import org.openrndr.extra.parameters.DoubleParameter
import org.openrndr.extra.parameters.Vector2Parameter
import org.openrndr.math.Vector2 import org.openrndr.math.Vector2
/** /**
@@ -23,7 +20,11 @@ fun main() = application {
@ColorParameter("color") @ColorParameter("color")
var color = ColorRGBa.PINK var color = ColorRGBa.PINK
@DoubleListParameter("a double list")
var adl = MutableList(2) { 0.0 }
} }
gui.add(settings) gui.add(settings)
extend(gui) extend(gui)
extend { extend {

View File

@@ -232,7 +232,7 @@ class GUI : Extension {
button { button {
label = "Save" label = "Save"
clicked { clicked {
val defaultPath = getDefaultPathForContext(contextID ="gui.parameters") val defaultPath = getDefaultPathForContext(contextID = "gui.parameters")
if (defaultPath == null) { if (defaultPath == null) {
val local = File(".") val local = File(".")
@@ -285,9 +285,9 @@ class GUI : Extension {
h3Header.mouse.pressed.listen { h3Header.mouse.pressed.listen {
it.cancelPropagation() it.cancelPropagation()
} }
h3Header.mouse.clicked.listen { h3Header.mouse.clicked.listen { me ->
if (KeyModifier.CTRL in it.modifiers) { if (KeyModifier.CTRL in me.modifiers) {
collapsible.classes.remove(collapseClass) collapsible.classes.remove(collapseClass)
persistentCompartmentStates[Driver.instance.contextID]!!.forEach { persistentCompartmentStates[Driver.instance.contextID]!!.forEach {
it.value.collapsed = true it.value.collapsed = true
@@ -568,8 +568,41 @@ class GUI : Extension {
} }
} }
} }
ParameterType.Option -> {
println("yo option")
dropdownButton {
val enumProperty = parameter.property as KMutableProperty1<Any, Enum<*>>
val value = enumProperty.get(obj)
// -- this is dirty, but it is the only way to get the constants for arbitrary enums
// -- (that I know of, at least)
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") val jEnum = value as java.lang.Enum<*>
// -- we don't use the property syntax here because that leads to compilation errors
@Suppress("UsePropertyAccessSyntax") val constants = jEnum.getDeclaringClass().getEnumConstants()
constants.forEach {
println("hey $it")
item {
label = it.name
data = it
}
}
events.valueChanged.listen {
setAndPersist(
compartment.label,
parameter.property as KMutableProperty1<Any, Enum<*>>,
obj,
it.value.data as? Enum<*> ?: error("no data")
)
}
getPersistedOrDefault(
compartment.label,
parameter.property as KMutableProperty1<Any, Enum<*>>,
obj
)?.let { enum ->
(this@dropdownButton).value = items().find { item -> item.data == enum }
?: error("no matching item found")
}
}
}
} }
} }
//</editor-fold> //</editor-fold>
@@ -592,7 +625,10 @@ class GUI : Extension {
var vector3Value: Vector3? = null, var vector3Value: Vector3? = null,
var vector4Value: Vector4? = null, var vector4Value: Vector4? = null,
var doubleListValue: MutableList<Double>? = null, var doubleListValue: MutableList<Double>? = null,
var textValue: String? = null) var textValue: String? = null,
var optionValue: String? = null
)
fun saveParameters(file: File) { fun saveParameters(file: File) {
@@ -616,6 +652,7 @@ class GUI : Extension {
ParameterType.Vector2 -> ParameterValue(vector2Value = k.property.qget(lo.obj) as Vector2) ParameterType.Vector2 -> ParameterValue(vector2Value = k.property.qget(lo.obj) as Vector2)
ParameterType.Vector3 -> ParameterValue(vector3Value = k.property.qget(lo.obj) as Vector3) ParameterType.Vector3 -> ParameterValue(vector3Value = k.property.qget(lo.obj) as Vector3)
ParameterType.Vector4 -> ParameterValue(vector4Value = k.property.qget(lo.obj) as Vector4) ParameterType.Vector4 -> ParameterValue(vector4Value = k.property.qget(lo.obj) as Vector4)
ParameterType.Option -> ParameterValue(optionValue = (k.property.qget(lo.obj) as Enum<*>).name)
}) })
}) })
} }
@@ -623,8 +660,16 @@ class GUI : Extension {
} }
fun loadParameters(file: File) { fun loadParameters(file: File) {
fun <T> KMutableProperty1<out Any, Any?>?.qset(obj: Any, value: T) { fun <T> KMutableProperty1<out Any, Any?>?.qset(obj: Any, value: T) =
return (this as KMutableProperty1<Any, T>).set(obj, value) (this as KMutableProperty1<Any, T>).set(obj, value)
fun KMutableProperty1<out Any, Any?>?.enumSet(obj: Any, value: String) {
val v = (this as KMutableProperty1<Any, Enum<*>>).get(obj)
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UsePropertyAccessSyntax")
val enumValue = (v as java.lang.Enum<*>).getDeclaringClass().getEnumConstants().find { it.name == value }
?: error("cannot map value $value to enum")
(this as KMutableProperty1<Any, Enum<*>>).set(obj, enumValue)
} }
val json = file.readText() val json = file.readText()
@@ -668,6 +713,9 @@ class GUI : Extension {
ParameterType.Vector4 -> parameterValue.vector4Value?.let { ParameterType.Vector4 -> parameterValue.vector4Value?.let {
parameter.property.qset(lo.obj, it) parameter.property.qset(lo.obj, it)
} }
ParameterType.Option -> parameterValue.optionValue?.let {
parameter.property.enumSet(lo.obj, it)
}
ParameterType.Action -> { ParameterType.Action -> {
// intentionally do nothing // intentionally do nothing
} }
@@ -712,6 +760,10 @@ class GUI : Extension {
ParameterType.Vector4 -> { ParameterType.Vector4 -> {
(control as SlidersVector4).value = (parameter.property as KMutableProperty1<Any, Vector4>).get(labeledObject.obj) (control as SlidersVector4).value = (parameter.property as KMutableProperty1<Any, Vector4>).get(labeledObject.obj)
} }
ParameterType.Option -> {
val ddb = control as DropdownButton
ddb.value = ddb.items().find { item -> item.data == (parameter.property as KMutableProperty1<Any, Enum<*>>).get(labeledObject.obj) } ?: error("could not find item")
}
ParameterType.Action -> { ParameterType.Action -> {
// intentionally do nothing // intentionally do nothing
} }

View File

@@ -22,6 +22,16 @@ import kotlin.reflect.full.memberProperties
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
annotation class Description(val title: String, val description: String = "") annotation class Description(val title: String, val description: String = "")
/**
* OptionParameter annotation for a double precision parameter
* @property label a short description of the parameter
* @property order hint for where to place the parameter in user interfaces
*/
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class OptionParameter(val label: String, val order: Int = Integer.MAX_VALUE)
/** /**
* DoubleParameter annotation for a double precision parameter * DoubleParameter annotation for a double precision parameter
* @property label a short description of the parameter * @property label a short description of the parameter
@@ -167,7 +177,8 @@ enum class ParameterType(val annotationClass: KClass<out Annotation>) {
DoubleList(DoubleListParameter::class), DoubleList(DoubleListParameter::class),
Vector2(Vector2Parameter::class), Vector2(Vector2Parameter::class),
Vector3(Vector3Parameter::class), Vector3(Vector3Parameter::class),
Vector4(Vector4Parameter::class) Vector4(Vector4Parameter::class),
Option(OptionParameter::class)
; ;
companion object { companion object {
@@ -204,6 +215,7 @@ class Parameter(
val precision: Int?, val precision: Int?,
val invertY: Boolean?, val invertY: Boolean?,
val showVector: Boolean?, val showVector: Boolean?,
// val optionEnum: Enum<*>,
val order: Int) val order: Int)
//</editor-fold> //</editor-fold>
//<editor-fold desc="4. Add handling annotation code to listParameters" defaultstate="collapsed"> //<editor-fold desc="4. Add handling annotation code to listParameters" defaultstate="collapsed">
@@ -288,6 +300,10 @@ fun Any.listParameters(): List<Parameter> {
doubleRange = it.min..it.max doubleRange = it.min..it.max
precision = it.precision precision = it.precision
} }
is OptionParameter -> {
label = it.label
order = it.order
}
} }
} }
Parameter( Parameter(

View File

@@ -43,6 +43,8 @@ val a = object {
@Vector4Parameter("a vector 4 parameter", order = 10) @Vector4Parameter("a vector 4 parameter", order = 10)
var v4 = Vector4.ZERO var v4 = Vector4.ZERO
@OptionParameter("an option parameter", order = 11)
var o = ParameterType.Option
} }
@@ -50,7 +52,7 @@ object TestAnnotations : Spek({
describe("an annotated object") { describe("an annotated object") {
it("has listable parameters") { it("has listable parameters") {
val list = a.listParameters() val list = a.listParameters()
list.size `should be equal to` 11 list.size `should be equal to` 12
list[0].property?.name `should be equal to` "d" list[0].property?.name `should be equal to` "d"
list[0].parameterType `should be equal to` ParameterType.Double list[0].parameterType `should be equal to` ParameterType.Double
@@ -117,6 +119,11 @@ object TestAnnotations : Spek({
list[10].parameterType `should be equal to` ParameterType.Vector4 list[10].parameterType `should be equal to` ParameterType.Vector4
list[10].property?.name `should be equal to` "v4" list[10].property?.name `should be equal to` "v4"
list[10].label `should be equal to` "a vector 4 parameter" list[10].label `should be equal to` "a vector 4 parameter"
list[11].parameterType `should be equal to` ParameterType.Option
list[11].property?.name `should be equal to` "o"
list[11].label `should be equal to` "an option parameter"
} }
} }
}) })