forked from kscience/visionforge
Working on configuration editor
This commit is contained in:
parent
7aaca8c34f
commit
eba5096129
@ -1,4 +1,4 @@
|
|||||||
val dataforgeVersion by extra("0.1.3-dev-1")
|
val dataforgeVersion by extra("0.1.3-dev-2")
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -9,6 +9,17 @@ kotlin {
|
|||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
api("hep.dataforge:dataforge-output:$dataforgeVersion")
|
api("hep.dataforge:dataforge-output:$dataforgeVersion")
|
||||||
|
api("hep.dataforge:dataforge-output-metadata:$dataforgeVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jvmMain by getting{
|
||||||
|
dependencies {
|
||||||
|
api("hep.dataforge:dataforge-output-jvm:$dataforgeVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jsMain by getting{
|
||||||
|
dependencies {
|
||||||
|
api("hep.dataforge:dataforge-output-js:$dataforgeVersion")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
package hep.dataforge.vis
|
||||||
|
|
||||||
|
import hep.dataforge.meta.EmptyMeta
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.Styled
|
||||||
|
|
||||||
|
internal data class InvalidationListener(
|
||||||
|
val owner: Any?,
|
||||||
|
val action: () -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [DisplayGroup] containing ordered list of elements
|
||||||
|
*/
|
||||||
|
class DisplayObjectList(
|
||||||
|
override val parent: DisplayObject? = null,
|
||||||
|
// override val type: String = DisplayObject.DEFAULT_TYPE,
|
||||||
|
meta: Meta = EmptyMeta
|
||||||
|
) : DisplayGroup {
|
||||||
|
private val _children = ArrayList<DisplayObject>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ordered list of direct descendants
|
||||||
|
*/
|
||||||
|
val children: List<DisplayObject> get() = _children
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<DisplayObject> = children.iterator()
|
||||||
|
|
||||||
|
|
||||||
|
override val properties = Styled(meta)
|
||||||
|
private val listeners = HashSet<InvalidationListener>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a child object and notify listeners
|
||||||
|
*/
|
||||||
|
fun addChild(obj: DisplayObject) {
|
||||||
|
_children.add(obj)
|
||||||
|
listeners.forEach { it.action() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a specific child and notify listeners
|
||||||
|
*/
|
||||||
|
fun removeChild(obj: DisplayObject) {
|
||||||
|
if (_children.remove(obj)) {
|
||||||
|
listeners.forEach { it.action }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add listener for children change
|
||||||
|
* TODO add detailed information into change listener
|
||||||
|
*/
|
||||||
|
fun onChildrenChange(owner: Any?, action: () -> Unit) {
|
||||||
|
listeners.add(InvalidationListener(owner, action))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove children change listener
|
||||||
|
*/
|
||||||
|
fun removeChildrenChangeListener(owner: Any?) {
|
||||||
|
listeners.removeAll { it.owner === owner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@ package hep.dataforge.vis
|
|||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE
|
|
||||||
import hep.dataforge.vis.DisplayObject.Companion.META_KEY
|
import hep.dataforge.vis.DisplayObject.Companion.META_KEY
|
||||||
import hep.dataforge.vis.DisplayObject.Companion.TAGS_KEY
|
import hep.dataforge.vis.DisplayObject.Companion.TAGS_KEY
|
||||||
|
|
||||||
@ -17,60 +16,34 @@ interface DisplayObject {
|
|||||||
*/
|
*/
|
||||||
val parent: DisplayObject?
|
val parent: DisplayObject?
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* The type of this object. Uses `.` notation. Empty type means untyped group
|
// * The type of this object. Uses `.` notation. Empty type means untyped group
|
||||||
*/
|
// */
|
||||||
val type: String
|
// val type: String
|
||||||
|
|
||||||
val properties: Styled
|
val properties: MutableMeta<*>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DEFAULT_TYPE = ""
|
const val DEFAULT_TYPE = ""
|
||||||
const val TYPE_KEY = "@type"
|
//const val TYPE_KEY = "@type"
|
||||||
const val CHILDREN_KEY = "@children"
|
//const val CHILDREN_KEY = "@children"
|
||||||
const val META_KEY = "@meta"
|
const val META_KEY = "@meta"
|
||||||
const val TAGS_KEY = "@tags"
|
const val TAGS_KEY = "@tags"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DisplayGroup : DisplayObject {
|
|
||||||
|
|
||||||
val children: List<DisplayObject>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a child object and notify listeners
|
|
||||||
*/
|
|
||||||
fun addChild(obj: DisplayObject)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a specific child and notify listeners
|
|
||||||
*/
|
|
||||||
fun removeChild(obj: DisplayObject)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add listener for children change
|
|
||||||
* TODO add detailed information into change listener
|
|
||||||
*/
|
|
||||||
fun onChildrenChange(owner: Any? = null, action: () -> Unit)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove children change listener
|
|
||||||
*/
|
|
||||||
fun removeChildrenChangeListener(owner: Any? = null)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the property of this display object of parent's if not found
|
* Get the property of this display object of parent's if not found
|
||||||
*/
|
*/
|
||||||
tailrec operator fun DisplayObject.get(name: Name): MetaItem<*>? = properties[name] ?: parent?.get(name)
|
tailrec fun DisplayObject.getProperty(name: Name): MetaItem<*>? = properties[name] ?: parent?.getProperty(name)
|
||||||
|
|
||||||
operator fun DisplayObject.get(name: String) = get(name.toName())
|
fun DisplayObject.getProperty(name: String): MetaItem<*>? = getProperty(name.toName())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A change listener for [DisplayObject] configuration.
|
* A change listener for [DisplayObject] configuration.
|
||||||
*/
|
*/
|
||||||
fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) {
|
fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, after: MetaItem<*>?) -> Unit) {
|
||||||
properties.style.onChange(owner, action)
|
properties.onChange(owner, action)
|
||||||
parent?.onChange(owner, action)
|
parent?.onChange(owner, action)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +51,7 @@ fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, aft
|
|||||||
* Remove all meta listeners with matching owners
|
* Remove all meta listeners with matching owners
|
||||||
*/
|
*/
|
||||||
fun DisplayObject.removeChangeListener(owner: Any?) {
|
fun DisplayObject.removeChangeListener(owner: Any?) {
|
||||||
properties.style.removeListener(owner)
|
properties.removeListener(owner)
|
||||||
parent?.removeChangeListener(owner)
|
parent?.removeChangeListener(owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,61 +63,15 @@ val DisplayObject.meta: Meta get() = properties[META_KEY]?.node ?: EmptyMeta
|
|||||||
|
|
||||||
val DisplayObject.tags: List<String> get() = properties[TAGS_KEY].stringList
|
val DisplayObject.tags: List<String> get() = properties[TAGS_KEY].stringList
|
||||||
|
|
||||||
internal data class ObjectListener(
|
///**
|
||||||
val owner: Any?,
|
// * Basic [DisplayObject] leaf element
|
||||||
val action: () -> Unit
|
// */
|
||||||
)
|
//open class DisplayLeaf(
|
||||||
|
// override val parent: DisplayObject?,
|
||||||
/**
|
//// override val type: String,
|
||||||
* Basic group of display objects
|
// meta: Meta = EmptyMeta
|
||||||
*/
|
//) : DisplayObject {
|
||||||
open class DisplayNode(
|
// final override val properties = Styled(meta)
|
||||||
override val parent: DisplayObject? = null,
|
//}
|
||||||
override val type: String = DEFAULT_TYPE,
|
|
||||||
meta: Meta = EmptyMeta
|
|
||||||
) : DisplayGroup {
|
|
||||||
|
|
||||||
private val _children = ArrayList<DisplayObject>()
|
|
||||||
override val children: List<DisplayObject> get() = _children
|
|
||||||
override val properties = Styled(meta)
|
|
||||||
private val listeners = HashSet<ObjectListener>()
|
|
||||||
|
|
||||||
override fun addChild(obj: DisplayObject) {
|
|
||||||
// val before = _children[name]
|
|
||||||
// if (obj == null) {
|
|
||||||
// _children.remove(name)
|
|
||||||
// } else {
|
|
||||||
// _children[name] = obj
|
|
||||||
// }
|
|
||||||
// listeners.forEach { it.action(name, before, obj) }
|
|
||||||
_children.add(obj)
|
|
||||||
listeners.forEach { it.action() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeChild(obj: DisplayObject) {
|
|
||||||
if (_children.remove(obj)) {
|
|
||||||
listeners.forEach { it.action }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onChildrenChange(owner: Any?, action: () -> Unit) {
|
|
||||||
listeners.add(ObjectListener(owner, action))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun removeChildrenChangeListener(owner: Any?) {
|
|
||||||
listeners.removeAll { it.owner === owner }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic [DisplayObject] leaf element
|
|
||||||
*/
|
|
||||||
open class DisplayLeaf(
|
|
||||||
override val parent: DisplayObject?,
|
|
||||||
override val type: String,
|
|
||||||
meta: Meta = EmptyMeta
|
|
||||||
) : DisplayObject {
|
|
||||||
final override val properties = Styled(meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
interface DisplayGroup: DisplayObject, Iterable<DisplayObject>
|
||||||
|
@ -20,7 +20,7 @@ class DisplayObjectDelegate(
|
|||||||
override fun getValue(thisRef: DisplayObject, property: KProperty<*>): MetaItem<*>? {
|
override fun getValue(thisRef: DisplayObject, property: KProperty<*>): MetaItem<*>? {
|
||||||
val name = key ?: property.name.toName()
|
val name = key ?: property.name.toName()
|
||||||
return if (inherited) {
|
return if (inherited) {
|
||||||
thisRef[name]
|
thisRef.getProperty(name)
|
||||||
} else {
|
} else {
|
||||||
thisRef.properties[name]
|
thisRef.properties[name]
|
||||||
} ?: default
|
} ?: default
|
||||||
@ -28,7 +28,7 @@ class DisplayObjectDelegate(
|
|||||||
|
|
||||||
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: MetaItem<*>?) {
|
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: MetaItem<*>?) {
|
||||||
val name = key ?: property.name.toName()
|
val name = key ?: property.name.toName()
|
||||||
thisRef.properties.style[name] = value
|
thisRef.properties[name] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class DisplayObjectDelegateWrapper<T>(
|
|||||||
override fun getValue(thisRef: DisplayObject, property: KProperty<*>): T {
|
override fun getValue(thisRef: DisplayObject, property: KProperty<*>): T {
|
||||||
val name = key ?: property.name.toName()
|
val name = key ?: property.name.toName()
|
||||||
return if (inherited) {
|
return if (inherited) {
|
||||||
read(thisRef[name])
|
read(thisRef.getProperty(name))
|
||||||
} else {
|
} else {
|
||||||
read(thisRef.properties[name])
|
read(thisRef.properties[name])
|
||||||
} ?: default
|
} ?: default
|
||||||
@ -50,7 +50,7 @@ class DisplayObjectDelegateWrapper<T>(
|
|||||||
|
|
||||||
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: T) {
|
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: T) {
|
||||||
val name = key ?: property.name.toName()
|
val name = key ?: property.name.toName()
|
||||||
thisRef.properties.style.write(name, value)
|
thisRef.properties[name] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package hep.dataforge.vis
|
||||||
|
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A navigable hierarchical display node
|
||||||
|
*/
|
||||||
|
interface DisplayTree : DisplayGroup {
|
||||||
|
operator fun get(nameToken: NameToken): DisplayObject?
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MutableDisplayTree : DisplayTree {
|
||||||
|
operator fun set(nameToken: NameToken, group: DisplayObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively get a child
|
||||||
|
*/
|
||||||
|
tailrec operator fun DisplayTree.get(name: Name): DisplayObject? = when (name.length) {
|
||||||
|
0 -> this
|
||||||
|
1 -> this[name[0]]
|
||||||
|
else -> name.first()?.let { this[it] as? DisplayTree }?.get(name.cutFirst())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set given object creating intermediate empty groups if needed
|
||||||
|
* @param name - the full name of a child
|
||||||
|
* @param objFactory - a function that creates child object from parent (to avoid mutable parent parameter)
|
||||||
|
*/
|
||||||
|
fun MutableDisplayTree.set(name: Name, objFactory: (parent: DisplayObject) -> DisplayObject): Unit =
|
||||||
|
when (name.length) {
|
||||||
|
0 -> error("Can't set object with empty name")
|
||||||
|
1 -> set(name[0], objFactory(this))
|
||||||
|
else -> (this[name.first()!!] ?: DisplayObjectList(this)).run {
|
||||||
|
if (this is MutableDisplayTree) {
|
||||||
|
this.set(name.cutFirst(), objFactory)
|
||||||
|
} else {
|
||||||
|
error("Can't assign child to a leaf element $this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +0,0 @@
|
|||||||
package hep.dataforge.vis
|
|
||||||
|
|
||||||
import hep.dataforge.names.Name
|
|
||||||
import hep.dataforge.names.NameToken
|
|
||||||
|
|
||||||
interface NamedObject : DisplayObject {
|
|
||||||
val name: String
|
|
||||||
|
|
||||||
operator fun get(nameToken: NameToken): DisplayGroup?
|
|
||||||
|
|
||||||
operator fun set(nameToken: NameToken, group: DisplayGroup)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively get a child
|
|
||||||
*/
|
|
||||||
tailrec operator fun NamedObject.get(name: Name): DisplayObject? = when (name.length) {
|
|
||||||
0 -> this
|
|
||||||
1 -> this[name[0]]
|
|
||||||
else -> name.first()?.let { this[it] as? NamedObject }?.get(name.cutFirst())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set given object creating intermediate empty groups if needed
|
|
||||||
* @param name - the full name of a child
|
|
||||||
* @param objFactory - a function that creates child object from parent (to avoid mutable parent parameter)
|
|
||||||
*/
|
|
||||||
fun NamedObject.set(name: Name, objFactory: (parent: DisplayObject) -> DisplayGroup): Unit = when (name.length) {
|
|
||||||
0 -> error("Can't set object with empty name")
|
|
||||||
1 -> set(name[0], objFactory(this))
|
|
||||||
else -> (this[name.first()!!] ?: DisplayNode(this))
|
|
||||||
.run {
|
|
||||||
if (this is NamedObject) {
|
|
||||||
this.set(name.cutFirst(), objFactory)
|
|
||||||
} else {
|
|
||||||
error("Can't assign child to a leaf element $this")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
23
dataforge-vis-fx/build.gradle.kts
Normal file
23
dataforge-vis-fx/build.gradle.kts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
import org.openjfx.gradle.JavaFXOptions
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
id("org.openjfx.javafxplugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(":dataforge-vis-common"))
|
||||||
|
api("no.tornado:tornadofx:1.7.18")
|
||||||
|
api("no.tornado:tornadofx-controlsfx:0.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
configure<JavaFXOptions> {
|
||||||
|
modules("javafx.controls")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<KotlinCompile> {
|
||||||
|
kotlinOptions{
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
package hep.dataforge.vis.fx
|
||||||
|
|
||||||
|
import hep.dataforge.context.*
|
||||||
|
import hep.dataforge.meta.EmptyMeta
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.boolean
|
||||||
|
import javafx.application.Application
|
||||||
|
import javafx.application.Platform
|
||||||
|
import javafx.collections.FXCollections
|
||||||
|
import javafx.collections.ObservableSet
|
||||||
|
import javafx.collections.SetChangeListener
|
||||||
|
import javafx.scene.Scene
|
||||||
|
import javafx.scene.image.Image
|
||||||
|
import javafx.scene.image.ImageView
|
||||||
|
import javafx.stage.Stage
|
||||||
|
import tornadofx.*
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin holding JavaFX application instance and its root stage
|
||||||
|
*/
|
||||||
|
class FXPlugin(meta: Meta = EmptyMeta) : AbstractPlugin(meta) {
|
||||||
|
override val tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
|
private val stages: ObservableSet<Stage> = FXCollections.observableSet()
|
||||||
|
|
||||||
|
val consoleMode: Boolean by meta.boolean(true)
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (consoleMode) {
|
||||||
|
stages.addListener(SetChangeListener { change ->
|
||||||
|
if (change.set.isEmpty()) {
|
||||||
|
Platform.setImplicitExit(true)
|
||||||
|
} else {
|
||||||
|
Platform.setImplicitExit(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for application and toolkit to start if needed
|
||||||
|
*/
|
||||||
|
override fun attach(context: Context) {
|
||||||
|
super.attach(context)
|
||||||
|
if (FX.getApplication(FX.defaultScope) == null) {
|
||||||
|
if (consoleMode) {
|
||||||
|
Thread {
|
||||||
|
context.logger.debug("Starting FX application surrogate")
|
||||||
|
launch<ApplicationSurrogate>()
|
||||||
|
}.apply {
|
||||||
|
name = "${context.name} FX application thread"
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!FX.initialized.get()) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw RuntimeException("Interrupted application start")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Platform.setImplicitExit(false)
|
||||||
|
} else {
|
||||||
|
throw RuntimeException("FX Application not defined")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define an application to use in this context
|
||||||
|
*/
|
||||||
|
fun setApp(app: Application, stage: Stage) {
|
||||||
|
FX.registerApplication(FX.defaultScope, app, stage)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show something in a pre-constructed stage. Blocks thread until stage is created
|
||||||
|
*
|
||||||
|
* @param cons
|
||||||
|
*/
|
||||||
|
fun display(action: Stage.() -> Unit) {
|
||||||
|
runLater {
|
||||||
|
val stage = Stage()
|
||||||
|
stage.initOwner(FX.primaryStage)
|
||||||
|
stage.action()
|
||||||
|
stage.show()
|
||||||
|
stages.add(stage)
|
||||||
|
stage.setOnCloseRequest { stages.remove(stage) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun display(component: UIComponent, width: Double = 800.0, height: Double = 600.0) {
|
||||||
|
display {
|
||||||
|
scene = Scene(component.root, width, height)
|
||||||
|
title = component.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : PluginFactory<FXPlugin> {
|
||||||
|
override val type: KClass<out FXPlugin> = FXPlugin::class
|
||||||
|
override val tag: PluginTag = PluginTag("vis.fx", group = PluginTag.DATAFORGE_GROUP)
|
||||||
|
override fun invoke(meta: Meta): FXPlugin = FXPlugin(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val dfIcon: Image = Image(Global::class.java.getResourceAsStream("/img/df.png"))
|
||||||
|
val dfIconView = ImageView(dfIcon)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An application surrogate without any visible primary stage
|
||||||
|
*/
|
||||||
|
class ApplicationSurrogate : App() {
|
||||||
|
override fun start(stage: Stage) {
|
||||||
|
FX.registerApplication(this, stage)
|
||||||
|
FX.initialized.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.display(width: Double = 800.0, height: Double = 600.0, component: () -> UIComponent) {
|
||||||
|
plugins.getOrLoad<FXPlugin>().display(component(), width, height)
|
||||||
|
}
|
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package hep.dataforge.vis.fx.meta
|
||||||
|
|
||||||
|
import hep.dataforge.descriptors.NodeDescriptor
|
||||||
|
import hep.dataforge.meta.Config
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
import hep.dataforge.vis.fx.dfIconView
|
||||||
|
import hep.dataforge.vis.fx.values.ValueChooser
|
||||||
|
import javafx.scene.control.*
|
||||||
|
import javafx.scene.control.cell.TextFieldTreeTableCell
|
||||||
|
import javafx.scene.layout.Priority
|
||||||
|
import javafx.scene.paint.Color
|
||||||
|
import javafx.scene.text.Text
|
||||||
|
import org.controlsfx.glyphfont.Glyph
|
||||||
|
import tornadofx.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FXML Controller class
|
||||||
|
*
|
||||||
|
* @author Alexander Nozik
|
||||||
|
*/
|
||||||
|
class ConfigEditor(
|
||||||
|
val configuration: Config,
|
||||||
|
title: String = "Configuration editor",
|
||||||
|
val descriptor: NodeDescriptor? = null
|
||||||
|
) : Fragment(title = title, icon = dfIconView) {
|
||||||
|
|
||||||
|
val filter: (FXMeta) -> Boolean = { cfg ->
|
||||||
|
when (cfg) {
|
||||||
|
is FXMetaNode<*> -> !(cfg.descriptor?.tags?.contains(NO_CONFIGURATOR_TAG) ?: false)
|
||||||
|
is FXMetaValue -> !(cfg.descriptor?.tags?.contains(NO_CONFIGURATOR_TAG) ?: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val root = borderpane {
|
||||||
|
center = treetableview<FXMeta> {
|
||||||
|
root = TreeItem(FXMeta.root(configuration, descriptor))
|
||||||
|
root.isExpanded = true
|
||||||
|
sortMode = TreeSortMode.ALL_DESCENDANTS
|
||||||
|
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
|
||||||
|
column("Name", FXMeta::name) {
|
||||||
|
setCellFactory {
|
||||||
|
object : TextFieldTreeTableCell<FXMeta, NameToken>() {
|
||||||
|
override fun updateItem(item: NameToken?, empty: Boolean) {
|
||||||
|
super.updateItem(item, empty)
|
||||||
|
contextMenu?.items?.removeIf { it.text == "Remove" }
|
||||||
|
if (!empty) {
|
||||||
|
if (treeTableRow.item != null) {
|
||||||
|
textFillProperty().bind(treeTableRow.item.hasValue.objectBinding {
|
||||||
|
if (it == true) {
|
||||||
|
Color.BLACK
|
||||||
|
} else {
|
||||||
|
Color.GRAY
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (treeTableRow.treeItem.value.hasValue.get()) {
|
||||||
|
contextmenu {
|
||||||
|
item("Remove") {
|
||||||
|
action {
|
||||||
|
treeTableRow.item.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
column("Value") { param: TreeTableColumn.CellDataFeatures<FXMeta, FXMeta> ->
|
||||||
|
param.value.valueProperty()
|
||||||
|
}.setCellFactory {
|
||||||
|
ValueCell()
|
||||||
|
}
|
||||||
|
|
||||||
|
column("Description") { param: TreeTableColumn.CellDataFeatures<FXMeta, String> -> param.value.value.descriptionProperty }
|
||||||
|
.setCellFactory { param: TreeTableColumn<FXMeta, String> ->
|
||||||
|
val cell = TreeTableCell<FXMeta, String>()
|
||||||
|
val text = Text()
|
||||||
|
cell.graphic = text
|
||||||
|
cell.prefHeight = Control.USE_COMPUTED_SIZE
|
||||||
|
text.wrappingWidthProperty().bind(param.widthProperty())
|
||||||
|
text.textProperty().bind(cell.itemProperty())
|
||||||
|
cell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showNodeDialog(): String? {
|
||||||
|
val dialog = TextInputDialog()
|
||||||
|
dialog.title = "Node name selection"
|
||||||
|
dialog.contentText = "Enter a name for new node: "
|
||||||
|
dialog.headerText = null
|
||||||
|
|
||||||
|
val result = dialog.showAndWait()
|
||||||
|
return result.orElse(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showValueDialog(): String? {
|
||||||
|
val dialog = TextInputDialog()
|
||||||
|
dialog.title = "Value name selection"
|
||||||
|
dialog.contentText = "Enter a name for new value: "
|
||||||
|
dialog.headerText = null
|
||||||
|
|
||||||
|
val result = dialog.showAndWait()
|
||||||
|
return result.orElse(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ValueCell : TreeTableCell<FXMeta, FXMeta?>() {
|
||||||
|
|
||||||
|
public override fun updateItem(item: FXMeta?, empty: Boolean) {
|
||||||
|
if (!empty) {
|
||||||
|
if (item != null) {
|
||||||
|
when (item) {
|
||||||
|
is FXMetaValue -> {
|
||||||
|
text = null
|
||||||
|
val chooser = ValueChooser.build(item.valueProperty, item.descriptor) {
|
||||||
|
item.set(it)
|
||||||
|
}
|
||||||
|
graphic = chooser.node
|
||||||
|
}
|
||||||
|
is FXMetaNode<*> -> {
|
||||||
|
item as FXMetaNode<Config>
|
||||||
|
|
||||||
|
text = null
|
||||||
|
graphic = hbox {
|
||||||
|
button("node", Glyph("FontAwesome", "PLUS_CIRCLE")) {
|
||||||
|
hgrow = Priority.ALWAYS
|
||||||
|
maxWidth = Double.POSITIVE_INFINITY
|
||||||
|
action {
|
||||||
|
showNodeDialog()?.let {
|
||||||
|
item.addNode(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button("value", Glyph("FontAwesome", "PLUS_SQUARE")) {
|
||||||
|
hgrow = Priority.ALWAYS
|
||||||
|
maxWidth = Double.POSITIVE_INFINITY
|
||||||
|
action {
|
||||||
|
showValueDialog()?.let {
|
||||||
|
item.addValue(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
text = null
|
||||||
|
graphic = null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text = null
|
||||||
|
graphic = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* The tag not to display node or value in configurator
|
||||||
|
*/
|
||||||
|
const val NO_CONFIGURATOR_TAG = "nocfg"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,286 @@
|
|||||||
|
//package hep.dataforge.vis.fx.meta
|
||||||
|
//
|
||||||
|
//import hep.dataforge.descriptors.NodeDescriptor
|
||||||
|
//import hep.dataforge.descriptors.ValueDescriptor
|
||||||
|
//import hep.dataforge.meta.Config
|
||||||
|
//import hep.dataforge.meta.Meta
|
||||||
|
//import hep.dataforge.names.Name
|
||||||
|
//import hep.dataforge.values.Null
|
||||||
|
//import hep.dataforge.values.Value
|
||||||
|
//import javafx.beans.binding.StringBinding
|
||||||
|
//import javafx.beans.property.SimpleObjectProperty
|
||||||
|
//import javafx.beans.property.SimpleStringProperty
|
||||||
|
//import javafx.beans.value.ObservableBooleanValue
|
||||||
|
//import javafx.beans.value.ObservableStringValue
|
||||||
|
//import javafx.collections.FXCollections
|
||||||
|
//import javafx.collections.ObservableList
|
||||||
|
//import javafx.scene.control.TreeItem
|
||||||
|
//import tornadofx.*
|
||||||
|
//
|
||||||
|
//class ConfigTreeItem(configFX: ConfigFX) : TreeItem<ConfigFX>(configFX) {
|
||||||
|
// init {
|
||||||
|
// this.children.bind(value.children) { ConfigTreeItem(it) }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun isLeaf(): Boolean = value is ConfigFXValue
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * A node, containing relative representation of configuration node and description
|
||||||
|
// * Created by darksnake on 01-May-17.
|
||||||
|
// */
|
||||||
|
//sealed class ConfigFX(name: String) {
|
||||||
|
//
|
||||||
|
// val nameProperty = SimpleStringProperty(name)
|
||||||
|
// val name by nameProperty
|
||||||
|
//
|
||||||
|
// val parentProperty = SimpleObjectProperty<ConfigFXNode>()
|
||||||
|
// val parent by parentProperty
|
||||||
|
//
|
||||||
|
// abstract val hasValueProperty: ObservableBooleanValue
|
||||||
|
// //abstract val hasDefaultProperty: ObservableBooleanValue
|
||||||
|
//
|
||||||
|
// abstract val descriptionProperty: ObservableStringValue
|
||||||
|
//
|
||||||
|
// abstract val children: ObservableList<ConfigFX>
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * remove itself from parent
|
||||||
|
// */
|
||||||
|
// abstract fun remove()
|
||||||
|
//
|
||||||
|
// abstract fun invalidate()
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * Tree item for node
|
||||||
|
// * Created by darksnake on 30-Apr-17.
|
||||||
|
// */
|
||||||
|
//open class ConfigFXNode(name: String, parent: ConfigFXNode? = null) : ConfigFX(name) {
|
||||||
|
//
|
||||||
|
// final override val hasValueProperty = parentProperty.booleanBinding(nameProperty) {
|
||||||
|
// it?.config?.hasMeta(this.name) ?: false
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * A descriptor that could be manually set to the node
|
||||||
|
// */
|
||||||
|
// val descriptorProperty = SimpleObjectProperty<NodeDescriptor?>()
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Actual descriptor which holds value inferred from parrent
|
||||||
|
// */
|
||||||
|
// private val actualDescriptor = objectBinding(descriptorProperty, parentProperty, nameProperty) {
|
||||||
|
// value ?: parent?.descriptor?.getNodeDescriptor(name)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// val descriptor: NodeDescriptor? by actualDescriptor
|
||||||
|
//
|
||||||
|
// val configProperty = SimpleObjectProperty<Config?>()
|
||||||
|
//
|
||||||
|
// private val actualConfig = objectBinding(configProperty, parentProperty, nameProperty) {
|
||||||
|
// value ?: parent?.config?.getMetaList(name)?.firstOrNull()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// val config: Config? by actualConfig
|
||||||
|
//
|
||||||
|
// final override val descriptionProperty: ObservableStringValue = stringBinding(actualDescriptor) {
|
||||||
|
// value?.info ?: ""
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override val children: ObservableList<ConfigFX> = FXCollections.observableArrayList<ConfigFX>()
|
||||||
|
//
|
||||||
|
// init {
|
||||||
|
// parentProperty.set(parent)
|
||||||
|
// hasValueProperty.onChange {
|
||||||
|
// parent?.hasValueProperty?.invalidate()
|
||||||
|
// }
|
||||||
|
// invalidate()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Get existing configuration node or create and attach new one
|
||||||
|
// *
|
||||||
|
// * @return
|
||||||
|
// */
|
||||||
|
// private fun getOrBuildNode(): Config {
|
||||||
|
// return config ?: if (parent == null) {
|
||||||
|
// throw RuntimeException("The configuration for root node is note defined")
|
||||||
|
// } else {
|
||||||
|
// parent.getOrBuildNode().requestNode(name)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fun addValue(name: String) {
|
||||||
|
// getOrBuildNode().setValue(name, Null)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fun setValue(name: String, value: Value) {
|
||||||
|
// getOrBuildNode().setValue(name, value)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fun removeValue(valueName: String) {
|
||||||
|
// config?.removeValue(valueName)
|
||||||
|
// children.removeIf { it.name == name }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fun addNode(name: String) {
|
||||||
|
// getOrBuildNode().requestNode(name)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fun removeNode(name: String) {
|
||||||
|
// config?.removeNode(name)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun remove() {
|
||||||
|
// //FIXME does not work on multinodes
|
||||||
|
// parent?.removeNode(name)
|
||||||
|
// invalidate()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final override fun invalidate() {
|
||||||
|
// actualDescriptor.invalidate()
|
||||||
|
// actualConfig.invalidate()
|
||||||
|
// hasValueProperty.invalidate()
|
||||||
|
//
|
||||||
|
// val nodeNames = ArrayList<String>()
|
||||||
|
// val valueNames = ArrayList<String>()
|
||||||
|
//
|
||||||
|
// config?.apply {
|
||||||
|
// nodeNames.addAll(this.nodeNames.toList())
|
||||||
|
// valueNames.addAll(this.valueNames.toList())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// descriptor?.apply {
|
||||||
|
// nodeNames.addAll(childrenDescriptors().keys)
|
||||||
|
// valueNames.addAll(valueDescriptors().keys)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //removing old values
|
||||||
|
// children.removeIf { !(valueNames.contains(it.name) || nodeNames.contains(it.name)) }
|
||||||
|
//
|
||||||
|
// valueNames.forEach { name ->
|
||||||
|
// children.find { it.name == name }?.invalidate().orElse {
|
||||||
|
// children.add(ConfigFXValue(name, this))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// nodeNames.forEach { name ->
|
||||||
|
// children.find { it.name == name }?.invalidate().orElse {
|
||||||
|
// children.add(ConfigFXNode(name, this))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// children.sortBy { it.name }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fun updateValue(path: Name, value: Value?) {
|
||||||
|
// when {
|
||||||
|
// path.length == 0 -> kotlin.error("Path never could be empty when updating value")
|
||||||
|
// path.length == 1 -> {
|
||||||
|
// val hasDescriptor = descriptor?.getValueDescriptor(path) != null
|
||||||
|
// if (value == null && !hasDescriptor) {
|
||||||
|
// //removing the value if it is present
|
||||||
|
// children.removeIf { it.name == path.unescaped }
|
||||||
|
// } else {
|
||||||
|
// //invalidating value if it is present
|
||||||
|
// children.find { it is ConfigFXValue && it.name == path.unescaped }?.invalidate().orElse {
|
||||||
|
// //adding new node otherwise
|
||||||
|
// children.add(ConfigFXValue(path.unescaped, this))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// path.length > 1 -> children.filterIsInstance<ConfigFXNode>().find { it.name == path.first.unescaped }?.updateValue(
|
||||||
|
// path.cutFirst(),
|
||||||
|
// value
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fun updateNode(path: Name, list: List<Meta>) {
|
||||||
|
// when {
|
||||||
|
// path.isEmpty() -> invalidate()
|
||||||
|
// path.length == 1 -> {
|
||||||
|
// val hasDescriptor = descriptor?.getNodeDescriptor(path.unescaped) != null
|
||||||
|
// if (list.isEmpty() && !hasDescriptor) {
|
||||||
|
// children.removeIf { it.name == path.unescaped }
|
||||||
|
// } else {
|
||||||
|
// children.find { it is ConfigFXNode && it.name == path.unescaped }?.invalidate().orElse {
|
||||||
|
// children.add(ConfigFXNode(path.unescaped, this))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else -> children.filterIsInstance<ConfigFXNode>().find { it.name == path.first.toString() }?.updateNode(
|
||||||
|
// path.cutFirst(),
|
||||||
|
// list
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//class ConfigFXRoot(rootConfig: Config, rootDescriptor: NodeDescriptor? = null) : ConfigFXNode(rootConfig.name),
|
||||||
|
// ConfigChangeListener {
|
||||||
|
//
|
||||||
|
// init {
|
||||||
|
// configProperty.set(rootConfig)
|
||||||
|
// descriptorProperty.set(rootDescriptor)
|
||||||
|
// rootConfig.addListener(this)
|
||||||
|
// invalidate()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun notifyValueChanged(name: Name, oldItem: Value?, newItem: Value?) {
|
||||||
|
// updateValue(name, newItem)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun notifyNodeChanged(nodeName: Name, oldItem: List<Meta>, newItem: List<Meta>) {
|
||||||
|
// updateNode(nodeName, newItem)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * Created by darksnake on 01-May-17.
|
||||||
|
// */
|
||||||
|
//class ConfigFXValue(name: String, parent: ConfigFXNode) : ConfigFX(name) {
|
||||||
|
//
|
||||||
|
// init {
|
||||||
|
// parentProperty.set(parent)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override val hasValueProperty = parentProperty.booleanBinding(nameProperty) {
|
||||||
|
// it?.config?.hasValue(this.name) ?: false
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// override val children: ObservableList<ConfigFX> = FXCollections.emptyObservableList()
|
||||||
|
//
|
||||||
|
// val descriptor: ValueDescriptor? = parent.descriptor?.values[name]
|
||||||
|
//
|
||||||
|
// override val descriptionProperty: ObservableStringValue = object : StringBinding() {
|
||||||
|
// override fun computeValue(): String {
|
||||||
|
// return descriptor?.info ?: ""
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// val valueProperty = parentProperty.objectBinding(nameProperty) {
|
||||||
|
// parent.config?.optValue(name).nullable ?: descriptor?.default
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var value: Value
|
||||||
|
// set(value) {
|
||||||
|
// parent?.setValue(name, value)
|
||||||
|
// }
|
||||||
|
// get() = valueProperty.value ?: Value.NULL
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// override fun remove() {
|
||||||
|
// parent?.removeValue(name)
|
||||||
|
// invalidate()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun invalidate() {
|
||||||
|
// valueProperty.invalidate()
|
||||||
|
// hasValueProperty.invalidate()
|
||||||
|
// }
|
||||||
|
//}
|
@ -0,0 +1,119 @@
|
|||||||
|
package hep.dataforge.vis.fx.meta
|
||||||
|
|
||||||
|
import hep.dataforge.descriptors.NodeDescriptor
|
||||||
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.names.NameToken
|
||||||
|
import hep.dataforge.names.asName
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import javafx.beans.binding.ListBinding
|
||||||
|
import javafx.beans.binding.ObjectBinding
|
||||||
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
|
import javafx.beans.value.ObservableBooleanValue
|
||||||
|
import javafx.beans.value.ObservableStringValue
|
||||||
|
import javafx.collections.ObservableList
|
||||||
|
import tornadofx.*
|
||||||
|
|
||||||
|
sealed class FXMeta {
|
||||||
|
abstract val name: NameToken
|
||||||
|
abstract val parent: FXMetaNode<*>?
|
||||||
|
abstract val descriptionProperty: ObservableStringValue
|
||||||
|
|
||||||
|
abstract val hasValue: ObservableBooleanValue
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun <M : MetaNode<M>> root(node: M, descriptor: NodeDescriptor? = null): FXMetaNode<M> =
|
||||||
|
FXMetaNode(NameToken("root"), null, node, descriptor)
|
||||||
|
|
||||||
|
fun root(node: Meta, descriptor: NodeDescriptor? = null): FXMetaNode<SealedMeta> =
|
||||||
|
root(node.seal(), descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FXMetaNode<M : MetaNode<M>>(
|
||||||
|
override val name: NameToken,
|
||||||
|
override val parent: FXMetaNode<M>?,
|
||||||
|
node: M? = null,
|
||||||
|
descriptor: NodeDescriptor? = null
|
||||||
|
) : FXMeta() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A descriptor that could be manually set to the node
|
||||||
|
*/
|
||||||
|
val descriptorProperty = SimpleObjectProperty(descriptor)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actual descriptor which holds value inferred from parrent
|
||||||
|
*/
|
||||||
|
private val actualDescriptorProperty = objectBinding(descriptorProperty) {
|
||||||
|
value ?: parent?.descriptor?.nodes?.get(this@FXMetaNode.name.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
val descriptor: NodeDescriptor? by actualDescriptorProperty
|
||||||
|
|
||||||
|
private val innerNodeProperty = SimpleObjectProperty(node)
|
||||||
|
|
||||||
|
val nodeProperty: ObjectBinding<M?> = objectBinding(innerNodeProperty) {
|
||||||
|
value ?: parent?.node?.get(this@FXMetaNode.name.asName()).node
|
||||||
|
}
|
||||||
|
|
||||||
|
val node: M? by nodeProperty
|
||||||
|
|
||||||
|
override val descriptionProperty = descriptorProperty.stringBinding { it?.info ?: "" }
|
||||||
|
|
||||||
|
override val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null }
|
||||||
|
|
||||||
|
val children = object : ListBinding<FXMeta>() {
|
||||||
|
override fun computeValue(): ObservableList<FXMeta> {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FXMetaValue(
|
||||||
|
override val name: NameToken,
|
||||||
|
override val parent: FXMetaNode<*>,
|
||||||
|
value: Value? = null
|
||||||
|
) : FXMeta() {
|
||||||
|
|
||||||
|
val descriptorProperty = parent.descriptorProperty.objectBinding {
|
||||||
|
it?.values?.get(name.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A descriptor that could be manually set to the node
|
||||||
|
*/
|
||||||
|
val descriptor by descriptorProperty
|
||||||
|
|
||||||
|
private val innerValueProperty = SimpleObjectProperty(value)
|
||||||
|
|
||||||
|
val valueProperty = descriptorProperty.objectBinding { descriptor ->
|
||||||
|
parent.node[name].value ?: descriptor?.default
|
||||||
|
}
|
||||||
|
|
||||||
|
override val hasValue: ObservableBooleanValue = valueProperty.booleanBinding { it != null }
|
||||||
|
|
||||||
|
val value by valueProperty
|
||||||
|
|
||||||
|
override val descriptionProperty = descriptorProperty.stringBinding { it?.info ?: "" }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <M : MutableMeta<M>> FXMetaNode<M>.remove(name: NameToken) {
|
||||||
|
node?.remove(name.asName())
|
||||||
|
children.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FXMeta.remove() {
|
||||||
|
(parent?.node as? MutableMeta<*>)?.remove(name.asName())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <M : MutableMeta<M>> FXMetaNode<M>.addValue(key: String){
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <M : MutableMeta<M>> FXMetaNode<M>.addNode(key: String){
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FXMetaValue.set(value: Value?){
|
||||||
|
TODO()
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hep.dataforge.vis.fx.meta
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.seal
|
||||||
|
import hep.dataforge.vis.fx.dfIconView
|
||||||
|
import javafx.beans.property.SimpleStringProperty
|
||||||
|
import javafx.scene.control.TreeItem
|
||||||
|
import javafx.scene.control.TreeSortMode
|
||||||
|
import javafx.scene.control.TreeTableView
|
||||||
|
import tornadofx.*
|
||||||
|
|
||||||
|
open class MetaViewer(val meta: Meta, title: String = "Meta viewer") : Fragment(title, dfIconView) {
|
||||||
|
override val root = borderpane {
|
||||||
|
center {
|
||||||
|
treetableview<FXMeta> {
|
||||||
|
isShowRoot = false
|
||||||
|
root = TreeItem(FXMeta.root(meta.seal()))
|
||||||
|
populate {
|
||||||
|
when (val fxMeta = it.value) {
|
||||||
|
is FXMetaNode<*> -> {
|
||||||
|
fxMeta.children
|
||||||
|
}
|
||||||
|
is FXMetaValue -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.isExpanded = true
|
||||||
|
sortMode = TreeSortMode.ALL_DESCENDANTS
|
||||||
|
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
|
||||||
|
column("Name", FXMeta::name)
|
||||||
|
column<FXMeta, String>("Value"){
|
||||||
|
when(val item = it.value.value){
|
||||||
|
is FXMetaValue -> item.valueProperty.stringBinding{it?.string?: ""}
|
||||||
|
is FXMetaNode<*> -> SimpleStringProperty("[node]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package hep.dataforge.vis.fx.values
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.values.Null
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import hep.dataforge.values.asValue
|
||||||
|
import javafx.scene.control.ColorPicker
|
||||||
|
import javafx.scene.paint.Color
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import tornadofx.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by darksnake on 01-May-17.
|
||||||
|
*/
|
||||||
|
class ColorValueChooser : ValueChooserBase<ColorPicker>() {
|
||||||
|
private fun ColorPicker.setColor(value: Value?) {
|
||||||
|
if (value != null && value != Null) {
|
||||||
|
try {
|
||||||
|
runLater {
|
||||||
|
this.value = Color.valueOf(value.string)
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
LoggerFactory.getLogger(javaClass).warn("Invalid color field value: " + value.string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun setDisplayValue(value: Value) {
|
||||||
|
node.setColor(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildNode(): ColorPicker {
|
||||||
|
val node = ColorPicker()
|
||||||
|
node.styleClass.add("split-button")
|
||||||
|
node.maxWidth = java.lang.Double.MAX_VALUE
|
||||||
|
node.setColor(value)
|
||||||
|
node.setOnAction { _ -> value = node.value.toString().asValue() }
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object: ValueChooser.Factory{
|
||||||
|
override val name: String = "color"
|
||||||
|
|
||||||
|
override fun invoke(meta: Meta): ValueChooser = ColorValueChooser()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package hep.dataforge.vis.fx.values
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.get
|
||||||
|
import hep.dataforge.meta.value
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import hep.dataforge.values.parseValue
|
||||||
|
import javafx.collections.FXCollections
|
||||||
|
import javafx.scene.control.ComboBox
|
||||||
|
import javafx.util.StringConverter
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ComboBoxValueChooser(val values: Collection<Value>? = null) : ValueChooserBase<ComboBox<Value>>() {
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// protected void displayError(String error) {
|
||||||
|
// //TODO ControlsFX decorator here
|
||||||
|
// }
|
||||||
|
|
||||||
|
private fun allowedValues(): Collection<Value> {
|
||||||
|
return values ?: descriptor?.allowedValues ?: Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildNode(): ComboBox<Value> {
|
||||||
|
val node = ComboBox(FXCollections.observableArrayList(allowedValues()))
|
||||||
|
node.maxWidth = java.lang.Double.MAX_VALUE
|
||||||
|
node.isEditable = false
|
||||||
|
node.selectionModel.select(currentValue())
|
||||||
|
node.converter = object : StringConverter<Value>() {
|
||||||
|
override fun toString(value: Value?): String {
|
||||||
|
return value?.string ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromString(string: String?): Value {
|
||||||
|
return (string ?: "").parseValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
this.valueProperty.bind(node.valueProperty())
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setDisplayValue(value: Value) {
|
||||||
|
node.selectionModel.select(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : ValueChooser.Factory {
|
||||||
|
override val name: String = "combo"
|
||||||
|
|
||||||
|
override fun invoke(meta: Meta): ValueChooser = ComboBoxValueChooser(meta["values"].value?.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package hep.dataforge.vis.fx.values
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.values.*
|
||||||
|
import javafx.beans.value.ObservableValue
|
||||||
|
import javafx.scene.control.TextField
|
||||||
|
import javafx.scene.input.KeyCode
|
||||||
|
import javafx.scene.input.KeyEvent
|
||||||
|
import tornadofx.*
|
||||||
|
|
||||||
|
class TextValueChooser : ValueChooserBase<TextField>() {
|
||||||
|
|
||||||
|
private val displayText: String
|
||||||
|
get() = currentValue().let {
|
||||||
|
if (it.isNull()) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
it.string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun buildNode(): TextField {
|
||||||
|
val node = TextField()
|
||||||
|
val defaultValue = currentValue()
|
||||||
|
node.text = displayText
|
||||||
|
node.style = String.format("-fx-text-fill: %s;", textColor(defaultValue))
|
||||||
|
|
||||||
|
// commit on enter
|
||||||
|
node.setOnKeyPressed { event: KeyEvent ->
|
||||||
|
if (event.code == KeyCode.ENTER) {
|
||||||
|
commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// restoring value on click outside
|
||||||
|
node.focusedProperty().addListener { _: ObservableValue<out Boolean>, oldValue: Boolean, newValue: Boolean ->
|
||||||
|
if (oldValue && !newValue) {
|
||||||
|
node.text = displayText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// changing text color while editing
|
||||||
|
node.textProperty().onChange { newValue ->
|
||||||
|
if (newValue != null) {
|
||||||
|
val value = newValue.parseValue()
|
||||||
|
if (!validate(value)) {
|
||||||
|
node.style = String.format("-fx-text-fill: %s;", "red")
|
||||||
|
} else {
|
||||||
|
node.style = String.format("-fx-text-fill: %s;", textColor(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun commit() {
|
||||||
|
val newValue = node.text.parseValue()
|
||||||
|
if (validate(newValue)) {
|
||||||
|
value = newValue
|
||||||
|
} else {
|
||||||
|
resetValue()
|
||||||
|
displayError("Value not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun textColor(item: Value): String {
|
||||||
|
return when (item.type) {
|
||||||
|
ValueType.BOOLEAN -> if (item.boolean) {
|
||||||
|
"blue"
|
||||||
|
} else {
|
||||||
|
"salmon"
|
||||||
|
}
|
||||||
|
ValueType.STRING -> "magenta"
|
||||||
|
else -> "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validate(value: Value): Boolean {
|
||||||
|
return descriptor?.isAllowedValue(value) ?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// protected void displayError(String error) {
|
||||||
|
// //TODO ControlsFX decorator here
|
||||||
|
// }
|
||||||
|
|
||||||
|
override fun setDisplayValue(value: Value) {
|
||||||
|
node.text = if (value.isNull()) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
value.string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : ValueChooser.Factory {
|
||||||
|
override val name: String = "text"
|
||||||
|
override fun invoke(meta: Meta): ValueChooser = TextValueChooser()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package hep.dataforge.vis.fx.values
|
||||||
|
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param success
|
||||||
|
* @param value Value after change
|
||||||
|
* @param message Message on unsuccessful change
|
||||||
|
*/
|
||||||
|
class ValueCallbackResponse(val success: Boolean, val value: Value, val message: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback for some visual object trying to change some value
|
||||||
|
* @author [Alexander Nozik](mailto:altavir@gmail.com)
|
||||||
|
*/
|
||||||
|
typealias ValueCallback = (Value) -> ValueCallbackResponse
|
||||||
|
|
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package hep.dataforge.vis.fx.values
|
||||||
|
|
||||||
|
import hep.dataforge.context.Context
|
||||||
|
import hep.dataforge.context.Named
|
||||||
|
import hep.dataforge.descriptors.ValueDescriptor
|
||||||
|
import hep.dataforge.meta.EmptyMeta
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.provider.Type
|
||||||
|
import hep.dataforge.provider.provideByType
|
||||||
|
import hep.dataforge.values.Null
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import javafx.beans.property.ObjectProperty
|
||||||
|
import javafx.beans.value.ObservableValue
|
||||||
|
import javafx.scene.Node
|
||||||
|
import tornadofx.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A value chooser object. Must have an empty constructor to be invoked by
|
||||||
|
* reflections.
|
||||||
|
*
|
||||||
|
* @author [Alexander Nozik](mailto:altavir@gmail.com)
|
||||||
|
*/
|
||||||
|
interface ValueChooser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a Node that could be later inserted into some parent
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
val node: Node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The descriptor property for this value. Could be null
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
val descriptorProperty: ObjectProperty<ValueDescriptor?>
|
||||||
|
var descriptor: ValueDescriptor?
|
||||||
|
|
||||||
|
val valueProperty: ObjectProperty<Value?>
|
||||||
|
var value: Value?
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set display value but do not notify listeners
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
fun setDisplayValue(value: Value)
|
||||||
|
|
||||||
|
|
||||||
|
fun setDisabled(disabled: Boolean) {
|
||||||
|
//TODO replace by property
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCallback(callback: ValueCallback)
|
||||||
|
|
||||||
|
@Type("hep.dataforge.vis.fx.valueChooserFactory")
|
||||||
|
interface Factory: Named {
|
||||||
|
operator fun invoke(meta: Meta = EmptyMeta): ValueChooser
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private fun findWidgetByType(context: Context, type: String): Factory? {
|
||||||
|
return when(type){
|
||||||
|
TextValueChooser.name -> TextValueChooser
|
||||||
|
ColorValueChooser.name -> ColorValueChooser
|
||||||
|
ComboBoxValueChooser.name -> ComboBoxValueChooser
|
||||||
|
else-> context.provideByType(type)//Search for additional factories in the plugin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun build(descriptor: ValueDescriptor?): ValueChooser {
|
||||||
|
return if (descriptor == null) {
|
||||||
|
TextValueChooser();
|
||||||
|
} else {
|
||||||
|
//val types = descriptor.type
|
||||||
|
val chooser: ValueChooser = when {
|
||||||
|
descriptor.allowedValues.isNotEmpty() -> ComboBoxValueChooser()
|
||||||
|
descriptor.tags.contains("widget:color") -> ColorValueChooser()
|
||||||
|
else -> TextValueChooser()
|
||||||
|
}
|
||||||
|
chooser.descriptor = descriptor
|
||||||
|
chooser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(
|
||||||
|
value: ObservableValue<Value?>,
|
||||||
|
descriptor: ValueDescriptor? = null,
|
||||||
|
setter: (Value) -> Unit
|
||||||
|
): ValueChooser {
|
||||||
|
val chooser = build(descriptor)
|
||||||
|
chooser.setDisplayValue(value.value ?: Null)
|
||||||
|
value.onChange {
|
||||||
|
chooser.setDisplayValue(it ?: Null)
|
||||||
|
}
|
||||||
|
chooser.setCallback { result ->
|
||||||
|
if (descriptor?.isAllowedValue(result) != false) {
|
||||||
|
setter(result)
|
||||||
|
ValueCallbackResponse(true, result, "OK")
|
||||||
|
} else {
|
||||||
|
ValueCallbackResponse(false, value.value ?: Null, "Not allowed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chooser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package hep.dataforge.vis.fx.values
|
||||||
|
|
||||||
|
import hep.dataforge.descriptors.ValueDescriptor
|
||||||
|
import hep.dataforge.values.Null
|
||||||
|
import hep.dataforge.values.Value
|
||||||
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
|
import javafx.scene.Node
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import tornadofx.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValueChooser boilerplate
|
||||||
|
*
|
||||||
|
* @author Alexander Nozik
|
||||||
|
*/
|
||||||
|
abstract class ValueChooserBase<out T : Node> : ValueChooser {
|
||||||
|
|
||||||
|
override val node by lazy { buildNode() }
|
||||||
|
final override val valueProperty = SimpleObjectProperty<Value>(Null)
|
||||||
|
final override val descriptorProperty = SimpleObjectProperty<ValueDescriptor>()
|
||||||
|
|
||||||
|
override var descriptor: ValueDescriptor? by descriptorProperty
|
||||||
|
override var value: Value? by valueProperty
|
||||||
|
|
||||||
|
fun resetValue() {
|
||||||
|
setDisplayValue(currentValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current value or default value
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected fun currentValue(): Value {
|
||||||
|
return value ?: descriptor?.default ?: Null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if builder node is successful
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected abstract fun buildNode(): T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display validation error
|
||||||
|
*
|
||||||
|
* @param error
|
||||||
|
*/
|
||||||
|
protected fun displayError(error: String) {
|
||||||
|
LoggerFactory.getLogger(javaClass).error(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setCallback(callback: ValueCallback) {
|
||||||
|
valueProperty.onChange { newValue: Value? ->
|
||||||
|
val response = callback(newValue ?: Null)
|
||||||
|
if (response.value != valueProperty.get()) {
|
||||||
|
setDisplayValue(response.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
displayError(response.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
dataforge-vis-fx/src/main/resources/img/df.png
Normal file
BIN
dataforge-vis-fx/src/main/resources/img/df.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
@ -7,6 +7,7 @@ import hep.dataforge.context.PluginTag
|
|||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vis.spatial.ThreePlugin
|
import hep.dataforge.vis.spatial.ThreePlugin
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class GDMLPlugin : AbstractPlugin() {
|
class GDMLPlugin : AbstractPlugin() {
|
||||||
override val tag: PluginTag get() = GDMLPlugin.tag
|
override val tag: PluginTag get() = GDMLPlugin.tag
|
||||||
@ -30,7 +31,7 @@ class GDMLPlugin : AbstractPlugin() {
|
|||||||
|
|
||||||
companion object : PluginFactory<GDMLPlugin> {
|
companion object : PluginFactory<GDMLPlugin> {
|
||||||
override val tag = PluginTag("vis.gdml", "hep.dataforge")
|
override val tag = PluginTag("vis.gdml", "hep.dataforge")
|
||||||
override val type = GDMLPlugin::class
|
override val type: KClass<GDMLPlugin> = GDMLPlugin::class
|
||||||
override fun invoke(meta: Meta) = GDMLPlugin()
|
override fun invoke(meta: Meta) = GDMLPlugin()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,10 +2,8 @@ package hep.dataforge.vis.spatial
|
|||||||
|
|
||||||
import hep.dataforge.meta.EmptyMeta
|
import hep.dataforge.meta.EmptyMeta
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.vis.DisplayGroup
|
|
||||||
import hep.dataforge.vis.DisplayLeaf
|
|
||||||
import hep.dataforge.vis.DisplayObject
|
import hep.dataforge.vis.DisplayObject
|
||||||
import hep.dataforge.vis.double
|
import hep.dataforge.vis.DisplayObjectList
|
||||||
|
|
||||||
class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) {
|
class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) {
|
||||||
var xSize by double(1.0)
|
var xSize by double(1.0)
|
||||||
@ -19,5 +17,5 @@ class Box(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DisplayGroup.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) =
|
fun DisplayObjectList.box(meta: Meta = EmptyMeta, action: Box.() -> Unit = {}) =
|
||||||
Box(this, meta).apply(action).also { addChild(it) }
|
Box(this, meta).apply(action).also { addChild(it) }
|
@ -2,9 +2,8 @@ package hep.dataforge.vis.spatial
|
|||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vis.DisplayGroup
|
|
||||||
import hep.dataforge.vis.DisplayLeaf
|
|
||||||
import hep.dataforge.vis.DisplayObject
|
import hep.dataforge.vis.DisplayObject
|
||||||
|
import hep.dataforge.vis.DisplayObjectList
|
||||||
|
|
||||||
class Convex(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) {
|
class Convex(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) {
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ class Convex(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, met
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DisplayGroup.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) =
|
fun DisplayObjectList.convex(meta: Meta = EmptyMeta, action: ConvexBuilder.() -> Unit = {}) =
|
||||||
ConvexBuilder().apply(action).build(this, meta).also { addChild(it) }
|
ConvexBuilder().apply(action).build(this, meta).also { addChild(it) }
|
||||||
|
|
||||||
class ConvexBuilder {
|
class ConvexBuilder {
|
||||||
|
@ -2,9 +2,8 @@ package hep.dataforge.vis.spatial
|
|||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vis.DisplayGroup
|
|
||||||
import hep.dataforge.vis.DisplayLeaf
|
|
||||||
import hep.dataforge.vis.DisplayObject
|
import hep.dataforge.vis.DisplayObject
|
||||||
|
import hep.dataforge.vis.DisplayObjectList
|
||||||
|
|
||||||
class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) {
|
class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, meta) {
|
||||||
|
|
||||||
@ -22,5 +21,5 @@ class Extruded(parent: DisplayObject?, meta: Meta) : DisplayLeaf(parent, TYPE, m
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun DisplayGroup.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) =
|
fun DisplayObjectList.extrude(meta: Meta = EmptyMeta, action: Extruded.() -> Unit = {}) =
|
||||||
Extruded(this, meta).apply(action).also { addChild(it) }
|
Extruded(this, meta).apply(action).also { addChild(it) }
|
@ -3,17 +3,17 @@ package hep.dataforge.vis.spatial
|
|||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.output.Output
|
import hep.dataforge.output.Output
|
||||||
import hep.dataforge.vis.DisplayGroup
|
import hep.dataforge.vis.DisplayGroup
|
||||||
import hep.dataforge.vis.DisplayNode
|
|
||||||
import hep.dataforge.vis.DisplayObject
|
import hep.dataforge.vis.DisplayObject
|
||||||
import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE
|
import hep.dataforge.vis.DisplayObject.Companion.DEFAULT_TYPE
|
||||||
import hep.dataforge.vis.get
|
import hep.dataforge.vis.DisplayObjectList
|
||||||
|
import hep.dataforge.vis.getProperty
|
||||||
|
|
||||||
fun DisplayGroup.group(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit = {}) =
|
fun DisplayObjectList.group(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit = {}): DisplayGroup =
|
||||||
DisplayNode(this, DEFAULT_TYPE, meta).apply(action).also { addChild(it) }
|
DisplayObjectList(this, DEFAULT_TYPE, meta).apply(action).also { addChild(it) }
|
||||||
|
|
||||||
|
|
||||||
fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) =
|
fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit) =
|
||||||
render(DisplayNode(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta)
|
render(DisplayObjectList(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta)
|
||||||
|
|
||||||
//TODO replace properties by containers?
|
//TODO replace properties by containers?
|
||||||
|
|
||||||
@ -23,9 +23,9 @@ fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayGroup.()
|
|||||||
* Visibility property. Inherited from parent
|
* Visibility property. Inherited from parent
|
||||||
*/
|
*/
|
||||||
var DisplayObject.visible
|
var DisplayObject.visible
|
||||||
get() = this["visible"].boolean ?: true
|
get() = getProperty("visible").boolean ?: true
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["visible"] = value
|
properties["visible"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3D Object position
|
// 3D Object position
|
||||||
@ -36,7 +36,7 @@ var DisplayObject.visible
|
|||||||
var DisplayObject.x
|
var DisplayObject.x
|
||||||
get() = properties["pos.x"].number ?: 0.0
|
get() = properties["pos.x"].number ?: 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["pos.x"] = value
|
properties["pos.x"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,7 +45,7 @@ var DisplayObject.x
|
|||||||
var DisplayObject.y
|
var DisplayObject.y
|
||||||
get() = properties["pos.y"].number ?: 0.0
|
get() = properties["pos.y"].number ?: 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["pos.y"] = value
|
properties["pos.y"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +54,7 @@ var DisplayObject.y
|
|||||||
var DisplayObject.z
|
var DisplayObject.z
|
||||||
get() = properties["pos.z"].number ?: 0.0
|
get() = properties["pos.z"].number ?: 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["pos.z"] = value
|
properties["pos.z"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3D Object rotation
|
// 3D Object rotation
|
||||||
@ -65,7 +65,7 @@ var DisplayObject.z
|
|||||||
var DisplayObject.rotationX
|
var DisplayObject.rotationX
|
||||||
get() = properties["rotation.x"].number ?: 0.0
|
get() = properties["rotation.x"].number ?: 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["rotation.x"] = value
|
properties["rotation.x"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +74,7 @@ var DisplayObject.rotationX
|
|||||||
var DisplayObject.rotationY
|
var DisplayObject.rotationY
|
||||||
get() = properties["rotation.y"].number ?: 0.0
|
get() = properties["rotation.y"].number ?: 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["rotation.y"] = value
|
properties["rotation.y"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,7 +83,7 @@ var DisplayObject.rotationY
|
|||||||
var DisplayObject.rotationZ
|
var DisplayObject.rotationZ
|
||||||
get() = properties["rotation.z"].number ?: 0.0
|
get() = properties["rotation.z"].number ?: 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["rotation.z"] = value
|
properties["rotation.z"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RotationOrder {
|
enum class RotationOrder {
|
||||||
@ -101,7 +101,7 @@ enum class RotationOrder {
|
|||||||
var DisplayObject.rotationOrder: RotationOrder
|
var DisplayObject.rotationOrder: RotationOrder
|
||||||
get() = properties["rotation.order"].enum<RotationOrder>() ?: RotationOrder.XYZ
|
get() = properties["rotation.order"].enum<RotationOrder>() ?: RotationOrder.XYZ
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["rotation.order"] = value
|
properties["rotation.order"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3D object scale
|
// 3D object scale
|
||||||
@ -112,7 +112,7 @@ var DisplayObject.rotationOrder: RotationOrder
|
|||||||
var DisplayObject.scaleX
|
var DisplayObject.scaleX
|
||||||
get() = properties["scale.x"].number ?: 1.0
|
get() = properties["scale.x"].number ?: 1.0
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["scale.x"] = value
|
properties["scale.x"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,7 +121,7 @@ var DisplayObject.scaleX
|
|||||||
var DisplayObject.scaleY
|
var DisplayObject.scaleY
|
||||||
get() = properties["scale.y"].number ?: 1.0
|
get() = properties["scale.y"].number ?: 1.0
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["scale.y"] = value
|
properties["scale.y"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,15 +130,15 @@ var DisplayObject.scaleY
|
|||||||
var DisplayObject.scaleZ
|
var DisplayObject.scaleZ
|
||||||
get() = properties["scale.z"].number ?: 1.0
|
get() = properties["scale.z"].number ?: 1.0
|
||||||
set(value) {
|
set(value) {
|
||||||
properties.style["scale.z"] = value
|
properties["scale.z"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DisplayObject.color(rgb: Int){
|
fun DisplayObject.color(rgb: Int) {
|
||||||
this.properties.style["color"] = rgb
|
this.properties["color"] = rgb
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DisplayObject.color(meta: Meta){
|
fun DisplayObject.color(meta: Meta) {
|
||||||
this.properties.style["color"] = meta
|
this.properties["color"] = meta
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DisplayObject.color(r: Int, g: Int, b: Int) = color(buildMeta {
|
fun DisplayObject.color(r: Int, g: Int, b: Int) = color(buildMeta {
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
package hep.dataforge.vis.spatial
|
package hep.dataforge.vis.spatial
|
||||||
|
|
||||||
import hep.dataforge.meta.get
|
|
||||||
import hep.dataforge.meta.getAll
|
|
||||||
import hep.dataforge.meta.node
|
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.vis.DisplayNode
|
import hep.dataforge.vis.DisplayObjectList
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class ConvexTest {
|
class ConvexTest {
|
||||||
@Test
|
@Test
|
||||||
fun testConvexBuilder() {
|
fun testConvexBuilder() {
|
||||||
val group = DisplayNode().apply {
|
val group = DisplayObjectList().apply {
|
||||||
convex {
|
convex {
|
||||||
point(50, 50, -50)
|
point(50, 50, -50)
|
||||||
point(50, -50, -50)
|
point(50, -50, -50)
|
||||||
|
@ -22,15 +22,16 @@ rootProject.name = "dataforge-vis"
|
|||||||
|
|
||||||
include(
|
include(
|
||||||
":dataforge-vis-common",
|
":dataforge-vis-common",
|
||||||
|
":dataforge-vis-fx",
|
||||||
":dataforge-vis-spatial",
|
":dataforge-vis-spatial",
|
||||||
":dataforge-vis-spatial-fx",
|
":dataforge-vis-spatial-fx",
|
||||||
":dataforge-vis-spatial-js"
|
":dataforge-vis-spatial-js"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(file("../dataforge-core").exists()) {
|
//if(file("../dataforge-core").exists()) {
|
||||||
includeBuild("../dataforge-core"){
|
// includeBuild("../dataforge-core"){
|
||||||
dependencySubstitution {
|
// dependencySubstitution {
|
||||||
substitute(module("hep.dataforge:dataforge-output")).with(project(":dataforge-output"))
|
// substitute(module("hep.dataforge:dataforge-output")).with(project(":dataforge-output"))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
Loading…
Reference in New Issue
Block a user