New styling design

This commit is contained in:
Alexander Nozik 2019-10-01 14:23:54 +03:00
parent c5f14fb5e7
commit 2fcfd767c1
11 changed files with 151 additions and 71 deletions

View File

@ -21,6 +21,7 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), VisualGroup {
*/ */
abstract override val children: Map<NameToken, VisualObject> //get() = _children abstract override val children: Map<NameToken, VisualObject> //get() = _children
// init { // init {
// //Do after deserialization // //Do after deserialization
// children.values.forEach { // children.values.forEach {

View File

@ -1,8 +1,10 @@
package hep.dataforge.vis.common package hep.dataforge.vis.common
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.toName
import hep.dataforge.vis.common.VisualObject.Companion.STYLE_KEY
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
internal data class PropertyListener( internal data class PropertyListener(
@ -15,17 +17,21 @@ abstract class AbstractVisualObject : VisualObject {
@Transient @Transient
override var parent: VisualObject? = null override var parent: VisualObject? = null
override var style: Meta? = null protected abstract var properties: Config?
override var style: List<String>
get() = properties?.let { it[STYLE_KEY].stringList } ?: emptyList()
set(value) { set(value) {
//notify about style removed setProperty(VisualObject.STYLE_KEY, value)
style?.items?.forEach {(name, value) -> }
propertyChanged(name.asName(), value, null)
} /**
field = value * The config is initialized and assigned on-demand.
//notify about style adition * To avoid unnecessary allocations, one should access [properties] via [getProperty] instead.
value?.items?.forEach { (name, value) -> */
propertyChanged(name.asName(), null, value) override val config: Config
} get() = properties ?: Config().also { config ->
properties = config.apply { onChange(this, ::propertyChanged) }
} }
@Transient @Transient
@ -45,22 +51,32 @@ abstract class AbstractVisualObject : VisualObject {
listeners.removeAll { it.owner == owner } listeners.removeAll { it.owner == owner }
} }
protected abstract var properties: Config?
override val config: Config
get() = properties ?: Config().also { config ->
properties = config.apply { onChange(this, ::propertyChanged) }
}
override fun setProperty(name: Name, value: Any?) { override fun setProperty(name: Name, value: Any?) {
config[name] = value config[name] = value
} }
private var styleCache: Laminate? = null
private fun styles(): Laminate {
return styleCache ?: kotlin.run {
Laminate(style.map { it.toName() }.mapNotNull(::findStyle))
.also { styleCache = it }
}
}
/**
* Helper to reset style cache
*/
protected fun styleChanged() {
styleCache = null
propertyChanged(EmptyName)
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {
style?.get(name) ?: properties?.get(name) ?: parent?.getProperty(name, inherit) properties?.get(name) ?: parent?.getProperty(name, inherit) ?: styles()[name]
} else { } else {
style?.get(name) ?: properties?.get(name) properties?.get(name) ?: styles()[name]
} }
} }
@ -72,3 +88,11 @@ abstract class AbstractVisualObject : VisualObject {
updateMeta() updateMeta()
} }
} }
internal fun VisualObject.findStyle(styleName: Name): Meta? {
if (this is VisualGroup) {
val style = getStyle(styleName)
if (style != null) return style
}
return parent?.findStyle(styleName)
}

View File

@ -1,5 +1,7 @@
package hep.dataforge.vis.common package hep.dataforge.vis.common
import kotlin.math.max
/** /**
* Taken from https://github.com/markaren/three.kt/blob/master/threejs-wrapper/src/main/kotlin/info/laht/threekt/math/ColorConstants.kt * Taken from https://github.com/markaren/three.kt/blob/master/threejs-wrapper/src/main/kotlin/info/laht/threekt/math/ColorConstants.kt
*/ */
@ -177,6 +179,6 @@ object Colors {
fun rgbToString(rgb: Int): String { fun rgbToString(rgb: Int): String {
val string = rgb.toString(16) val string = rgb.toString(16)
return "#" + string.substring(string.length - 6) return "#" + string.substring(max(0, string.length - 6))
} }
} }

View File

@ -1,5 +1,6 @@
package hep.dataforge.vis.common package hep.dataforge.vis.common
import hep.dataforge.meta.Meta
import hep.dataforge.names.* import hep.dataforge.names.*
import hep.dataforge.provider.Provider import hep.dataforge.provider.Provider
@ -11,18 +12,19 @@ interface VisualGroup : VisualObject, Provider, Iterable<VisualObject> {
override val defaultTarget: String get() = VisualObject.TYPE override val defaultTarget: String get() = VisualObject.TYPE
override fun provideTop(target: String): Map<Name, VisualObject> = if (target == VisualObject.TYPE) { override fun provideTop(target: String): Map<Name, Any> =
children.flatMap { (key, value) -> when (target) {
val res: Map<Name, VisualObject> = if (value is VisualGroup) { VisualObject.TYPE -> children.flatMap { (key, value) ->
value.provideTop(target).mapKeys { key + it.key } val res: Map<Name, Any> = if (value is VisualGroup) {
} else { value.provideTop(target).mapKeys { key + it.key }
mapOf(key.asName() to value) } else {
} mapOf(key.asName() to value)
res.entries }
}.associate { it.toPair() } res.entries
} else { }.associate { it.toPair() }
emptyMap() //TODO add styles
} else -> emptyMap()
}
/** /**
* Iterate over children of this group * Iterate over children of this group
@ -30,7 +32,20 @@ interface VisualGroup : VisualObject, Provider, Iterable<VisualObject> {
override fun iterator(): Iterator<VisualObject> = children.values.iterator() override fun iterator(): Iterator<VisualObject> = children.values.iterator()
/** /**
* Add listener for children change * Resolve style by its name
* TODO change to Config?
*/
fun getStyle(name: Name): Meta?
/**
* Add or replace style with given name
*/
fun setStyle(name: Name, meta: Meta)
/**
* Add listener for children structure change.
* @param owner the handler to properly remove listeners
* @param action First argument of the action is the name of changed child. Second argument is the new value of the object.
*/ */
fun onChildrenChange(owner: Any?, action: (Name, VisualObject?) -> Unit) fun onChildrenChange(owner: Any?, action: (Name, VisualObject?) -> Unit)

View File

@ -1,14 +1,17 @@
package hep.dataforge.vis.common package hep.dataforge.vis.common
import hep.dataforge.meta.* import hep.dataforge.meta.Configurable
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MetaRepr
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject.Companion.TYPE import hep.dataforge.vis.common.VisualObject.Companion.TYPE
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers) //private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers)
private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta) //private fun Laminate.withBottom(meta: Meta): Laminate = Laminate(layers + meta)
/** /**
* A root type for display hierarchy * A root type for display hierarchy
@ -22,11 +25,6 @@ interface VisualObject : MetaRepr, Configurable {
@Transient @Transient
var parent: VisualObject? var parent: VisualObject?
/**
* A style which is set externally and could not be modified from inside
*/
var style: Meta?
/** /**
* Set property for this object * Set property for this object
*/ */
@ -52,8 +50,11 @@ interface VisualObject : MetaRepr, Configurable {
*/ */
fun removeChangeListener(owner: Any?) fun removeChangeListener(owner: Any?)
var style: List<String>
companion object { companion object {
const val TYPE = "visual" const val TYPE = "visual"
val STYLE_KEY = "style".asName()
//const val META_KEY = "@meta" //const val META_KEY = "@meta"
//const val TAGS_KEY = "@tags" //const val TAGS_KEY = "@tags"
@ -62,4 +63,3 @@ interface VisualObject : MetaRepr, Configurable {
fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit) fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit)
fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value) fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value)

View File

@ -1,7 +1,6 @@
package hep.dataforge.vis.common package hep.dataforge.vis.common
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.asName import hep.dataforge.names.asName
@ -11,12 +10,11 @@ import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
fun String.asName() = if (isBlank()) EmptyName else NameToken(this).asName()
/** /**
* A delegate for display object properties * A delegate for display object properties
*/ */
class DisplayObjectDelegate( class VisualObjectDelegate(
val key: Name?, val key: Name?,
val default: MetaItem<*>?, val default: MetaItem<*>?,
val inherited: Boolean val inherited: Boolean
@ -36,7 +34,7 @@ class DisplayObjectDelegate(
} }
} }
class DisplayObjectDelegateWrapper<T>( class VisualObjectDelegateWrapper<T>(
val key: Name?, val key: Name?,
val default: T, val default: T,
val inherited: Boolean, val inherited: Boolean,
@ -63,55 +61,55 @@ class DisplayObjectDelegateWrapper<T>(
fun VisualObject.value(default: Value? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.value(default: Value? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.value } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.value }
fun VisualObject.string(default: String? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.string(default: String? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.string } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.string }
fun VisualObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.boolean(default: Boolean? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean }
fun VisualObject.number(default: Number? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.number(default: Number? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.number } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.number }
fun VisualObject.double(default: Double? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.double(default: Double? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.double } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.double }
fun VisualObject.int(default: Int? = null, key: String? = null, inherited: Boolean = false) = fun VisualObject.int(default: Int? = null, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.int } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.int }
fun VisualObject.node(key: String? = null, inherited: Boolean = true) = fun VisualObject.node(key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.asName(), null, inherited) { it.node } VisualObjectDelegateWrapper(key?.asName(), null, inherited) { it.node }
fun VisualObject.item(key: String? = null, inherited: Boolean = true) = fun VisualObject.item(key: String? = null, inherited: Boolean = true) =
DisplayObjectDelegateWrapper(key?.asName(), null, inherited) { it } VisualObjectDelegateWrapper(key?.asName(), null, inherited) { it }
//fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) } //fun <T : Configurable> Configurable.spec(spec: Specification<T>, key: String? = null) = ChildConfigDelegate<T>(key) { spec.wrap(this) }
@JvmName("safeString") @JvmName("safeString")
fun VisualObject.string(default: String, key: String? = null, inherited: Boolean = false) = fun VisualObject.string(default: String, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.string } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.string }
@JvmName("safeBoolean") @JvmName("safeBoolean")
fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) = fun VisualObject.boolean(default: Boolean, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.boolean }
@JvmName("safeNumber") @JvmName("safeNumber")
fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) = fun VisualObject.number(default: Number, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.number } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.number }
@JvmName("safeDouble") @JvmName("safeDouble")
fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) = fun VisualObject.double(default: Double, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.double } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.double }
@JvmName("safeInt") @JvmName("safeInt")
fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) = fun VisualObject.int(default: Int, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.int } VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.int }
inline fun <reified E : Enum<E>> VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) = inline fun <reified E : Enum<E>> VisualObject.enum(default: E, key: String? = null, inherited: Boolean = false) =
DisplayObjectDelegateWrapper( VisualObjectDelegateWrapper(
key?.let { NameToken(it).asName() }, key?.let { NameToken(it).asName() },
default, default,
inherited inherited

View File

@ -1,8 +1,8 @@
package hep.dataforge.vis.spatial.gdml package hep.dataforge.vis.spatial.gdml
import hep.dataforge.names.EmptyName import hep.dataforge.names.EmptyName
import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.vis.common.asName
import hep.dataforge.vis.common.get import hep.dataforge.vis.common.get
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import scientifik.gdml.* import scientifik.gdml.*

View File

@ -5,6 +5,7 @@ package hep.dataforge.vis.spatial
import hep.dataforge.io.ConfigSerializer import hep.dataforge.io.ConfigSerializer
import hep.dataforge.io.NameSerializer import hep.dataforge.io.NameSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.names.Name import hep.dataforge.names.Name
@ -37,6 +38,11 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
get() = (parent as? VisualGroup3D)?.getTemplate(templateName) get() = (parent as? VisualGroup3D)?.getTemplate(templateName)
?: error("Template with name $templateName not found in $parent") ?: error("Template with name $templateName not found in $parent")
override fun getStyle(name: Name): Meta? = null
override fun setStyle(name: Name, meta: Meta) {
//do nothing
}
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {

View File

@ -1,21 +1,29 @@
@file:UseSerializers(Point3DSerializer::class, ConfigSerializer::class, NameTokenSerializer::class) @file:UseSerializers(
Point3DSerializer::class,
ConfigSerializer::class,
NameTokenSerializer::class,
NameSerializer::class
)
package hep.dataforge.vis.spatial package hep.dataforge.vis.spatial
import hep.dataforge.io.ConfigSerializer import hep.dataforge.io.ConfigSerializer
import hep.dataforge.io.NameSerializer
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.set import hep.dataforge.meta.set
import hep.dataforge.names.Name import hep.dataforge.names.*
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.names.isEmpty
import hep.dataforge.vis.common.AbstractVisualGroup import hep.dataforge.vis.common.AbstractVisualGroup
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.VisualGroup import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
@Serializable @Serializable
class VisualGroup3D : AbstractVisualGroup(), VisualObject3D { class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
@ -28,8 +36,34 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
field = value field = value
} }
//FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed
public override var properties: Config? = null public override var properties: Config? = null
private val styles = HashMap<Name, Meta>()
override fun getStyle(name: Name): Meta? = styles[name]
override fun setStyle(name: Name, meta: Meta) {
fun VisualObject.applyStyle(name: Name, meta: Meta) {
if (style.contains(name.toString())) {
//full update
//TODO do a fine grained update
if(this is AbstractVisualObject){
styleChanged()
} else {
propertyChanged(EmptyName)
}
}
if (this is VisualGroup) {
this.children.forEach { (_, child) ->
child.applyStyle(name, meta)
}
}
}
styles[name] = meta
applyStyle(name, meta)
}
override var position: Point3D? = null override var position: Point3D? = null
override var rotation: Point3D? = null override var rotation: Point3D? = null
override var scale: Point3D? = null override var scale: Point3D? = null
@ -96,7 +130,7 @@ fun VisualGroup.attachChildren() {
it.parent = this it.parent = this
(it as? VisualGroup)?.attachChildren() (it as? VisualGroup)?.attachChildren()
} }
if(this is VisualGroup3D){ if (this is VisualGroup3D) {
templates?.apply { templates?.apply {
parent = this@attachChildren parent = this@attachChildren
attachChildren() attachChildren()

View File

@ -4,11 +4,11 @@ package hep.dataforge.vis.spatial
import hep.dataforge.io.NameSerializer import hep.dataforge.io.NameSerializer
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.output.Output import hep.dataforge.output.Output
import hep.dataforge.vis.common.Colors.rgbToString import hep.dataforge.vis.common.Colors.rgbToString
import hep.dataforge.vis.common.VisualObject import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.asName
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.MATERIAL_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.MATERIAL_KEY

View File

@ -2,10 +2,10 @@ package hep.dataforge.vis.spatial.three
import hep.dataforge.meta.boolean import hep.dataforge.meta.boolean
import hep.dataforge.meta.node import hep.dataforge.meta.node
import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.asName
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
import info.laht.threekt.core.BufferGeometry import info.laht.threekt.core.BufferGeometry