From a49648e296a34884647caad2b03abe29beaf814e Mon Sep 17 00:00:00 2001 From: UserTeemu <20520700+UserTeemu@users.noreply.github.com> Date: Tue, 26 Apr 2022 10:51:00 +0300 Subject: [PATCH] Properties: Superseded PropertyValues by Elementa's States (concept) --- api/Vigilance.api | 18 +++ .../kotlin/gg/essential/vigilance/Vigilant.kt | 75 ++++++++--- .../vigilance/data/PropertyCollector.kt | 17 ++- .../essential/vigilance/data/PropertyData.kt | 125 +++++++----------- .../essential/vigilance/data/PropertyValue.kt | 95 +++++++++++++ .../vigilance/gui/settings/ButtonComponent.kt | 9 +- 6 files changed, 235 insertions(+), 104 deletions(-) create mode 100644 src/main/kotlin/gg/essential/vigilance/data/PropertyValue.kt diff --git a/api/Vigilance.api b/api/Vigilance.api index 83f0836b..581794ff 100644 --- a/api/Vigilance.api +++ b/api/Vigilance.api @@ -73,9 +73,11 @@ public final class gg/essential/vigilance/Vigilant$CategoryPropertyBuilder { public static synthetic fun paragraph$default (Lgg/essential/vigilance/Vigilant$CategoryPropertyBuilder;Lkotlin/reflect/KMutableProperty0;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public final fun percentSlider (Lkotlin/reflect/KMutableProperty0;Ljava/lang/String;Ljava/lang/String;ZZLkotlin/jvm/functions/Function1;)V public static synthetic fun percentSlider$default (Lgg/essential/vigilance/Vigilant$CategoryPropertyBuilder;Lkotlin/reflect/KMutableProperty0;Ljava/lang/String;Ljava/lang/String;ZZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public final fun property (Lgg/essential/elementa/state/State;Lgg/essential/vigilance/data/PropertyType;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;IIFFIILjava/util/List;ZLjava/lang/String;ZZLkotlin/jvm/functions/Function1;)V public final synthetic fun property (Lgg/essential/vigilance/data/PropertyValue;Lgg/essential/vigilance/data/PropertyType;Ljava/lang/String;Ljava/lang/String;IIFFIILjava/util/List;ZLjava/lang/String;ZZLkotlin/jvm/functions/Function1;)V public final fun property (Lgg/essential/vigilance/data/PropertyValue;Lgg/essential/vigilance/data/PropertyType;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;IIFFIILjava/util/List;ZLjava/lang/String;ZZLkotlin/jvm/functions/Function1;)V public final fun property (Lkotlin/reflect/KMutableProperty0;Lgg/essential/vigilance/data/PropertyType;Ljava/lang/String;Ljava/lang/String;IIFFIILjava/util/List;ZLjava/lang/String;ZZLkotlin/jvm/functions/Function1;)V + public static synthetic fun property$default (Lgg/essential/vigilance/Vigilant$CategoryPropertyBuilder;Lgg/essential/elementa/state/State;Lgg/essential/vigilance/data/PropertyType;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;IIFFIILjava/util/List;ZLjava/lang/String;ZZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static synthetic fun property$default (Lgg/essential/vigilance/Vigilant$CategoryPropertyBuilder;Lgg/essential/vigilance/data/PropertyValue;Lgg/essential/vigilance/data/PropertyType;Ljava/lang/String;Ljava/lang/String;IIFFIILjava/util/List;ZLjava/lang/String;ZZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static synthetic fun property$default (Lgg/essential/vigilance/Vigilant$CategoryPropertyBuilder;Lgg/essential/vigilance/data/PropertyValue;Lgg/essential/vigilance/data/PropertyType;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;IIFFIILjava/util/List;ZLjava/lang/String;ZZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static synthetic fun property$default (Lgg/essential/vigilance/Vigilant$CategoryPropertyBuilder;Lkotlin/reflect/KMutableProperty0;Lgg/essential/vigilance/data/PropertyType;Ljava/lang/String;Ljava/lang/String;IIFFIILjava/util/List;ZLjava/lang/String;ZZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V @@ -276,7 +278,9 @@ public abstract class gg/essential/vigilance/data/PropertyCollector { public final class gg/essential/vigilance/data/PropertyData { public static final field Companion Lgg/essential/vigilance/data/PropertyData$Companion; + public fun (Lgg/essential/vigilance/data/PropertyAttributes;Lgg/essential/elementa/state/State;Lgg/essential/vigilance/Vigilant;)V public fun (Lgg/essential/vigilance/data/PropertyAttributes;Lgg/essential/vigilance/data/PropertyValue;Lgg/essential/vigilance/Vigilant;)V + public fun (Lgg/essential/vigilance/data/PropertyAttributesExt;Lgg/essential/elementa/state/State;Lgg/essential/vigilance/Vigilant;)V public fun (Lgg/essential/vigilance/data/PropertyAttributesExt;Lgg/essential/vigilance/data/PropertyValue;Lgg/essential/vigilance/Vigilant;)V public final fun component1 ()Lgg/essential/vigilance/data/PropertyAttributes; public final fun component2 ()Lgg/essential/vigilance/data/PropertyValue; @@ -294,7 +298,9 @@ public final class gg/essential/vigilance/data/PropertyData { public final fun getDependsOn ()Lgg/essential/vigilance/data/PropertyData; public final fun getHasDependants ()Z public final fun getInstance ()Lgg/essential/vigilance/Vigilant; + public final fun getState ()Lgg/essential/elementa/state/State; public final fun getValue ()Lgg/essential/vigilance/data/PropertyValue; + public final fun getWriteDataToFile ()Z public fun hashCode ()I public final fun isHidden ()Z public final fun setAction (Lkotlin/jvm/functions/Function1;)V @@ -353,6 +359,12 @@ public abstract class gg/essential/vigilance/data/PropertyValue { public abstract fun setValue (Ljava/lang/Object;Lgg/essential/vigilance/Vigilant;)V } +public final class gg/essential/vigilance/data/PropertyValueBackedState : gg/essential/elementa/state/State { + public fun (Lgg/essential/vigilance/data/PropertyValue;Lgg/essential/vigilance/Vigilant;)V + public fun get ()Ljava/lang/Object; + public fun set (Ljava/lang/Object;)V +} + public class gg/essential/vigilance/data/SortingBehavior { public fun ()V public fun getCategoryComparator ()Ljava/util/Comparator; @@ -360,6 +372,12 @@ public class gg/essential/vigilance/data/SortingBehavior { public fun getSubcategoryComparator ()Ljava/util/Comparator; } +public final class gg/essential/vigilance/data/StateBackedPropertyValue : gg/essential/vigilance/data/PropertyValue { + public fun (Lgg/essential/elementa/state/State;)V + public fun getValue (Lgg/essential/vigilance/Vigilant;)Ljava/lang/Object; + public fun setValue (Ljava/lang/Object;Lgg/essential/vigilance/Vigilant;)V +} + public final class gg/essential/vigilance/data/ValueBackedPropertyValue : gg/essential/vigilance/data/PropertyValue { public fun (Ljava/lang/Object;)V public fun getValue (Lgg/essential/vigilance/Vigilant;)Ljava/lang/Object; diff --git a/src/main/kotlin/gg/essential/vigilance/Vigilant.kt b/src/main/kotlin/gg/essential/vigilance/Vigilant.kt index 92d15fd4..f7eb7ced 100644 --- a/src/main/kotlin/gg/essential/vigilance/Vigilant.kt +++ b/src/main/kotlin/gg/essential/vigilance/Vigilant.kt @@ -1,6 +1,8 @@ package gg.essential.vigilance import com.electronwill.nightconfig.core.file.FileConfig +import gg.essential.elementa.state.BasicState +import gg.essential.elementa.state.State import gg.essential.universal.UChat import gg.essential.vigilance.data.* import gg.essential.vigilance.gui.SettingsGui @@ -99,17 +101,14 @@ abstract class Vigilant @JvmOverloads constructor( @Suppress("UNCHECKED_CAST") fun registerListener(field: Field, listener: Consumer) { - propertyCollector - .getProperties() - .firstOrNull { it.value is FieldBackedPropertyValue && it.value.field == field }!! - .action = { obj -> listener.accept(obj as T) } + propertyCollector.getProperty(field)!! + .setCallbackConsumer { obj -> listener.accept(obj as T) } } @Suppress("UNCHECKED_CAST") fun registerListener(propertyName: String, listener: Consumer) { - propertyCollector.getProperties() - .firstOrNull { it.value is FieldBackedPropertyValue && it.value.field.name == propertyName }!! - .action = { obj -> listener.accept(obj as T) } + propertyCollector.getProperty(propertyName)!! + .setCallbackConsumer { obj -> listener.accept(obj as T) } } @Deprecated( @@ -220,7 +219,7 @@ abstract class Vigilant @JvmOverloads constructor( private fun readData() { fileConfig.load() - propertyCollector.getProperties().filter { it.value.writeDataToFile }.forEach { + propertyCollector.getProperties().filter { it.writeDataToFile }.forEach { val fullPath = it.attributesExt.fullPropertyPath() var oldValue: Any? = fileConfig.get(fullPath) @@ -249,7 +248,7 @@ abstract class Vigilant @JvmOverloads constructor( fun writeData() { if (!dirty) return - propertyCollector.getProperties().filter { it.value.writeDataToFile }.forEach { + propertyCollector.getProperties().filter { it.writeDataToFile }.forEach { val fullPath = it.attributesExt.fullPropertyPath() var toSet = it.getAsAny() @@ -325,7 +324,7 @@ abstract class Vigilant @JvmOverloads constructor( } fun property( - value: PropertyValue, + state: State, type: PropertyType, name: String, description: String = "", @@ -362,9 +361,15 @@ abstract class Vigilant @JvmOverloads constructor( triggerActionOnInitialization = triggerActionOnInitialization, hidden = hidden, ), - value, + state, instance - ).also { it.attributesExt.searchTags.toMutableList().addAll(searchTags) } + ).also { + if (type == PropertyType.BUTTON) { + it.writeDataToFile = false + } + + it.attributesExt.searchTags.toMutableList().addAll(searchTags) + } if (action != null) { data.action = { action(it as T) } @@ -373,6 +378,46 @@ abstract class Vigilant @JvmOverloads constructor( properties.add(data) } + fun property( + value: PropertyValue, + type: PropertyType, + name: String, + description: String = "", + searchTags: List = listOf(), + min: Int = 0, + max: Int = 0, + minF: Float = 0f, + maxF: Float = 0f, + decimalPlaces: Int = 1, + increment: Int = 1, + options: List = listOf(), + allowAlpha: Boolean = true, + placeholder: String = "", + triggerActionOnInitialization: Boolean = true, + hidden: Boolean = false, + action: ((T) -> Unit)? = null + ) { + property( + state = PropertyValueBackedState(value, instance), + type = type, + name = name, + description = description, + searchTags = searchTags, + min = min, + max = max, + minF = minF, + maxF = maxF, + decimalPlaces = decimalPlaces, + increment = increment, + options = options, + allowAlpha = allowAlpha, + placeholder = placeholder, + triggerActionOnInitialization = triggerActionOnInitialization, + hidden = hidden, + action = action + ) + } + fun property( field: KMutableProperty0, type: PropertyType, @@ -631,15 +676,15 @@ abstract class Vigilant @JvmOverloads constructor( hidden: Boolean = false, action: (() -> Unit) ) { - property( - KFunctionBackedPropertyValue(action), + property( + BasicState(Unit), PropertyType.BUTTON, name, description, placeholder = buttonText, triggerActionOnInitialization = triggerActionOnInitialization, hidden = hidden, - action = null + action = { _: Any? -> action() } ) } diff --git a/src/main/kotlin/gg/essential/vigilance/data/PropertyCollector.kt b/src/main/kotlin/gg/essential/vigilance/data/PropertyCollector.kt index 7c734d52..aa0aed65 100644 --- a/src/main/kotlin/gg/essential/vigilance/data/PropertyCollector.kt +++ b/src/main/kotlin/gg/essential/vigilance/data/PropertyCollector.kt @@ -20,14 +20,21 @@ abstract class PropertyCollector { } internal fun getProperty(propertyName: String): PropertyData? = collectedProperties.firstOrNull { - it.value is FieldBackedPropertyValue && it.value.field.name == propertyName || - it.value is KPropertyBackedPropertyValue<*> && it.value.property.name == propertyName || - it.value is MethodBackedPropertyValue && it.value.method.name == propertyName + propertyName == it.value.let { propertyValue -> + when (propertyValue) { + is FieldBackedPropertyValue -> propertyValue.field.name + is KPropertyBackedPropertyValue<*> -> propertyValue.property.name + is MethodBackedPropertyValue -> propertyValue.method.name + else -> return@firstOrNull false + } + } } internal fun getProperty(field: Field): PropertyData? = collectedProperties.firstOrNull { - it.value is FieldBackedPropertyValue && it.value.field == field || - it.value is KPropertyBackedPropertyValue<*> && it.value.property.javaField == field + it.value.let { propertyValue -> + propertyValue is FieldBackedPropertyValue && propertyValue.field == field || + propertyValue is KPropertyBackedPropertyValue<*> && propertyValue.property.javaField == field + } } } diff --git a/src/main/kotlin/gg/essential/vigilance/data/PropertyData.kt b/src/main/kotlin/gg/essential/vigilance/data/PropertyData.kt index d1eb2dc3..6a3288ce 100644 --- a/src/main/kotlin/gg/essential/vigilance/data/PropertyData.kt +++ b/src/main/kotlin/gg/essential/vigilance/data/PropertyData.kt @@ -1,31 +1,61 @@ package gg.essential.vigilance.data +import gg.essential.elementa.state.BasicState +import gg.essential.elementa.state.State import gg.essential.vigilance.Vigilant import java.lang.reflect.Field import java.lang.reflect.Method import java.util.function.Consumer -import kotlin.reflect.KMutableProperty0 - -data class PropertyData(@Deprecated("Replace with attributesExt", ReplaceWith("attributesExt")) val attributes: PropertyAttributes, val value: PropertyValue, val instance: Vigilant) { +data class PropertyData(@Deprecated("Replace with attributesExt", ReplaceWith("attributesExt")) val attributes: PropertyAttributes, @Deprecated("Replace with strictValue", ReplaceWith("strictValue")) val value: PropertyValue, val instance: Vigilant) { constructor(attributesExt: PropertyAttributesExt, value: PropertyValue, instance: Vigilant) : this(attributesExt.toPropertyAttributes(), value, instance) { this.attributesExt = attributesExt } + constructor(attributesExt: PropertyAttributesExt, state: State<*>, instance: Vigilant) : + this(attributesExt, StateBackedPropertyValue(state), instance) { + this.state = state + } + + constructor(attributesExt: PropertyAttributes, state: State<*>, instance: Vigilant) : + this(attributesExt, StateBackedPropertyValue(state), instance) { + this.state = state + } + var attributesExt: PropertyAttributesExt = PropertyAttributesExt(attributes) private set - var action: ((Any?) -> Unit)? = null + var state: State<*> = PropertyValueBackedState(value, instance) + private set + + @Deprecated("Please use the state's listeners instead.") + var action: ((Any?) -> Unit)? + set(value) { + // If the action is not null, it's added as a listener to the state. + if (value != null) this.state.onSetValue(value) + } + get() { + throw IllegalAccessError("Action property is deprecated. Please use the state's listeners instead.") + } + var dependsOn: PropertyData? = null var hasDependants: Boolean = false + var writeDataToFile: Boolean = true + internal set + + private var initialized: Boolean + get() = value.initialized + set(isInitialized) { + value.initialized = isInitialized + } fun getDataType() = attributesExt.type inline fun getValue(): T { - return value.getValue(instance) as T + return state.get() as T } - fun getAsAny(): Any? = value.getValue(instance) + fun getAsAny(): Any? = state.get() fun getAsBoolean(): Boolean = getValue() @@ -39,17 +69,19 @@ data class PropertyData(@Deprecated("Replace with attributesExt", ReplaceWith("a return } - if (attributesExt.triggerActionOnInitialization || this.value.initialized) - action?.invoke(value) + (this.state as State).set(value) - this.value.initialized = true - this.value.setValue(value, instance) + this.initialized = true instance.markDirty() } fun setCallbackConsumer(callback: Consumer) { - this.action = callback::accept + (this.state as State).onSetValue { + if (attributesExt.triggerActionOnInitialization || this.initialized) { + callback.accept(it) + } + } } companion object { @@ -72,9 +104,11 @@ data class PropertyData(@Deprecated("Replace with attributesExt", ReplaceWith("a fun fromMethod(property: Property, method: Method, instance: Vigilant): PropertyData { return PropertyData( PropertyAttributesExt.fromPropertyAnnotation(property), - MethodBackedPropertyValue(method), + BasicState(Unit), instance - ) + ).apply { + setCallbackConsumer { method.invoke(this.instance) } + } } fun withValue(property: Property, obj: Any?, instance: Vigilant): PropertyData { @@ -86,68 +120,3 @@ data class PropertyData(@Deprecated("Replace with attributesExt", ReplaceWith("a } } } - -abstract class PropertyValue { - var initialized = false - - open val writeDataToFile = true - - abstract fun getValue(instance: Vigilant): Any? - abstract fun setValue(value: Any?, instance: Vigilant) -} - -class FieldBackedPropertyValue(internal val field: Field) : PropertyValue() { - override fun getValue(instance: Vigilant): Any? { - return field.get(instance) - } - - override fun setValue(value: Any?, instance: Vigilant) { - if (value is Double && field.type == Float::class.java) { - field.set(instance, value.toFloat()) - } else if (value is Float && field.type == Double::class.java) { - field.set(instance, value.toDouble()) - } else { - field.set(instance, value) - } - } -} - -class ValueBackedPropertyValue(private var obj: Any?) : PropertyValue() { - override fun getValue(instance: Vigilant): Any? { - return obj - } - - override fun setValue(value: Any?, instance: Vigilant) { - obj = value - } -} - -class KPropertyBackedPropertyValue(internal val property: KMutableProperty0) : PropertyValue() { - override fun getValue(instance: Vigilant) = property.get() - - override fun setValue(value: Any?, instance: Vigilant) { - property.set(value as T) - } -} - -abstract class CallablePropertyValue : PropertyValue() { - override val writeDataToFile = false - - override fun getValue(instance: Vigilant): Nothing = throw IllegalStateException() - - override fun setValue(value: Any?, instance: Vigilant): Nothing = throw IllegalStateException() - - abstract operator fun invoke(instance: Vigilant) -} - -class MethodBackedPropertyValue(internal val method: Method) : CallablePropertyValue() { - override fun invoke(instance: Vigilant) { - method.invoke(instance) - } -} - -class KFunctionBackedPropertyValue(private val kFunction: () -> Unit) : CallablePropertyValue() { - override fun invoke(instance: Vigilant) { - kFunction() - } -} diff --git a/src/main/kotlin/gg/essential/vigilance/data/PropertyValue.kt b/src/main/kotlin/gg/essential/vigilance/data/PropertyValue.kt new file mode 100644 index 00000000..f91f11ea --- /dev/null +++ b/src/main/kotlin/gg/essential/vigilance/data/PropertyValue.kt @@ -0,0 +1,95 @@ +package gg.essential.vigilance.data + +import gg.essential.elementa.state.State +import gg.essential.vigilance.Vigilant +import java.lang.reflect.Field +import java.lang.reflect.Method +import kotlin.reflect.KMutableProperty0 + +@Deprecated("Use Elementa's State instead", ReplaceWith("State", "gg.essential.elementa.state.State")) +abstract class PropertyValue { + @Deprecated("Use PropertyData#initialized instead.") + var initialized: Boolean = false + + open val writeDataToFile = true + + abstract fun getValue(instance: Vigilant): Any? + abstract fun setValue(value: Any?, instance: Vigilant) +} + +class FieldBackedPropertyValue(internal val field: Field) : PropertyValue() { + override fun getValue(instance: Vigilant): Any? { + return field.get(instance) + } + + override fun setValue(value: Any?, instance: Vigilant) { + if (value is Double && field.type == Float::class.java) { + field.set(instance, value.toFloat()) + } else if (value is Float && field.type == Double::class.java) { + field.set(instance, value.toDouble()) + } else { + field.set(instance, value) + } + } +} + +class ValueBackedPropertyValue(private var obj: Any?) : PropertyValue() { + override fun getValue(instance: Vigilant): Any? { + return obj + } + + override fun setValue(value: Any?, instance: Vigilant) { + obj = value + } +} + +class KPropertyBackedPropertyValue(internal val property: KMutableProperty0) : PropertyValue() { + override fun getValue(instance: Vigilant) = property.get() + + override fun setValue(value: Any?, instance: Vigilant) { + property.set(value as T) + } +} + +class StateBackedPropertyValue(private val state: State<*>) : PropertyValue() { + override fun getValue(instance: Vigilant): Any? { + return state.get() + } + + override fun setValue(value: Any?, instance: Vigilant) { + (state as? State)?.set(value) + } +} + +@Deprecated("Callable property values have been deprecated. Please use PropertyData#action instead.") +abstract class CallablePropertyValue : PropertyValue() { + override val writeDataToFile = false + + override fun getValue(instance: Vigilant): Nothing = throw IllegalStateException() + + override fun setValue(value: Any?, instance: Vigilant): Nothing = throw IllegalStateException() + + abstract operator fun invoke(instance: Vigilant) +} + +class MethodBackedPropertyValue(internal val method: Method) : CallablePropertyValue() { + override fun invoke(instance: Vigilant) { + method.invoke(instance) + } +} + +class KFunctionBackedPropertyValue(private val kFunction: () -> Unit) : CallablePropertyValue() { + override fun invoke(instance: Vigilant) { + kFunction() + } +} + +class PropertyValueBackedState(private val propertyValue: PropertyValue, private val instance: Vigilant) : State() { + override fun get(): T = + propertyValue.getValue(instance) as T + + override fun set(value: T) { + propertyValue.setValue(propertyValue, instance) + super.set(value) + } +} diff --git a/src/main/kotlin/gg/essential/vigilance/gui/settings/ButtonComponent.kt b/src/main/kotlin/gg/essential/vigilance/gui/settings/ButtonComponent.kt index 09f8fe11..b42c4fbf 100644 --- a/src/main/kotlin/gg/essential/vigilance/gui/settings/ButtonComponent.kt +++ b/src/main/kotlin/gg/essential/vigilance/gui/settings/ButtonComponent.kt @@ -12,7 +12,6 @@ import gg.essential.elementa.state.State import gg.essential.elementa.state.toConstraint import gg.essential.elementa.utils.withAlpha import gg.essential.universal.USound -import gg.essential.vigilance.data.CallablePropertyValue import gg.essential.vigilance.data.PropertyData import gg.essential.vigilance.gui.ExpandingClickEffect import gg.essential.vigilance.gui.VigilancePalette @@ -87,11 +86,9 @@ class ButtonComponent(placeholder: String? = null, private val callback: () -> U companion object { private fun callbackFromPropertyData(data: PropertyData): () -> Unit { - val value = data.value - if (value !is CallablePropertyValue) - throw IllegalStateException() - - return { value.invoke(data.instance) } + val state = data.state as State + // Sets the value to null, and therefore executes all listeners of the state. + return { state.set(Unit) } } } }