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
// init {
// //Do after deserialization
// children.values.forEach {

View File

@ -1,8 +1,10 @@
package hep.dataforge.vis.common
import hep.dataforge.meta.*
import hep.dataforge.names.EmptyName
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
internal data class PropertyListener(
@ -15,17 +17,21 @@ abstract class AbstractVisualObject : VisualObject {
@Transient
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) {
//notify about style removed
style?.items?.forEach {(name, value) ->
propertyChanged(name.asName(), value, null)
}
field = value
//notify about style adition
value?.items?.forEach { (name, value) ->
propertyChanged(name.asName(), null, value)
setProperty(VisualObject.STYLE_KEY, value)
}
/**
* The config is initialized and assigned on-demand.
* To avoid unnecessary allocations, one should access [properties] via [getProperty] instead.
*/
override val config: Config
get() = properties ?: Config().also { config ->
properties = config.apply { onChange(this, ::propertyChanged) }
}
@Transient
@ -45,22 +51,32 @@ abstract class AbstractVisualObject : VisualObject {
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?) {
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<*>? {
return if (inherit) {
style?.get(name) ?: properties?.get(name) ?: parent?.getProperty(name, inherit)
properties?.get(name) ?: parent?.getProperty(name, inherit) ?: styles()[name]
} else {
style?.get(name) ?: properties?.get(name)
properties?.get(name) ?: styles()[name]
}
}
@ -72,3 +88,11 @@ abstract class AbstractVisualObject : VisualObject {
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
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
*/
@ -177,6 +179,6 @@ object Colors {
fun rgbToString(rgb: Int): String {
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
import hep.dataforge.meta.Meta
import hep.dataforge.names.*
import hep.dataforge.provider.Provider
@ -11,17 +12,18 @@ interface VisualGroup : VisualObject, Provider, Iterable<VisualObject> {
override val defaultTarget: String get() = VisualObject.TYPE
override fun provideTop(target: String): Map<Name, VisualObject> = if (target == VisualObject.TYPE) {
children.flatMap { (key, value) ->
val res: Map<Name, VisualObject> = if (value is VisualGroup) {
override fun provideTop(target: String): Map<Name, Any> =
when (target) {
VisualObject.TYPE -> children.flatMap { (key, value) ->
val res: Map<Name, Any> = if (value is VisualGroup) {
value.provideTop(target).mapKeys { key + it.key }
} else {
mapOf(key.asName() to value)
}
res.entries
}.associate { it.toPair() }
} else {
emptyMap()
//TODO add styles
else -> emptyMap()
}
/**
@ -30,7 +32,20 @@ interface VisualGroup : VisualObject, Provider, Iterable<VisualObject> {
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)

View File

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

View File

@ -1,7 +1,6 @@
package hep.dataforge.vis.common
import hep.dataforge.meta.*
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
@ -11,12 +10,11 @@ import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
fun String.asName() = if (isBlank()) EmptyName else NameToken(this).asName()
/**
* A delegate for display object properties
*/
class DisplayObjectDelegate(
class VisualObjectDelegate(
val key: Name?,
val default: MetaItem<*>?,
val inherited: Boolean
@ -36,7 +34,7 @@ class DisplayObjectDelegate(
}
}
class DisplayObjectDelegateWrapper<T>(
class VisualObjectDelegateWrapper<T>(
val key: Name?,
val default: T,
val inherited: Boolean,
@ -63,55 +61,55 @@ class DisplayObjectDelegateWrapper<T>(
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) =
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) =
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) =
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) =
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) =
DisplayObjectDelegateWrapper(key?.asName(), default, inherited) { it.int }
VisualObjectDelegateWrapper(key?.asName(), default, inherited) { it.int }
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) =
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) }
@JvmName("safeString")
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")
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")
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")
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")
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) =
DisplayObjectDelegateWrapper(
VisualObjectDelegateWrapper(
key?.let { NameToken(it).asName() },
default,
inherited

View File

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

View File

@ -5,6 +5,7 @@ package hep.dataforge.vis.spatial
import hep.dataforge.io.ConfigSerializer
import hep.dataforge.io.NameSerializer
import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem
import hep.dataforge.names.Name
@ -37,6 +38,11 @@ class Proxy(val templateName: Name) : AbstractVisualObject(), VisualGroup, Visua
get() = (parent as? VisualGroup3D)?.getTemplate(templateName)
?: 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<*>? {
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
import hep.dataforge.io.ConfigSerializer
import hep.dataforge.io.NameSerializer
import hep.dataforge.meta.Config
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.set
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.asName
import hep.dataforge.names.isEmpty
import hep.dataforge.names.*
import hep.dataforge.vis.common.AbstractVisualGroup
import hep.dataforge.vis.common.AbstractVisualObject
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
@Serializable
class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
@ -28,8 +36,34 @@ class VisualGroup3D : AbstractVisualGroup(), VisualObject3D {
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
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 rotation: Point3D? = null
override var scale: Point3D? = null
@ -96,7 +130,7 @@ fun VisualGroup.attachChildren() {
it.parent = this
(it as? VisualGroup)?.attachChildren()
}
if(this is VisualGroup3D){
if (this is VisualGroup3D) {
templates?.apply {
parent = this@attachChildren
attachChildren()

View File

@ -4,11 +4,11 @@ package hep.dataforge.vis.spatial
import hep.dataforge.io.NameSerializer
import hep.dataforge.meta.*
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.output.Output
import hep.dataforge.vis.common.Colors.rgbToString
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.LAYER_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.node
import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.names.startsWith
import hep.dataforge.provider.Type
import hep.dataforge.vis.common.asName
import hep.dataforge.vis.spatial.*
import hep.dataforge.vis.spatial.three.ThreeFactory.Companion.TYPE
import info.laht.threekt.core.BufferGeometry