From 591dccda6357b3900b4f27d39490a989c9b87405 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Tue, 14 Apr 2020 20:43:26 +0200 Subject: [PATCH] Add OptionParameter Closes #78 --- orx-gui/build.gradle | 2 +- orx-gui/src/demo/kotlin/DemoOptions01.kt | 35 ++++++++++ orx-gui/src/demo/kotlin/DemoSimple01.kt | 9 +-- orx-gui/src/main/kotlin/Gui.kt | 68 ++++++++++++++++--- orx-parameters/src/main/kotlin/Annotations.kt | 18 ++++- .../src/test/kotlin/TestAnnotations.kt | 9 ++- 6 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 orx-gui/src/demo/kotlin/DemoOptions01.kt diff --git a/orx-gui/build.gradle b/orx-gui/build.gradle index 4d50f886..eab77f99 100644 --- a/orx-gui/build.gradle +++ b/orx-gui/build.gradle @@ -13,7 +13,7 @@ dependencies { api project(":orx-panel") implementation "org.openrndr:openrndr-dialogs:$openrndrVersion" implementation "com.google.code.gson:gson:$gsonVersion" - + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" demoImplementation("org.openrndr:openrndr-core:$openrndrVersion") demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion") demoRuntimeOnly("org.openrndr:openrndr-gl3-natives-$openrndrOS:$openrndrVersion") diff --git a/orx-gui/src/demo/kotlin/DemoOptions01.kt b/orx-gui/src/demo/kotlin/DemoOptions01.kt new file mode 100644 index 00000000..4dfa594e --- /dev/null +++ b/orx-gui/src/demo/kotlin/DemoOptions01.kt @@ -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) + } + } + } +} \ No newline at end of file diff --git a/orx-gui/src/demo/kotlin/DemoSimple01.kt b/orx-gui/src/demo/kotlin/DemoSimple01.kt index 0254f896..44636bce 100644 --- a/orx-gui/src/demo/kotlin/DemoSimple01.kt +++ b/orx-gui/src/demo/kotlin/DemoSimple01.kt @@ -1,10 +1,7 @@ import org.openrndr.application import org.openrndr.color.ColorRGBa import org.openrndr.extra.gui.GUI -import org.openrndr.extra.parameters.ColorParameter -import org.openrndr.extra.parameters.Description -import org.openrndr.extra.parameters.DoubleParameter -import org.openrndr.extra.parameters.Vector2Parameter +import org.openrndr.extra.parameters.* import org.openrndr.math.Vector2 /** @@ -23,7 +20,11 @@ fun main() = application { @ColorParameter("color") var color = ColorRGBa.PINK + @DoubleListParameter("a double list") + var adl = MutableList(2) { 0.0 } + } + gui.add(settings) extend(gui) extend { diff --git a/orx-gui/src/main/kotlin/Gui.kt b/orx-gui/src/main/kotlin/Gui.kt index bcdbe7d7..78aa0da5 100644 --- a/orx-gui/src/main/kotlin/Gui.kt +++ b/orx-gui/src/main/kotlin/Gui.kt @@ -232,7 +232,7 @@ class GUI : Extension { button { label = "Save" clicked { - val defaultPath = getDefaultPathForContext(contextID ="gui.parameters") + val defaultPath = getDefaultPathForContext(contextID = "gui.parameters") if (defaultPath == null) { val local = File(".") @@ -285,9 +285,9 @@ class GUI : Extension { h3Header.mouse.pressed.listen { 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) persistentCompartmentStates[Driver.instance.contextID]!!.forEach { it.value.collapsed = true @@ -568,8 +568,41 @@ class GUI : Extension { } } } - - + ParameterType.Option -> { + println("yo option") + dropdownButton { + val enumProperty = parameter.property as KMutableProperty1> + 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>, + obj, + it.value.data as? Enum<*> ?: error("no data") + ) + } + getPersistedOrDefault( + compartment.label, + parameter.property as KMutableProperty1>, + obj + )?.let { enum -> + (this@dropdownButton).value = items().find { item -> item.data == enum } + ?: error("no matching item found") + } + } + } } } // @@ -592,7 +625,10 @@ class GUI : Extension { var vector3Value: Vector3? = null, var vector4Value: Vector4? = null, var doubleListValue: MutableList? = null, - var textValue: String? = null) + var textValue: String? = null, + var optionValue: String? = null + + ) fun saveParameters(file: File) { @@ -616,6 +652,7 @@ class GUI : Extension { ParameterType.Vector2 -> ParameterValue(vector2Value = k.property.qget(lo.obj) as Vector2) ParameterType.Vector3 -> ParameterValue(vector3Value = k.property.qget(lo.obj) as Vector3) 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 KMutableProperty1?.qset(obj: Any, value: T) { - return (this as KMutableProperty1).set(obj, value) + fun KMutableProperty1?.qset(obj: Any, value: T) = + (this as KMutableProperty1).set(obj, value) + + fun KMutableProperty1?.enumSet(obj: Any, value: String) { + val v = (this as KMutableProperty1>).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>).set(obj, enumValue) } val json = file.readText() @@ -668,6 +713,9 @@ class GUI : Extension { ParameterType.Vector4 -> parameterValue.vector4Value?.let { parameter.property.qset(lo.obj, it) } + ParameterType.Option -> parameterValue.optionValue?.let { + parameter.property.enumSet(lo.obj, it) + } ParameterType.Action -> { // intentionally do nothing } @@ -712,6 +760,10 @@ class GUI : Extension { ParameterType.Vector4 -> { (control as SlidersVector4).value = (parameter.property as KMutableProperty1).get(labeledObject.obj) } + ParameterType.Option -> { + val ddb = control as DropdownButton + ddb.value = ddb.items().find { item -> item.data == (parameter.property as KMutableProperty1>).get(labeledObject.obj) } ?: error("could not find item") + } ParameterType.Action -> { // intentionally do nothing } diff --git a/orx-parameters/src/main/kotlin/Annotations.kt b/orx-parameters/src/main/kotlin/Annotations.kt index de2ad511..aefb669d 100644 --- a/orx-parameters/src/main/kotlin/Annotations.kt +++ b/orx-parameters/src/main/kotlin/Annotations.kt @@ -22,6 +22,16 @@ import kotlin.reflect.full.memberProperties @Retention(AnnotationRetention.RUNTIME) 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 * @property label a short description of the parameter @@ -167,7 +177,8 @@ enum class ParameterType(val annotationClass: KClass) { DoubleList(DoubleListParameter::class), Vector2(Vector2Parameter::class), Vector3(Vector3Parameter::class), - Vector4(Vector4Parameter::class) + Vector4(Vector4Parameter::class), + Option(OptionParameter::class) ; companion object { @@ -204,6 +215,7 @@ class Parameter( val precision: Int?, val invertY: Boolean?, val showVector: Boolean?, +// val optionEnum: Enum<*>, val order: Int) // // @@ -288,6 +300,10 @@ fun Any.listParameters(): List { doubleRange = it.min..it.max precision = it.precision } + is OptionParameter -> { + label = it.label + order = it.order + } } } Parameter( diff --git a/orx-parameters/src/test/kotlin/TestAnnotations.kt b/orx-parameters/src/test/kotlin/TestAnnotations.kt index efa81e12..1bda4cf8 100644 --- a/orx-parameters/src/test/kotlin/TestAnnotations.kt +++ b/orx-parameters/src/test/kotlin/TestAnnotations.kt @@ -43,6 +43,8 @@ val a = object { @Vector4Parameter("a vector 4 parameter", order = 10) 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") { it("has listable parameters") { 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].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].property?.name `should be equal to` "v4" 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" + } } })