@@ -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")
|
||||||
|
|||||||
35
orx-gui/src/demo/kotlin/DemoOptions01.kt
Normal file
35
orx-gui/src/demo/kotlin/DemoOptions01.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user