Advanced backwards events

This commit is contained in:
Alexander Nozik 2023-12-18 09:59:43 +03:00
parent 0c9d849e97
commit e36e4abb7f
10 changed files with 122 additions and 69 deletions

View File

@ -11,7 +11,7 @@ val dataforgeVersion by extra("0.7.1")
allprojects {
group = "space.kscience"
version = "0.3.0-RC"
version = "0.3.0"
}
subprojects {

View File

@ -225,6 +225,8 @@ public abstract interface class space/kscience/visionforge/ControlVision : space
public final class space/kscience/visionforge/ControlVisionKt {
public static final fun VisionClickEvent (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/visionforge/VisionClickEvent;
public static synthetic fun VisionClickEvent$default (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/visionforge/VisionClickEvent;
public static final fun VisionInputEvent (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/visionforge/VisionInputEvent;
public static synthetic fun VisionInputEvent$default (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/visionforge/VisionInputEvent;
public static final fun VisionValueChangeEvent (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/visionforge/VisionValueChangeEvent;
public static synthetic fun VisionValueChangeEvent$default (Lspace/kscience/dataforge/meta/Value;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/visionforge/VisionValueChangeEvent;
public static final fun onClick (Lspace/kscience/visionforge/ClickControl;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
@ -495,6 +497,7 @@ public final class space/kscience/visionforge/VisionClientKt {
public static final fun notifyPropertyChanged (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Ljava/lang/String;)V
public static final fun notifyPropertyChanged (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;)V
public static final fun notifyPropertyChanged (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Z)V
public static final fun sendEventAsync (Lspace/kscience/visionforge/VisionClient;Lspace/kscience/dataforge/names/Name;Lspace/kscience/visionforge/VisionEvent;)Lkotlinx/coroutines/Job;
}
public abstract interface class space/kscience/visionforge/VisionContainer {
@ -560,6 +563,30 @@ public final class space/kscience/visionforge/VisionGroupKt {
public static synthetic fun group$default (Lspace/kscience/visionforge/MutableVisionContainer;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/SimpleVisionGroup;
}
public final class space/kscience/visionforge/VisionInputEvent : space/kscience/visionforge/VisionControlEvent {
public static final field Companion Lspace/kscience/visionforge/VisionInputEvent$Companion;
public fun <init> (Lspace/kscience/dataforge/meta/Meta;)V
public fun getMeta ()Lspace/kscience/dataforge/meta/Meta;
public final fun getName ()Lspace/kscience/dataforge/names/Name;
public final fun getValue ()Lspace/kscience/dataforge/meta/Value;
public fun toString ()Ljava/lang/String;
}
public final class space/kscience/visionforge/VisionInputEvent$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lspace/kscience/visionforge/VisionInputEvent$$serializer;
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/visionforge/VisionInputEvent;
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/visionforge/VisionInputEvent;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class space/kscience/visionforge/VisionInputEvent$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class space/kscience/visionforge/VisionKt {
public static final fun getVisible (Lspace/kscience/visionforge/Vision;)Ljava/lang/Boolean;
public static final fun onPropertyChange (Lspace/kscience/visionforge/Vision;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
@ -649,8 +676,8 @@ public final class space/kscience/visionforge/VisionPropertiesKt {
public static final fun get (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;)Lspace/kscience/dataforge/meta/Meta;
public static synthetic fun get$default (Lspace/kscience/visionforge/MutableVisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMeta;
public static synthetic fun get$default (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/Meta;
public static final fun getValue (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;)Lspace/kscience/dataforge/meta/Value;
public static synthetic fun getValue$default (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/Value;
public static final fun getValue (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;ZLjava/lang/Boolean;)Lspace/kscience/dataforge/meta/Value;
public static synthetic fun getValue$default (Lspace/kscience/visionforge/VisionProperties;Ljava/lang/String;ZLjava/lang/Boolean;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/Value;
public static final fun invoke (Lspace/kscience/visionforge/MutableVisionProperties;Lkotlin/jvm/functions/Function1;)V
public static final fun remove (Lspace/kscience/visionforge/MutableVisionProperties;Ljava/lang/String;)V
public static final fun remove (Lspace/kscience/visionforge/MutableVisionProperties;Lspace/kscience/dataforge/names/Name;)V
@ -781,8 +808,8 @@ public abstract class space/kscience/visionforge/html/VisionOfHtml : space/kscie
public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtml$Companion;
public fun <init> ()V
public synthetic fun <init> (ILspace/kscience/dataforge/meta/MutableMeta;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public final fun getClasses ()Ljava/util/List;
public final fun setClasses (Ljava/util/List;)V
public final fun getClasses ()Ljava/util/Set;
public final fun setClasses (Ljava/util/Set;)V
public static final synthetic fun write$Self (Lspace/kscience/visionforge/html/VisionOfHtml;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
}
@ -813,9 +840,16 @@ public final class space/kscience/visionforge/html/VisionOfHtmlButton$Companion
}
public abstract class space/kscience/visionforge/html/VisionOfHtmlControl : space/kscience/visionforge/html/VisionOfHtml, space/kscience/visionforge/ControlVision {
public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtmlControl$Companion;
public fun <init> ()V
public synthetic fun <init> (ILspace/kscience/dataforge/meta/MutableMeta;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun dispatchControlEvent (Lspace/kscience/visionforge/VisionControlEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getControlEventFlow ()Lkotlinx/coroutines/flow/SharedFlow;
public static final synthetic fun write$Self (Lspace/kscience/visionforge/html/VisionOfHtmlControl;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
}
public final class space/kscience/visionforge/html/VisionOfHtmlControl$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class space/kscience/visionforge/html/VisionOfHtmlForm : space/kscience/visionforge/html/VisionOfHtmlControl {
@ -849,11 +883,9 @@ public final class space/kscience/visionforge/html/VisionOfHtmlFormKt {
public class space/kscience/visionforge/html/VisionOfHtmlInput : space/kscience/visionforge/html/VisionOfHtmlControl {
public static final field Companion Lspace/kscience/visionforge/html/VisionOfHtmlInput$Companion;
public synthetic fun <init> (ILjava/lang/String;Lspace/kscience/visionforge/html/InputFeedbackMode;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (Ljava/lang/String;Lspace/kscience/visionforge/html/InputFeedbackMode;)V
public synthetic fun <init> (Ljava/lang/String;Lspace/kscience/visionforge/html/InputFeedbackMode;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (ILspace/kscience/dataforge/meta/MutableMeta;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (Ljava/lang/String;)V
public final fun getDisabled ()Z
public final fun getFeedbackMode ()Lspace/kscience/visionforge/html/InputFeedbackMode;
public final fun getFieldName ()Ljava/lang/String;
public final fun getInputType ()Ljava/lang/String;
public final fun getValue ()Lspace/kscience/dataforge/meta/Value;
@ -891,6 +923,8 @@ public final class space/kscience/visionforge/html/VisionOfHtmlKt {
public static synthetic fun htmlRangeField$default (Lspace/kscience/visionforge/html/VisionOutput;Ljava/lang/Number;Ljava/lang/Number;Ljava/lang/Number;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/html/VisionOfRangeField;
public static final fun htmlTextField (Lspace/kscience/visionforge/html/VisionOutput;Lkotlin/jvm/functions/Function1;)Lspace/kscience/visionforge/html/VisionOfTextField;
public static synthetic fun htmlTextField$default (Lspace/kscience/visionforge/html/VisionOutput;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/visionforge/html/VisionOfTextField;
public static final fun onInput (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
public static synthetic fun onInput$default (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
public static final fun onValueChange (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job;
public static synthetic fun onValueChange$default (Lspace/kscience/visionforge/html/VisionOfHtmlInput;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
}

View File

@ -83,9 +83,32 @@ public class VisionValueChangeEvent(override val meta: Meta) : VisionControlEven
override fun toString(): String = meta.toString()
}
public fun VisionValueChangeEvent(value: Value?, name: Name? = null): VisionValueChangeEvent = VisionValueChangeEvent(
Meta {
this.value = value
name?.let { set("name", it.toString()) }
}
)
@Serializable
@SerialName("control.input")
public class VisionInputEvent(override val meta: Meta) : VisionControlEvent() {
public val value: Value? get() = meta.value
/**
* The name of a control that fired the event
*/
public val name: Name? get() = meta["name"]?.string?.parseAsName()
override fun toString(): String = meta.toString()
}
public fun VisionInputEvent(value: Value?, name: Name? = null): VisionInputEvent = VisionInputEvent(
Meta {
this.value = value
name?.let { set("name", it.toString()) }
}
)

View File

@ -1,5 +1,7 @@
package space.kscience.visionforge
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import space.kscience.dataforge.context.Plugin
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
@ -16,6 +18,10 @@ public interface VisionClient: Plugin {
public fun notifyPropertyChanged(visionName: Name, propertyName: Name, item: Meta?)
}
public fun VisionClient.sendEventAsync(targetName: Name, event: VisionEvent): Job = context.launch {
sendEvent(targetName, event)
}
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Meta?) {
notifyPropertyChanged(visionName, propertyName.parseAsName(true), item)
}

View File

@ -85,6 +85,7 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont
subclass(VisionMetaEvent.serializer())
subclass(VisionClickEvent.serializer())
subclass(VisionValueChangeEvent.serializer())
subclass(VisionInputEvent.serializer())
}
}

View File

@ -265,14 +265,14 @@ public abstract class AbstractVisionProperties(
public fun VisionProperties.getValue(
name: String,
inherit: Boolean? = null,
inherit: Boolean,
includeStyles: Boolean? = null,
): Value? = getValue(name.parseAsName(), inherit, includeStyles)
/**
* Get [Vision] property using key as a String
*/
public fun VisionProperties.get(
public operator fun VisionProperties.get(
name: String,
inherit: Boolean? = null,
includeStyles: Boolean? = null,
@ -292,7 +292,7 @@ public fun MutableVisionProperties.root(
/**
* Get [Vision] property using key as a String
*/
public fun MutableVisionProperties.get(
public operator fun MutableVisionProperties.get(
name: String,
inherit: Boolean? = null,
includeStyles: Boolean? = null,

View File

@ -12,15 +12,16 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.asName
import space.kscience.visionforge.AbstractVision
import space.kscience.visionforge.ControlVision
import space.kscience.visionforge.VisionControlEvent
import space.kscience.visionforge.VisionValueChangeEvent
import space.kscience.visionforge.*
@Serializable
public abstract class VisionOfHtml : AbstractVision() {
public var classes: List<String> by properties.stringList(*emptyArray())
public var classes: Set<String>
get() = properties.get(::classes.name,false).stringList?.toSet() ?: emptySet()
set(value) {
properties[::classes.name] = value.map { it.asValue() }
}
}
@Serializable
@ -58,6 +59,7 @@ public enum class InputFeedbackMode {
NONE
}
@Serializable
public abstract class VisionOfHtmlControl: VisionOfHtml(), ControlVision{
@Transient
@ -76,7 +78,6 @@ public abstract class VisionOfHtmlControl: VisionOfHtml(), ControlVision{
@SerialName("html.input")
public open class VisionOfHtmlInput(
public val inputType: String,
public val feedbackMode: InputFeedbackMode = InputFeedbackMode.ONCHANGE,
) : VisionOfHtmlControl() {
public var value: Value? by properties.value()
public var disabled: Boolean by properties.boolean { false }
@ -92,6 +93,11 @@ public fun VisionOfHtmlInput.onValueChange(
callback: suspend VisionValueChangeEvent.() -> Unit,
): Job = controlEventFlow.filterIsInstance<VisionValueChangeEvent>().onEach(callback).launchIn(scope)
public fun VisionOfHtmlInput.onInput(
scope: CoroutineScope = manager?.context ?: error("Coroutine context is not resolved for $this"),
callback: suspend VisionInputEvent.() -> Unit,
): Job = controlEventFlow.filterIsInstance<VisionInputEvent>().onEach(callback).launchIn(scope)
@Suppress("UnusedReceiverParameter")
public inline fun VisionOutput.htmlInput(
inputType: String,

View File

@ -40,7 +40,7 @@ internal class VisionPropertyTest {
@Test
fun testPropertyEdit() {
val vision = manager.group()
vision.properties.get("fff.ddd").apply {
vision.properties["fff.ddd"].apply {
value = 2.asValue()
}
assertEquals(2, vision.properties.getValue("fff.ddd")?.int)
@ -50,7 +50,7 @@ internal class VisionPropertyTest {
@Test
fun testPropertyUpdate() {
val vision = manager.group()
vision.properties.get("fff").updateWith(TestScheme) {
vision.properties["fff"].updateWith(TestScheme) {
ddd = 2
}
assertEquals(2, vision.properties.getValue("fff.ddd")?.int)

View File

@ -1,18 +1,14 @@
package space.kscience.visionforge
import kotlinx.coroutines.launch
import kotlinx.dom.clear
import kotlinx.html.InputType
import kotlinx.html.div
import kotlinx.html.js.input
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event
import space.kscience.dataforge.meta.Value
import space.kscience.dataforge.meta.asValue
import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.html.*
/**
@ -26,13 +22,6 @@ internal fun HTMLElement.subscribeToVision(vision: VisionOfHtml) {
}
}
private fun VisionClient.sendInputEvent(name: Name, value: Value?) {
context.launch {
sendEvent(name, VisionValueChangeEvent(value, name))
}
}
/**
* Subscribes the HTML input element to a given vision.
*
@ -62,16 +51,13 @@ internal val inputVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<
input {
type = InputType.text
}.also { htmlInputElement ->
val onEvent: (Event) -> Unit = {
client.sendInputEvent(name, htmlInputElement.value.asValue())
htmlInputElement.onchange = {
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
}
when (vision.feedbackMode) {
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
htmlInputElement.oninput = {
client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name))
}
htmlInputElement.subscribeToInput(vision)
@ -86,18 +72,16 @@ internal val checkboxVisionRenderer: ElementVisionRenderer =
input {
type = InputType.checkBox
}.also { htmlInputElement ->
val onEvent: (Event) -> Unit = {
client.sendInputEvent(name, htmlInputElement.checked.asValue())
htmlInputElement.onchange = {
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
}
when (vision.feedbackMode) {
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
htmlInputElement.oninput = {
client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name))
}
htmlInputElement.subscribeToInput(vision)
vision.useProperty(VisionOfCheckbox::checked) {
htmlInputElement.checked = it ?: false
@ -110,16 +94,13 @@ internal val textVisionRenderer: ElementVisionRenderer =
input {
type = InputType.text
}.also { htmlInputElement ->
val onEvent: (Event) -> Unit = {
client.sendInputEvent(name, htmlInputElement.value.asValue())
htmlInputElement.onchange = {
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
}
when (vision.feedbackMode) {
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
htmlInputElement.oninput = {
client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name))
}
htmlInputElement.subscribeToInput(vision)
@ -135,18 +116,19 @@ internal val numberVisionRenderer: ElementVisionRenderer =
type = InputType.number
}.also { htmlInputElement ->
val onEvent: (Event) -> Unit = {
htmlInputElement.onchange = {
htmlInputElement.value.toDoubleOrNull()?.let {
client.sendInputEvent(name, htmlInputElement.value.asValue())
client.sendEventAsync(name, VisionValueChangeEvent(it.asValue(), name))
}
}
when (vision.feedbackMode) {
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
htmlInputElement.oninput = {
htmlInputElement.value.toDoubleOrNull()?.let {
client.sendEventAsync(name, VisionInputEvent(it.asValue(), name))
}
}
htmlInputElement.subscribeToInput(vision)
vision.useProperty(VisionOfNumberField::value) {
htmlInputElement.valueAsNumber = it?.double ?: 0.0
@ -163,18 +145,18 @@ internal val rangeVisionRenderer: ElementVisionRenderer =
step = vision.step.toString()
}.also { htmlInputElement ->
val onEvent: (Event) -> Unit = {
htmlInputElement.onchange = {
htmlInputElement.value.toDoubleOrNull()?.let {
client.sendInputEvent(name, htmlInputElement.value.asValue())
client.sendEventAsync(name, VisionValueChangeEvent(it.asValue(), name))
}
}
when (vision.feedbackMode) {
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
InputFeedbackMode.NONE -> {}
htmlInputElement.oninput = {
htmlInputElement.value.toDoubleOrNull()?.let {
client.sendEventAsync(name, VisionInputEvent(it.asValue(), name))
}
}
htmlInputElement.subscribeToInput(vision)
vision.useProperty(VisionOfRangeField::value) {
htmlInputElement.valueAsNumber = it?.double ?: 0.0

View File

@ -4,6 +4,7 @@ import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import space.kscience.dataforge.meta.getValue
import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.set
import space.kscience.dataforge.meta.string