Stylesheets moved to properties

This commit is contained in:
Alexander Nozik 2020-12-10 12:38:35 +03:00
parent 6a742658af
commit a38d70bade
14 changed files with 62 additions and 114 deletions

View File

@ -10,6 +10,7 @@
- Point3D and Point2D are made separate classes instead of expect/actual (to split up different engines.
- JavaFX support moved to a separate module
- Threejs support moved to a separate module
- \[Breaking change!\] Stylesheets are moved into properties under `@stylesheet` key
### Deprecated

View File

@ -3,7 +3,7 @@ plugins {
}
val dataforgeVersion by extra("0.2.1-dev-4")
val ktorVersion by extra("1.4.2")
val ktorVersion by extra("1.4.3")
val htmlVersion by extra("0.7.2")
val kotlinWrappersVersion by extra("pre.129-kotlin-1.4.20")
val fxVersion by extra("14")

View File

@ -3,9 +3,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.getStyle
import hep.dataforge.vision.react.configEditor
import hep.dataforge.vision.react.metaViewer
import hep.dataforge.vision.resolveStyle
import org.w3c.dom.Element
import react.RBuilder
import react.dom.render
@ -24,7 +24,7 @@ public fun RBuilder.visionPropertyEditor(
card("Styles") {
accordion("styles") {
styles.forEach { styleName ->
val style = item.resolveStyle(styleName)
val style = item.getStyle(styleName)
if (style != null) {
entry(styleName) {
metaViewer(style)

View File

@ -1,62 +1,30 @@
@file:UseSerializers(MetaSerializer::class)
package hep.dataforge.vision
import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UseSerializers
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import hep.dataforge.names.plus
/**
* A container for styles
*/
@Serializable(StyleSheet.Companion::class)
public class StyleSheet private constructor(private val styleMap: MutableMap<String, Meta>) {
@Transient
internal var owner: Vision? = null
public inline class StyleSheet(private val owner: VisionGroup) {
public constructor(owner: Vision) : this(LinkedHashMap()) {
this.owner = owner
}
private val styleNode get() = owner.properties?.get(STYLESHEET_KEY).node
public val items: Map<String, Meta> get() = styleMap
public val items: Map<NameToken, Meta>? get() = styleNode?.items?.mapValues { it.value.node ?: Meta.EMPTY }
private fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) {
if (styles.contains(key)) {
//TODO optimize set concatenation
val tokens: Collection<Name> =
((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
.map { it.asName() }
tokens.forEach { parent?.propertyChanged(it) }
}
if (this is VisionGroup) {
for (obj in this) {
obj.styleChanged(key, oldStyle, newStyle)
}
}
}
public operator fun get(key: String): Meta? {
return styleMap[key] ?: owner?.parent?.styleSheet?.get(key)
}
public operator fun get(key: String): Meta? = owner.getStyle(key)
/**
* Define a style without notifying owner
*/
public fun define(key: String, style: Meta?) {
if (style == null) {
styleMap.remove(key)
styleNode?.remove(key)
} else {
styleMap[key] = style
owner.config[STYLESHEET_KEY + key] = style
}
}
@ -64,11 +32,13 @@ public class StyleSheet private constructor(private val styleMap: MutableMap<Str
* Set or clear the style
*/
public operator fun set(key: String, style: Meta?) {
val oldStyle = styleMap[key]
val oldStyle = get(key)
define(key, style)
owner?.styleChanged(key, oldStyle, style)
owner.styleChanged(key, oldStyle, style)
}
public inline operator fun invoke(block: StyleSheet.() -> Unit): Unit = this.block()
/**
* Create and set a style
*/
@ -77,33 +47,33 @@ public class StyleSheet private constructor(private val styleMap: MutableMap<Str
set(key, newStyle.seal())
}
public fun update(key: String, meta: Meta) {
val existing = get(key)
set(key, existing?.edit { this.update(meta) } ?: meta)
public companion object {
public val STYLESHEET_KEY: Name = "@stylesheet".asName()
}
}
public fun update(other: StyleSheet) {
other.items.forEach { (key, value) ->
update(key, value)
}
internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?) {
if (styles.contains(key)) {
//TODO optimize set concatenation
val tokens: Collection<Name> =
((oldStyle?.items?.keys ?: emptySet()) + (newStyle?.items?.keys ?: emptySet()))
.map { it.asName() }
tokens.forEach { parent?.propertyChanged(it) }
}
public companion object : KSerializer<StyleSheet> {
private val mapSerializer = MapSerializer(String.serializer(), MetaSerializer)
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
override fun deserialize(decoder: Decoder): StyleSheet {
val map = mapSerializer.deserialize(decoder)
return StyleSheet(map as? MutableMap<String, Meta> ?: LinkedHashMap(map))
}
override fun serialize(encoder: Encoder, value: StyleSheet) {
mapSerializer.serialize(encoder, value.items)
if (this is VisionGroup) {
for (obj in this) {
obj.styleChanged(key, oldStyle, newStyle)
}
}
}
/**
* A stylesheet for this group and its descendants. Stylesheet is not applied directly,
* but instead is just a repository for named configurations.
*/
public val VisionGroup.styleSheet: StyleSheet get() = StyleSheet(this)
/**
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
*/
@ -111,12 +81,19 @@ public fun Vision.useStyle(name: String) {
styles = (properties[Vision.STYLE_KEY]?.stringList ?: emptyList()) + name
}
/**
* Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
*/
public tailrec fun Vision.getStyle(name: String): Meta? =
properties?.get(StyleSheet.STYLESHEET_KEY + name).node ?: parent?.getStyle(name)
/**
* Resolve an item in all style layers
*/
public fun Vision.getStyleItems(name: Name): Sequence<MetaItem<*>> {
return styles.asSequence().map {
resolveStyle(it)
getStyle(it)
}.map {
it[name]
}.filterNotNull()
@ -125,4 +102,4 @@ public fun Vision.getStyleItems(name: Name): Sequence<MetaItem<*>> {
/**
* Collect all styles for this object in a single laminate
*/
public val Vision.allStyles: Laminate get() = Laminate(styles.mapNotNull(::resolveStyle))
public val Vision.allStyles: Laminate get() = Laminate(styles.mapNotNull(::getStyle))

View File

@ -98,12 +98,6 @@ public fun Vision.setProperty(key: String, value: Any?) {
config[key] = value
}
/**
* Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
*/
public tailrec fun Vision.resolveStyle(name: String): Meta? =
(this as? VisionGroup)?.styleSheet?.get(name) ?: parent?.resolveStyle(name)
/**
* Control visibility of the element
*/

View File

@ -34,7 +34,7 @@ public open class VisionBase : Vision {
override val descriptor: NodeDescriptor? get() = null
protected fun updateStyles(names: List<String>) {
names.mapNotNull { resolveStyle(it) }.asSequence()
names.mapNotNull { getStyle(it) }.asSequence()
.flatMap { it.items.asSequence() }
.distinctBy { it.key }
.forEach {

View File

@ -3,7 +3,7 @@ package hep.dataforge.vision
import hep.dataforge.names.*
import hep.dataforge.provider.Provider
public interface VisionContainer<out V: Vision>{
public interface VisionContainer<out V : Vision> {
public operator fun get(name: Name): V?
}
@ -18,12 +18,6 @@ public interface VisionGroup : Provider, Vision, VisionContainer<Vision> {
override val defaultTarget: String get() = Vision.TYPE
/**
* A stylesheet for this group and its descendants. Stylesheet is not applied directly,
* but instead is just a repository for named configurations.
*/
public val styleSheet: StyleSheet?
/**
* A map of direct children for specific target
* (currently "visual" or "style")
@ -38,7 +32,7 @@ public interface VisionGroup : Provider, Vision, VisionContainer<Vision> {
}
res.entries
}.associate { it.toPair() }
STYLE_TARGET -> styleSheet?.items?.mapKeys { it.key.toName() } ?: emptyMap()
STYLE_TARGET -> styleSheet.items?.mapKeys { it.key.asName() } ?: emptyMap()
else -> emptyMap()
}
@ -54,7 +48,6 @@ public interface VisionGroup : Provider, Vision, VisionContainer<Vision> {
* A fix for serialization bug that writes all proper parents inside the tree after deserialization
*/
public fun attachChildren() {
styleSheet?.owner = this
children.values.forEach {
it.parent = this
(it as? VisionGroup)?.attachChildren()
@ -73,7 +66,7 @@ public operator fun VisionGroup.iterator(): Iterator<Vision> = children.values.i
public val VisionGroup.isEmpty: Boolean get() = this.children.isEmpty()
public interface VisionContainerBuilder<in V: Vision>{
public interface VisionContainerBuilder<in V : Vision> {
public operator fun set(name: Name, child: V?)
}
@ -97,9 +90,11 @@ public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision
// public operator fun set(name: Name, child: Vision?)
}
public operator fun <V: Vision> VisionContainer<V>.get(str: String): V? = get(str.toName())
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(str.toName())
public operator fun <V: Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit = set(token.asName(), child)
public operator fun <V: Vision> VisionContainerBuilder<V>.set(key: String, child: V?): Unit = set(key.toName(), child)
public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit =
set(token.asName(), child)
public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String, child: V?): Unit = set(key.toName(), child)
public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null }

View File

@ -26,19 +26,6 @@ public open class VisionGroupBase : VisionBase(), MutableVisionGroup {
*/
override val children: Map<NameToken, Vision> get() = childrenInternal
final override var styleSheet: StyleSheet? = null
private set
/**
* Update or create stylesheet
*/
public open fun styleSheet(block: StyleSheet.() -> Unit) {
if (styleSheet == null) {
styleSheet = StyleSheet(this@VisionGroupBase)
}
styleSheet!!.block()
}
override fun propertyChanged(name: Name) {
super.propertyChanged(name)
for (obj in this) {

View File

@ -5,7 +5,7 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.update
import hep.dataforge.vision.Vision
import hep.dataforge.vision.resolveStyle
import hep.dataforge.vision.getStyle
import hep.dataforge.vision.setProperty
import javafx.beans.binding.Binding
import javafx.beans.property.SimpleObjectProperty
@ -55,7 +55,7 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
private val styleBoxProperty: Binding<Node?> = configProperty.objectBinding() {
VBox().apply {
item?.styles?.forEach { styleName ->
val styleMeta = item?.resolveStyle(styleName)
val styleMeta = item?.getStyle(styleName)
if (styleMeta != null) {
titledpane(styleName, node = MetaViewer(styleMeta).root)
}

View File

@ -10,6 +10,7 @@ import hep.dataforge.names.toName
import hep.dataforge.vision.set
import hep.dataforge.vision.solid.*
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
import hep.dataforge.vision.styleSheet
import hep.dataforge.vision.useStyle
import kscience.gdml.*
import kotlin.math.cos

View File

@ -3,7 +3,10 @@ package hep.dataforge.vision.solid
import hep.dataforge.meta.update
import hep.dataforge.names.NameToken
import hep.dataforge.vision.*
import hep.dataforge.vision.Vision
import hep.dataforge.vision.VisionContainerBuilder
import hep.dataforge.vision.VisionGroup
import hep.dataforge.vision.set
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@ -28,9 +31,6 @@ public class Composite(
override val children: Map<NameToken, Vision>
get() = mapOf(NameToken("first") to first, NameToken("second") to second)
override val styleSheet: StyleSheet?
get() = null
}
public inline fun VisionContainerBuilder<Solid>.composite(

View File

@ -103,10 +103,6 @@ internal class Prototypes(
children: Map<NameToken, Vision> = emptyMap(),
) : VisionGroupBase(), PrototypeHolder {
override fun styleSheet(block: StyleSheet.() -> Unit) {
error("Can't define stylesheet for prototypes block")
}
init {
this.childrenInternal.putAll(children)
}

View File

@ -58,8 +58,6 @@ public class SolidReference(
get() = (parent as? SolidGroup)?.getPrototype(templateName)
?: error("Prototype with name $templateName not found in $parent")
override val styleSheet: StyleSheet get() = parent?.styleSheet ?: StyleSheet(this)
@Transient
private val propertyCache: HashMap<Name, Config> = HashMap()
@ -90,8 +88,6 @@ public class SolidReference(
override val prototype: Solid get() = prototypeFor(name)
override val styleSheet: StyleSheet get() = this@SolidReference.styleSheet
override val children: Map<NameToken, Vision>
get() = (prototype as? VisionGroup)?.children
?.filter { !it.key.toString().startsWith("@") }

View File

@ -3,6 +3,7 @@ package hep.dataforge.vision.solid
import hep.dataforge.meta.int
import hep.dataforge.meta.set
import hep.dataforge.names.asName
import hep.dataforge.vision.styleSheet
import hep.dataforge.vision.useStyle
import kotlin.test.Test
import kotlin.test.assertEquals