forked from kscience/visionforge
Html input events
This commit is contained in:
parent
9fc6f1e34c
commit
bce61c0fb0
@ -12,7 +12,7 @@ val fxVersion by extra("11")
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.3.0-dev-17"
|
version = "0.3.0-dev-18"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
@ -11,6 +11,7 @@ import kotlinx.serialization.Serializable
|
|||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaRepr
|
import space.kscience.dataforge.meta.MetaRepr
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
import space.kscience.dataforge.meta.MutableMeta
|
||||||
|
import space.kscience.dataforge.meta.Value
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("control")
|
@SerialName("control")
|
||||||
@ -60,4 +61,10 @@ public fun ClickControl.onClick(scope: CoroutineScope, block: suspend VisionClic
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("control.valueChange")
|
@SerialName("control.valueChange")
|
||||||
public class VisionValueChangeEvent(override val meta: Meta) : VisionControlEvent()
|
public class VisionValueChangeEvent(override val meta: Meta) : VisionControlEvent() {
|
||||||
|
|
||||||
|
public val value: Value? get() = meta.value
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun VisionValueChangeEvent(value: Value?): VisionValueChangeEvent =
|
||||||
|
VisionValueChangeEvent(Meta { this.value = value })
|
||||||
|
@ -34,7 +34,7 @@ public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: St
|
|||||||
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
|
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun VisionClient.sendEvent(targetName: Name, payload: MetaRepr): Unit {
|
public fun VisionClient.sendMetaEvent(targetName: Name, payload: MetaRepr): Unit {
|
||||||
context.launch {
|
context.launch {
|
||||||
sendEvent(targetName, VisionMetaEvent(payload.toMeta()))
|
sendEvent(targetName, VisionMetaEvent(payload.toMeta()))
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
package space.kscience.visionforge.html
|
package space.kscience.visionforge.html
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.html.InputType
|
import kotlinx.html.InputType
|
||||||
import kotlinx.html.TagConsumer
|
import kotlinx.html.TagConsumer
|
||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.visionforge.AbstractVision
|
import space.kscience.visionforge.AbstractVision
|
||||||
|
import space.kscience.visionforge.ControlVision
|
||||||
|
import space.kscience.visionforge.VisionControlEvent
|
||||||
|
import space.kscience.visionforge.VisionValueChangeEvent
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -53,10 +59,23 @@ public enum class InputFeedbackMode {
|
|||||||
public open class VisionOfHtmlInput(
|
public open class VisionOfHtmlInput(
|
||||||
public val inputType: String,
|
public val inputType: String,
|
||||||
public val feedbackMode: InputFeedbackMode = InputFeedbackMode.ONCHANGE,
|
public val feedbackMode: InputFeedbackMode = InputFeedbackMode.ONCHANGE,
|
||||||
) : VisionOfHtml() {
|
) : VisionOfHtml(), ControlVision {
|
||||||
public var value: Value? by properties.value()
|
public var value: Value? by properties.value()
|
||||||
public var disabled: Boolean by properties.boolean { false }
|
public var disabled: Boolean by properties.boolean { false }
|
||||||
public var fieldName: String? by properties.string()
|
public var fieldName: String? by properties.string()
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private val mutableControlEventFlow = MutableSharedFlow<VisionControlEvent>()
|
||||||
|
|
||||||
|
override val controlEventFlow: SharedFlow<VisionControlEvent>
|
||||||
|
get() = mutableControlEventFlow
|
||||||
|
|
||||||
|
override fun dispatchControlEvent(event: VisionControlEvent) {
|
||||||
|
if(event is VisionValueChangeEvent){
|
||||||
|
this.value = event.value
|
||||||
|
}
|
||||||
|
mutableControlEventFlow.tryEmit(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UnusedReceiverParameter")
|
@Suppress("UnusedReceiverParameter")
|
||||||
|
@ -34,7 +34,13 @@ public interface ElementVisionRenderer : Named {
|
|||||||
* Display the [vision] inside a given [element] replacing its current content.
|
* Display the [vision] inside a given [element] replacing its current content.
|
||||||
* @param meta additional parameters for rendering container
|
* @param meta additional parameters for rendering container
|
||||||
*/
|
*/
|
||||||
public fun render(element: Element, name: Name, vision: Vision, meta: Meta = Meta.EMPTY)
|
public fun render(
|
||||||
|
element: Element,
|
||||||
|
client: VisionClient,
|
||||||
|
name: Name,
|
||||||
|
vision: Vision,
|
||||||
|
meta: Meta = Meta.EMPTY,
|
||||||
|
)
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public const val TYPE: String = "elementVisionRenderer"
|
public const val TYPE: String = "elementVisionRenderer"
|
||||||
@ -49,7 +55,7 @@ public interface ElementVisionRenderer : Named {
|
|||||||
public class SingleTypeVisionRenderer<T : Vision>(
|
public class SingleTypeVisionRenderer<T : Vision>(
|
||||||
public val kClass: KClass<T>,
|
public val kClass: KClass<T>,
|
||||||
private val acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING,
|
private val acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING,
|
||||||
private val renderFunction: TagConsumer<HTMLElement>.(name: Name, vision: T, meta: Meta) -> Unit,
|
private val renderFunction: TagConsumer<HTMLElement>.(name: Name, client: VisionClient, vision: T, meta: Meta) -> Unit,
|
||||||
) : ElementVisionRenderer {
|
) : ElementVisionRenderer {
|
||||||
|
|
||||||
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
|
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
|
||||||
@ -60,15 +66,21 @@ public class SingleTypeVisionRenderer<T : Vision>(
|
|||||||
override fun rateVision(vision: Vision): Int =
|
override fun rateVision(vision: Vision): Int =
|
||||||
if (vision::class == kClass) acceptRating else ElementVisionRenderer.ZERO_RATING
|
if (vision::class == kClass) acceptRating else ElementVisionRenderer.ZERO_RATING
|
||||||
|
|
||||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
override fun render(
|
||||||
|
element: Element,
|
||||||
|
client: VisionClient,
|
||||||
|
name: Name,
|
||||||
|
vision: Vision,
|
||||||
|
meta: Meta,
|
||||||
|
) {
|
||||||
element.clear()
|
element.clear()
|
||||||
element.append {
|
element.append {
|
||||||
renderFunction(name, kClass.cast(vision), meta)
|
renderFunction(name, client, kClass.cast(vision), meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline fun <reified T : Vision> ElementVisionRenderer(
|
public inline fun <reified T : Vision> ElementVisionRenderer(
|
||||||
acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING,
|
acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING,
|
||||||
noinline renderFunction: TagConsumer<HTMLElement>.(name: Name, vision: T, meta: Meta) -> Unit,
|
noinline renderFunction: TagConsumer<HTMLElement>.(name: Name, client: VisionClient, vision: T, meta: Meta) -> Unit,
|
||||||
): ElementVisionRenderer = SingleTypeVisionRenderer(T::class, acceptRating, renderFunction)
|
): ElementVisionRenderer = SingleTypeVisionRenderer(T::class, acceptRating, renderFunction)
|
||||||
|
@ -94,8 +94,9 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
|
|||||||
|
|
||||||
private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) {
|
private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) {
|
||||||
vision.setAsRoot(visionManager)
|
vision.setAsRoot(visionManager)
|
||||||
val renderer = findRendererFor(vision) ?: error("Could not find renderer for ${vision::class}")
|
val renderer: ElementVisionRenderer =
|
||||||
renderer.render(element, name, vision, outputMeta)
|
findRendererFor(vision) ?: error("Could not find renderer for ${vision::class}")
|
||||||
|
renderer.render(element, this, name, vision, outputMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startVisionUpdate(element: Element, visionName: Name, vision: Vision, outputMeta: Meta) {
|
private fun startVisionUpdate(element: Element, visionName: Name, vision: Vision, outputMeta: Meta) {
|
||||||
|
@ -40,7 +40,7 @@ private fun HTMLInputElement.subscribeToInput(inputVision: VisionOfHtmlInput) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val htmlVisionRenderer: ElementVisionRenderer =
|
internal val htmlVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfPlainHtml> { _, vision, _ ->
|
ElementVisionRenderer<VisionOfPlainHtml> { _, _, vision, _ ->
|
||||||
div {}.also { div ->
|
div {}.also { div ->
|
||||||
div.subscribeToVision(vision)
|
div.subscribeToVision(vision)
|
||||||
vision.useProperty(VisionOfPlainHtml::content) {
|
vision.useProperty(VisionOfPlainHtml::content) {
|
||||||
@ -50,12 +50,14 @@ internal val htmlVisionRenderer: ElementVisionRenderer =
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val inputVisionRenderer: ElementVisionRenderer =
|
internal val inputVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfHtmlInput>(acceptRating = ElementVisionRenderer.DEFAULT_RATING - 1) { _, vision, _ ->
|
ElementVisionRenderer<VisionOfHtmlInput>(
|
||||||
|
acceptRating = ElementVisionRenderer.DEFAULT_RATING - 1
|
||||||
|
) { name, client, vision, _ ->
|
||||||
input {
|
input {
|
||||||
type = InputType.text
|
type = InputType.text
|
||||||
}.also { htmlInputElement ->
|
}.also { htmlInputElement ->
|
||||||
val onEvent: (Event) -> Unit = {
|
val onEvent: (Event) -> Unit = {
|
||||||
vision.value = htmlInputElement.value.asValue()
|
client.sendEvent(name, VisionValueChangeEvent(htmlInputElement.value.asValue()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -74,12 +76,12 @@ internal val inputVisionRenderer: ElementVisionRenderer =
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val checkboxVisionRenderer: ElementVisionRenderer =
|
internal val checkboxVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfCheckbox> { _, vision, _ ->
|
ElementVisionRenderer<VisionOfCheckbox> { name, client, vision, _ ->
|
||||||
input {
|
input {
|
||||||
type = InputType.checkBox
|
type = InputType.checkBox
|
||||||
}.also { htmlInputElement ->
|
}.also { htmlInputElement ->
|
||||||
val onEvent: (Event) -> Unit = {
|
val onEvent: (Event) -> Unit = {
|
||||||
vision.checked = htmlInputElement.checked
|
client.sendEvent(name, VisionValueChangeEvent(htmlInputElement.checked.asValue()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -98,12 +100,12 @@ internal val checkboxVisionRenderer: ElementVisionRenderer =
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val textVisionRenderer: ElementVisionRenderer =
|
internal val textVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfTextField> { _, vision, _ ->
|
ElementVisionRenderer<VisionOfTextField> { name, client, vision, _ ->
|
||||||
input {
|
input {
|
||||||
type = InputType.text
|
type = InputType.text
|
||||||
}.also { htmlInputElement ->
|
}.also { htmlInputElement ->
|
||||||
val onEvent: (Event) -> Unit = {
|
val onEvent: (Event) -> Unit = {
|
||||||
vision.text = htmlInputElement.value
|
client.sendEvent(name, VisionValueChangeEvent(htmlInputElement.value.asValue()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -122,13 +124,15 @@ internal val textVisionRenderer: ElementVisionRenderer =
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val numberVisionRenderer: ElementVisionRenderer =
|
internal val numberVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfNumberField> { _, vision, _ ->
|
ElementVisionRenderer<VisionOfNumberField> { name, client, vision, _ ->
|
||||||
input {
|
input {
|
||||||
type = InputType.text
|
type = InputType.text
|
||||||
}.also { htmlInputElement ->
|
}.also { htmlInputElement ->
|
||||||
|
|
||||||
val onEvent: (Event) -> Unit = {
|
val onEvent: (Event) -> Unit = {
|
||||||
htmlInputElement.value.toDoubleOrNull()?.let { vision.number = it }
|
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||||
|
client.sendEvent(name, VisionValueChangeEvent(it.asValue()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (vision.feedbackMode) {
|
when (vision.feedbackMode) {
|
||||||
@ -145,7 +149,7 @@ internal val numberVisionRenderer: ElementVisionRenderer =
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val rangeVisionRenderer: ElementVisionRenderer =
|
internal val rangeVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfRangeField> { _, vision, _ ->
|
ElementVisionRenderer<VisionOfRangeField> { name, client, vision, _ ->
|
||||||
input {
|
input {
|
||||||
type = InputType.text
|
type = InputType.text
|
||||||
min = vision.min.toString()
|
min = vision.min.toString()
|
||||||
@ -154,7 +158,9 @@ internal val rangeVisionRenderer: ElementVisionRenderer =
|
|||||||
}.also { htmlInputElement ->
|
}.also { htmlInputElement ->
|
||||||
|
|
||||||
val onEvent: (Event) -> Unit = {
|
val onEvent: (Event) -> Unit = {
|
||||||
htmlInputElement.value.toDoubleOrNull()?.let { vision.number = it }
|
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||||
|
client.sendEvent(name, VisionValueChangeEvent(it.asValue()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (vision.feedbackMode) {
|
when (vision.feedbackMode) {
|
||||||
|
Loading…
Reference in New Issue
Block a user