diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/MetaProperty.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/MetaProperty.kt deleted file mode 100644 index 85435602..00000000 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/MetaProperty.kt +++ /dev/null @@ -1,31 +0,0 @@ -package space.kscience.dataforge.properties - - -import space.kscience.dataforge.meta.* -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.startsWith - -@DFExperimental -public class MetaProperty( - public val meta: ObservableMutableMeta, - public val name: Name, - public val converter: MetaConverter, -) : Property { - - override var value: T? - get() = converter.readNullable(meta[name]) - set(value) { - meta[name] = converter.convertNullable(value) ?: Meta.EMPTY - } - - override fun onChange(owner: Any?, callback: (T?) -> Unit) { - meta.onChange(owner) { name -> - if (name.startsWith(this@MetaProperty.name)) callback(converter.readNullable(this[name])) - } - } - - override fun removeChangeListener(owner: Any?) { - meta.removeListener(owner) - } -} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/Property.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/Property.kt deleted file mode 100644 index b11fb2e1..00000000 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/Property.kt +++ /dev/null @@ -1,45 +0,0 @@ -package space.kscience.dataforge.properties - -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import space.kscience.dataforge.misc.DFExperimental - -@DFExperimental -public interface Property { - public var value: T - - public fun onChange(owner: Any? = null, callback: (T) -> Unit) - public fun removeChangeListener(owner: Any? = null) -} - -@DFExperimental -public fun Property.toFlow(): StateFlow = MutableStateFlow(value).also { stateFlow -> - onChange { - stateFlow.value = it - } -} - -/** - * Reflect all changes in the [source] property onto this property. Does not reflect changes back. - * - * @return a mirroring job - */ -@DFExperimental -public fun Property.mirror(source: Property) { - source.onChange(this) { - this.value = it - } -} - -/** - * Bi-directional connection between properties - */ -@DFExperimental -public fun Property.bind(other: Property) { - onChange(other) { - other.value = it - } - other.onChange { - this.value = it - } -} \ No newline at end of file diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt new file mode 100644 index 00000000..90fafc5e --- /dev/null +++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt @@ -0,0 +1,51 @@ +package space.kscience.dataforge.properties + + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import space.kscience.dataforge.meta.* +import space.kscience.dataforge.misc.DFExperimental + +@DFExperimental +public fun ObservableMeta.asFlow(converter: MetaSpec): Flow = callbackFlow { + onChange(this){ + trySend(converter.read(this)) + } + + awaitClose{ + removeListener(this) + } +} + +@DFExperimental +public fun MutableMeta.listenTo( + scope: CoroutineScope, + converter: MetaConverter, + flow: Flow, +): Job = flow.onEach { + update(converter.convert(it)) +}.launchIn(scope) + +@DFExperimental +public fun ObservableMutableMeta.bind( + scope: CoroutineScope, + converter: MetaConverter, + flow: MutableSharedFlow, +): Job = scope.launch{ + listenTo(this, converter,flow) + onChange(flow){ + launch { + flow.emit(converter.read(this@onChange)) + } + } + flow.onCompletion { + removeListener(flow) + } +}.also { + it.invokeOnCompletion { + removeListener(flow) + } +} diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/schemeProperty.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/schemeProperty.kt deleted file mode 100644 index 3b4d948d..00000000 --- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/schemeProperty.kt +++ /dev/null @@ -1,31 +0,0 @@ -package space.kscience.dataforge.properties - - -import space.kscience.dataforge.meta.Scheme -import space.kscience.dataforge.misc.DFExperimental -import space.kscience.dataforge.names.parseAsName -import space.kscience.dataforge.names.startsWith -import kotlin.reflect.KMutableProperty1 - -@DFExperimental -public fun S.property(property: KMutableProperty1): Property = - object : Property { - override var value: T? - get() = property.get(this@property) - set(value) { - property.set(this@property, value) - } - - override fun onChange(owner: Any?, callback: (T?) -> Unit) { - this@property.meta.onChange(this) { name -> - if (name.startsWith(property.name.parseAsName(true))) { - callback(property.get(this@property)) - } - } - } - - override fun removeChangeListener(owner: Any?) { - this@property.meta.removeListener(this@property) - } - - } \ No newline at end of file diff --git a/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/properties/MetaPropertiesTest.kt b/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/properties/MetaPropertiesTest.kt deleted file mode 100644 index 00b71673..00000000 --- a/dataforge-context/src/commonTest/kotlin/space/kscience/dataforge/properties/MetaPropertiesTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -package space.kscience.dataforge.properties - -import space.kscience.dataforge.meta.Scheme -import space.kscience.dataforge.meta.SchemeSpec -import space.kscience.dataforge.meta.int -import space.kscience.dataforge.misc.DFExperimental -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class TestScheme : Scheme() { - var a by int() - var b by int() - companion object : SchemeSpec(::TestScheme) -} - -@DFExperimental -class MetaPropertiesTest { - @Test - fun testBinding() { - val scheme = TestScheme.empty() - val a = scheme.property(TestScheme::a) - val b = scheme.property(TestScheme::b) - a.bind(b) - scheme.a = 2 - assertEquals(2, scheme.b) - assertEquals(2, b.value) - } -} \ No newline at end of file diff --git a/dataforge-context/src/jsMain/kotlin/space/kscience/dataforge/properties/bindings.kt b/dataforge-context/src/jsMain/kotlin/space/kscience/dataforge/properties/bindings.kt deleted file mode 100644 index b4451c97..00000000 --- a/dataforge-context/src/jsMain/kotlin/space/kscience/dataforge/properties/bindings.kt +++ /dev/null @@ -1,32 +0,0 @@ -package space.kscience.dataforge.properties - -import org.w3c.dom.HTMLInputElement -import space.kscience.dataforge.misc.DFExperimental - -@DFExperimental -public fun HTMLInputElement.bindValue(property: Property) { - if (this.onchange != null) error("Input element already bound") - this.onchange = { - property.value = this.value - Unit - } - property.onChange(this) { - if (value != it) { - value = it - } - } -} - -@DFExperimental -public fun HTMLInputElement.bindChecked(property: Property) { - if (this.onchange != null) error("Input element already bound") - this.onchange = { - property.value = this.checked - Unit - } - property.onChange(this) { - if (checked != it) { - checked = it - } - } -} \ No newline at end of file diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt index 4a3db511..b481962e 100644 --- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt +++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt @@ -17,12 +17,15 @@ internal data class MetaListener( */ public interface ObservableMeta : Meta { /** - * Add change listener to this meta. Owner is declared to be able to remove listeners later. Listener without owner could not be removed + * Add change listener to this meta. The Owner is declared to be able to remove listeners later. + * Listeners without an owner could be only removed all together. + * + * `this` object in the listener represents the current state of this meta. The name points to a changed node */ public fun onChange(owner: Any?, callback: Meta.(name: Name) -> Unit) /** - * Remove all listeners belonging to given owner + * Remove all listeners belonging to the given [owner]. Passing null removes all listeners. */ public fun removeListener(owner: Any?)