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 {
|
||||
group = "space.kscience"
|
||||
version = "0.3.0-dev-17"
|
||||
version = "0.3.0-dev-18"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
@ -11,6 +11,7 @@ import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaRepr
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.Value
|
||||
|
||||
@Serializable
|
||||
@SerialName("control")
|
||||
@ -60,4 +61,10 @@ public fun ClickControl.onClick(scope: CoroutineScope, block: suspend VisionClic
|
||||
|
||||
@Serializable
|
||||
@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))
|
||||
}
|
||||
|
||||
public fun VisionClient.sendEvent(targetName: Name, payload: MetaRepr): Unit {
|
||||
public fun VisionClient.sendMetaEvent(targetName: Name, payload: MetaRepr): Unit {
|
||||
context.launch {
|
||||
sendEvent(targetName, VisionMetaEvent(payload.toMeta()))
|
||||
}
|
||||
|
@ -1,13 +1,19 @@
|
||||
package space.kscience.visionforge.html
|
||||
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.html.InputType
|
||||
import kotlinx.html.TagConsumer
|
||||
import kotlinx.html.stream.createHTML
|
||||
import kotlinx.serialization.SerialName
|
||||
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
|
||||
|
||||
|
||||
@Serializable
|
||||
@ -53,10 +59,23 @@ public enum class InputFeedbackMode {
|
||||
public open class VisionOfHtmlInput(
|
||||
public val inputType: String,
|
||||
public val feedbackMode: InputFeedbackMode = InputFeedbackMode.ONCHANGE,
|
||||
) : VisionOfHtml() {
|
||||
) : VisionOfHtml(), ControlVision {
|
||||
public var value: Value? by properties.value()
|
||||
public var disabled: Boolean by properties.boolean { false }
|
||||
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")
|
||||
|
@ -34,7 +34,13 @@ public interface ElementVisionRenderer : Named {
|
||||
* Display the [vision] inside a given [element] replacing its current content.
|
||||
* @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 const val TYPE: String = "elementVisionRenderer"
|
||||
@ -49,7 +55,7 @@ public interface ElementVisionRenderer : Named {
|
||||
public class SingleTypeVisionRenderer<T : Vision>(
|
||||
public val kClass: KClass<T>,
|
||||
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 {
|
||||
|
||||
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
|
||||
@ -60,15 +66,21 @@ public class SingleTypeVisionRenderer<T : Vision>(
|
||||
override fun rateVision(vision: Vision): Int =
|
||||
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.append {
|
||||
renderFunction(name, kClass.cast(vision), meta)
|
||||
renderFunction(name, client, kClass.cast(vision), meta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <reified T : Vision> ElementVisionRenderer(
|
||||
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)
|
||||
|
@ -94,8 +94,9 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
|
||||
|
||||
private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) {
|
||||
vision.setAsRoot(visionManager)
|
||||
val renderer = findRendererFor(vision) ?: error("Could not find renderer for ${vision::class}")
|
||||
renderer.render(element, name, vision, outputMeta)
|
||||
val renderer: ElementVisionRenderer =
|
||||
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) {
|
||||
|
@ -40,7 +40,7 @@ private fun HTMLInputElement.subscribeToInput(inputVision: VisionOfHtmlInput) {
|
||||
}
|
||||
|
||||
internal val htmlVisionRenderer: ElementVisionRenderer =
|
||||
ElementVisionRenderer<VisionOfPlainHtml> { _, vision, _ ->
|
||||
ElementVisionRenderer<VisionOfPlainHtml> { _, _, vision, _ ->
|
||||
div {}.also { div ->
|
||||
div.subscribeToVision(vision)
|
||||
vision.useProperty(VisionOfPlainHtml::content) {
|
||||
@ -50,12 +50,14 @@ internal val htmlVisionRenderer: ElementVisionRenderer =
|
||||
}
|
||||
|
||||
internal val inputVisionRenderer: ElementVisionRenderer =
|
||||
ElementVisionRenderer<VisionOfHtmlInput>(acceptRating = ElementVisionRenderer.DEFAULT_RATING - 1) { _, vision, _ ->
|
||||
ElementVisionRenderer<VisionOfHtmlInput>(
|
||||
acceptRating = ElementVisionRenderer.DEFAULT_RATING - 1
|
||||
) { name, client, vision, _ ->
|
||||
input {
|
||||
type = InputType.text
|
||||
}.also { htmlInputElement ->
|
||||
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 =
|
||||
ElementVisionRenderer<VisionOfCheckbox> { _, vision, _ ->
|
||||
ElementVisionRenderer<VisionOfCheckbox> { name, client, vision, _ ->
|
||||
input {
|
||||
type = InputType.checkBox
|
||||
}.also { htmlInputElement ->
|
||||
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 =
|
||||
ElementVisionRenderer<VisionOfTextField> { _, vision, _ ->
|
||||
ElementVisionRenderer<VisionOfTextField> { name, client, vision, _ ->
|
||||
input {
|
||||
type = InputType.text
|
||||
}.also { htmlInputElement ->
|
||||
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 =
|
||||
ElementVisionRenderer<VisionOfNumberField> { _, vision, _ ->
|
||||
ElementVisionRenderer<VisionOfNumberField> { name, client, vision, _ ->
|
||||
input {
|
||||
type = InputType.text
|
||||
}.also { htmlInputElement ->
|
||||
|
||||
val onEvent: (Event) -> Unit = {
|
||||
htmlInputElement.value.toDoubleOrNull()?.let { vision.number = it }
|
||||
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||
client.sendEvent(name, VisionValueChangeEvent(it.asValue()))
|
||||
}
|
||||
}
|
||||
|
||||
when (vision.feedbackMode) {
|
||||
@ -145,7 +149,7 @@ internal val numberVisionRenderer: ElementVisionRenderer =
|
||||
}
|
||||
|
||||
internal val rangeVisionRenderer: ElementVisionRenderer =
|
||||
ElementVisionRenderer<VisionOfRangeField> { _, vision, _ ->
|
||||
ElementVisionRenderer<VisionOfRangeField> { name, client, vision, _ ->
|
||||
input {
|
||||
type = InputType.text
|
||||
min = vision.min.toString()
|
||||
@ -154,7 +158,9 @@ internal val rangeVisionRenderer: ElementVisionRenderer =
|
||||
}.also { htmlInputElement ->
|
||||
|
||||
val onEvent: (Event) -> Unit = {
|
||||
htmlInputElement.value.toDoubleOrNull()?.let { vision.number = it }
|
||||
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||
client.sendEvent(name, VisionValueChangeEvent(it.asValue()))
|
||||
}
|
||||
}
|
||||
|
||||
when (vision.feedbackMode) {
|
||||
|
Loading…
Reference in New Issue
Block a user