Stylesheets moved to properties
This commit is contained in:
parent
6a742658af
commit
a38d70bade
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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))
|
@ -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
|
||||
*/
|
||||
|
@ -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 {
|
||||
|
@ -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 }
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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("@") }
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user