diff --git a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt index 9f4c3680..14a93eb8 100644 --- a/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt +++ b/ui/bootstrap/src/main/kotlin/hep/dataforge/vision/bootstrap/visionPropertyEditor.kt @@ -1,13 +1,9 @@ package hep.dataforge.vision.bootstrap -import hep.dataforge.meta.Meta import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.vision.Vision -import hep.dataforge.vision.allProperties -import hep.dataforge.vision.getStyle +import hep.dataforge.vision.* import hep.dataforge.vision.react.metaViewer import hep.dataforge.vision.react.propertyEditor -import hep.dataforge.vision.styles import org.w3c.dom.Element import react.RBuilder import react.dom.render @@ -19,7 +15,8 @@ public fun RBuilder.visionPropertyEditor( ) { card("Properties") { propertyEditor( - vision.allProperties(), + provider = vision.ownProperties, + defaultProvider = vision.allProperties(), updateFlow = vision.propertyNameFlow, descriptor = descriptor, key = key) @@ -44,7 +41,6 @@ public fun RBuilder.visionPropertyEditor( public fun Element.visionPropertyEditor( item: Vision, descriptor: NodeDescriptor? = item.descriptor, - default: Meta? = null, ): Unit = render(this) { - visionPropertyEditor(item, descriptor, default) + visionPropertyEditor(item, descriptor = descriptor) } \ No newline at end of file diff --git a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt index e88e42a1..c730123b 100644 --- a/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt +++ b/ui/react/src/main/kotlin/hep/dataforge/vision/react/PropertyEditor.kt @@ -22,17 +22,22 @@ import react.* import react.dom.render import styled.* -public external interface PropertyEditorItemProps : RProps { +public external interface PropertyEditorProps : RProps { /** * Root config object - always non null */ public var provider: MutableItemProvider + /** + * Provide default item (greyed out if used + */ + public var defaultProvider: ItemProvider? + /** * Full path to the displayed node in [provider]. Could be empty */ - public var name: Name + public var name: Name? /** * Root descriptor @@ -40,29 +45,35 @@ public external interface PropertyEditorItemProps : RProps { public var descriptor: NodeDescriptor? + /** + * A coroutine scope for updates + */ public var scope: CoroutineScope? /** - * + * Flow names of updated properties */ public var updateFlow: Flow? } -private val PropertyEditorItem: FunctionalComponent = +private val PropertyEditorItem: FunctionalComponent = functionalComponent("ConfigEditorItem") { props -> propertyEditorItem(props) } -private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { +private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) { var expanded: Boolean by useState { true } - var item: MetaItem<*>? by useState { props.provider.getItem(props.name) } - val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) - var actualItem: MetaItem? by useState { item ?: descriptorItem?.defaultItem() } + val itemName by useState { props.name ?: Name.EMPTY } + var item: MetaItem<*>? by useState { props.provider.getItem(itemName) } + val descriptorItem: ItemDescriptor? = props.descriptor?.get(itemName) + var actualItem: MetaItem? by useState { + item ?: props.defaultProvider?.getItem(itemName) ?: descriptorItem?.defaultItem() + } - val token = props.name.lastOrNull()?.toString() ?: "Properties" + val token = itemName.lastOrNull()?.toString() ?: "Properties" fun update() { - item = props.provider.getItem(props.name) + item = props.provider.getItem(itemName) actualItem = item ?: descriptorItem?.defaultItem() } @@ -83,15 +94,15 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { val valueChanged: (Value?) -> Unit = { if (it == null) { - props.provider.remove(props.name) + props.provider.remove(itemName) } else { - props.provider[props.name] = it + props.provider[itemName] = it } update() } val removeClick: (Event) -> Unit = { - props.provider.remove(props.name) + props.provider.remove(itemName) update() } @@ -144,7 +155,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { attrs { this.key = props.name.toString() this.provider = props.provider - this.name = props.name + token + this.name = itemName + token this.descriptor = props.descriptor } } @@ -176,7 +187,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { +TreeStyles.resizeableInput } valueChooser( - props.name, + itemName, actualItem, descriptorItem as? ValueDescriptor, valueChanged @@ -200,19 +211,14 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorItemProps) { } } -public external interface PropertyEditorProps : RProps { - public var provider: MutableItemProvider - public var updateFlow: Flow? - public var descriptor: NodeDescriptor? - public var scope: CoroutineScope? -} @JsExport -public val PropertyEditor: FunctionalComponent = functionalComponent("ConfigEditor") { props -> +public val PropertyEditor: FunctionalComponent = functionalComponent("PropertyEditor") { props -> child(PropertyEditorItem) { attrs { this.key = "" this.provider = props.provider + this.defaultProvider = props.defaultProvider this.name = Name.EMPTY this.descriptor = props.descriptor this.scope = props.scope @@ -222,17 +228,19 @@ public val PropertyEditor: FunctionalComponent = functional public fun RBuilder.propertyEditor( provider: MutableItemProvider, + defaultProvider: ItemProvider?, updateFlow: Flow? = null, descriptor: NodeDescriptor? = null, - key: Any? = null, scope: CoroutineScope? = null, + key: Any? = null, ) { child(PropertyEditor) { attrs { - this.key = key?.toString() ?: "" this.provider = provider + this.defaultProvider = defaultProvider this.updateFlow = updateFlow this.descriptor = descriptor + this.key = key?.toString() ?: "" this.scope = scope } } @@ -249,21 +257,14 @@ private fun Config.flowUpdates(): Flow = callbackFlow { } } -public fun MutableItemProvider.withDefault(default: ItemProvider): MutableItemProvider = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem<*>? = getItem(name) ?: default.getItem(name) - - override fun setItem(name: Name, item: MetaItem<*>?) = this@withDefault.setItem(name, item) -} - - public fun RBuilder.configEditor( config: Config, + default: ItemProvider? = null, descriptor: NodeDescriptor? = null, - default: Meta? = null, key: Any? = null, scope: CoroutineScope? = null, -) = propertyEditor(config.withDefault(default ?: ItemProvider.EMPTY), config.flowUpdates(), descriptor, key, scope) +): Unit = propertyEditor(config, default, config.flowUpdates(), descriptor, scope, key = key) public fun Element.configEditor( config: Config, @@ -271,8 +272,6 @@ public fun Element.configEditor( default: Meta? = null, key: Any? = null, scope: CoroutineScope? = null, -) { - render(this) { - configEditor(config,descriptor,default, key, scope) - } +): Unit = render(this) { + configEditor(config, default, descriptor, key, scope) } \ No newline at end of file diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt index 7012698d..7363d67e 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/Vision.kt @@ -4,7 +4,6 @@ import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MutableItemProvider import hep.dataforge.meta.descriptors.Described import hep.dataforge.meta.descriptors.NodeDescriptor -import hep.dataforge.meta.descriptors.get import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.toName @@ -33,13 +32,13 @@ public interface Vision : Described { /** * Get property. - * @param inherit toggles parent node property lookup - * @param includeStyles toggles inclusion of + * @param inherit toggles parent node property lookup. Null means default inheritance. + * @param includeStyles toggles inclusion of. Null means default style inclusion. */ public fun getProperty( name: Name, - inherit: Boolean = true, - includeStyles: Boolean = true, + inherit: Boolean? = null, + includeStyles: Boolean? = null, includeDefaults: Boolean = true, ): MetaItem<*>? @@ -76,6 +75,16 @@ public interface Vision : Described { } } +/** + * Own properties, excluding inheritance, styles and descriptor + */ +public val Vision.ownProperties: MutableItemProvider + get() = object : MutableItemProvider { + override fun getItem(name: Name): MetaItem<*>? = getOwnProperty(name) + override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item) + } + + /** * Convenient accessor for all properties of a vision. * @param inherit - inherit property value from the parent by default. If null, inheritance is inferred from descriptor @@ -85,16 +94,12 @@ public fun Vision.allProperties( includeStyles: Boolean? = null, includeDefaults: Boolean = true, ): MutableItemProvider = object : MutableItemProvider { - override fun getItem(name: Name): MetaItem<*>? { - val actualInherit = inherit ?: descriptor?.get(name)?.inherited ?: false - val actualUseStyles = includeStyles ?: descriptor?.get(name)?.usesStyles ?: true - return getProperty( - name, - inherit = actualInherit, - includeStyles = actualUseStyles, - includeDefaults = includeDefaults - ) - } + override fun getItem(name: Name): MetaItem<*>? = getProperty( + name, + inherit = inherit, + includeStyles = includeStyles, + includeDefaults = includeDefaults + ) override fun setItem(name: Name, item: MetaItem<*>?): Unit = setProperty(name, item) } @@ -104,8 +109,8 @@ public fun Vision.allProperties( */ public fun Vision.getProperty( key: String, - inherit: Boolean = true, - includeStyles: Boolean = true, + inherit: Boolean? = null, + includeStyles: Boolean? = null, includeDefaults: Boolean = true, ): MetaItem<*>? = getProperty(key.toName(), inherit, includeStyles, includeDefaults) diff --git a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt index c608e4c4..17f08888 100644 --- a/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt +++ b/visionforge-core/src/commonMain/kotlin/hep/dataforge/vision/VisionBase.kt @@ -59,15 +59,15 @@ public open class VisionBase : Vision { override fun getProperty( name: Name, - inherit: Boolean, - includeStyles: Boolean, + inherit: Boolean?, + includeStyles: Boolean?, includeDefaults: Boolean, ): MetaItem<*>? = sequence { yield(getOwnProperty(name)) - if (includeStyles) { + if (includeStyles ?: descriptor?.get(name)?.usesStyles != false) { yieldAll(getStyleItems(name)) } - if (inherit) { + if (inherit ?: descriptor?.get(name)?.inherited == true) { yield(parent?.getProperty(name, inherit)) } yield(descriptor?.get(name)?.defaultItem()) diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt index 77aa950c..355d2b7d 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidGroup.kt @@ -125,8 +125,8 @@ internal class Prototypes( override fun getProperty( name: Name, - inherit: Boolean, - includeStyles: Boolean, + inherit: Boolean?, + includeStyles: Boolean?, includeDefaults: Boolean, ): MetaItem<*>? = null diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt index f060afea..7c69d283 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidMaterial.kt @@ -114,16 +114,12 @@ public class SolidMaterial : Scheme() { public val Solid.color: ColorAccessor get() = ColorAccessor( - allProperties( - inherit = true, - includeStyles = true, - includeDefaults = true - ), + allProperties(inherit = true), MATERIAL_COLOR_KEY ) public var Solid.material: SolidMaterial? - get() = getProperty(MATERIAL_KEY).node?.let { SolidMaterial.read(it) } + get() = getProperty(MATERIAL_KEY, inherit = true).node?.let { SolidMaterial.read(it) } set(value) = setProperty(MATERIAL_KEY, value?.config) @VisionBuilder @@ -141,7 +137,7 @@ public fun Solid.material(builder: SolidMaterial.() -> Unit) { } public var Solid.opacity: Number? - get() = getProperty(MATERIAL_OPACITY_KEY).number + get() = getProperty(MATERIAL_OPACITY_KEY, inherit = true).number set(value) { setProperty(MATERIAL_OPACITY_KEY, value?.asValue()) } \ No newline at end of file diff --git a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt index 5378f71d..4512cf54 100644 --- a/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt +++ b/visionforge-solid/src/commonMain/kotlin/hep/dataforge/vision/solid/SolidReference.kt @@ -2,6 +2,7 @@ package hep.dataforge.vision.solid import hep.dataforge.meta.* import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.meta.descriptors.get import hep.dataforge.names.* import hep.dataforge.vision.* import kotlinx.coroutines.flow.Flow @@ -14,6 +15,24 @@ public interface SolidReference : Vision { public val prototype: Solid } +private fun SolidReference.getRefProperty( + name: Name, + inherit: Boolean?, + includeStyles: Boolean?, + includeDefaults: Boolean, +): MetaItem<*>? { + return sequence { + yield(getOwnProperty(name)) + if (includeStyles ?: descriptor?.get(name)?.usesStyles != false) { + yieldAll(getStyleItems(name)) + } + yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) + if (inherit ?: descriptor?.get(name)?.inherited == true) { + yield(parent?.getProperty(name, inherit)) + } + }.merge() +} + /** * A reference [Solid] to reuse a template object */ @@ -60,19 +79,10 @@ public class SolidReferenceGroup( override fun getProperty( name: Name, - inherit: Boolean, - includeStyles: Boolean, + inherit: Boolean?, + includeStyles: Boolean?, includeDefaults: Boolean, - ): MetaItem<*>? = sequence { - yield(getOwnProperty(name)) - if (includeStyles) { - yieldAll(getStyleItems(name)) - } - yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) - if (inherit) { - yield(parent?.getProperty(name, inherit)) - } - }.merge() + ): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults) override fun attachChildren() { //do nothing @@ -104,19 +114,10 @@ public class SolidReferenceGroup( override fun getProperty( name: Name, - inherit: Boolean, - includeStyles: Boolean, + inherit: Boolean?, + includeStyles: Boolean?, includeDefaults: Boolean, - ): MetaItem<*>? = sequence { - yield(getOwnProperty(name)) - if (includeStyles) { - yieldAll(getStyleItems(name)) - } - yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults)) - if (inherit) { - yield(parent?.getProperty(name, inherit)) - } - }.merge() + ): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults) override var parent: VisionGroup? get() { diff --git a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt index 30917b90..5f6fad96 100644 --- a/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt +++ b/visionforge-solid/src/commonTest/kotlin/hep/dataforge/vision/solid/PropertyTest.kt @@ -17,7 +17,7 @@ class PropertyTest { box = box(100, 100, 100) } } - assertEquals(22, box?.getProperty("test").int) + assertEquals(22, box?.getProperty("test", inherit = true).int) } @Test