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 {
|
||||
repositories {
|
||||
|
@ -9,6 +9,17 @@ kotlin {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
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.names.Name
|
||||
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.TAGS_KEY
|
||||
|
||||
@ -17,60 +16,34 @@ interface DisplayObject {
|
||||
*/
|
||||
val parent: DisplayObject?
|
||||
|
||||
/**
|
||||
* The type of this object. Uses `.` notation. Empty type means untyped group
|
||||
*/
|
||||
val type: String
|
||||
// /**
|
||||
// * The type of this object. Uses `.` notation. Empty type means untyped group
|
||||
// */
|
||||
// val type: String
|
||||
|
||||
val properties: Styled
|
||||
val properties: MutableMeta<*>
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_TYPE = ""
|
||||
const val TYPE_KEY = "@type"
|
||||
const val CHILDREN_KEY = "@children"
|
||||
//const val TYPE_KEY = "@type"
|
||||
//const val CHILDREN_KEY = "@children"
|
||||
const val META_KEY = "@meta"
|
||||
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
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
@ -78,7 +51,7 @@ fun DisplayObject.onChange(owner: Any?, action: (Name, before: MetaItem<*>?, aft
|
||||
* Remove all meta listeners with matching owners
|
||||
*/
|
||||
fun DisplayObject.removeChangeListener(owner: Any?) {
|
||||
properties.style.removeListener(owner)
|
||||
properties.removeListener(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
|
||||
|
||||
internal data class ObjectListener(
|
||||
val owner: Any?,
|
||||
val action: () -> Unit
|
||||
)
|
||||
|
||||
/**
|
||||
* Basic group of display objects
|
||||
*/
|
||||
open class DisplayNode(
|
||||
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
|
||||
///**
|
||||
// * 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)
|
||||
//}
|
||||
// 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<*>? {
|
||||
val name = key ?: property.name.toName()
|
||||
return if (inherited) {
|
||||
thisRef[name]
|
||||
thisRef.getProperty(name)
|
||||
} else {
|
||||
thisRef.properties[name]
|
||||
} ?: default
|
||||
@ -28,7 +28,7 @@ class DisplayObjectDelegate(
|
||||
|
||||
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: MetaItem<*>?) {
|
||||
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 {
|
||||
val name = key ?: property.name.toName()
|
||||
return if (inherited) {
|
||||
read(thisRef[name])
|
||||
read(thisRef.getProperty(name))
|
||||
} else {
|
||||
read(thisRef.properties[name])
|
||||
} ?: default
|
||||
@ -50,7 +50,7 @@ class DisplayObjectDelegateWrapper<T>(
|
||||
|
||||
override fun setValue(thisRef: DisplayObject, property: KProperty<*>, value: T) {
|
||||
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.names.toName
|
||||
import hep.dataforge.vis.spatial.ThreePlugin
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class GDMLPlugin : AbstractPlugin() {
|
||||
override val tag: PluginTag get() = GDMLPlugin.tag
|
||||
@ -30,7 +31,7 @@ class GDMLPlugin : AbstractPlugin() {
|
||||
|
||||
companion object : PluginFactory<GDMLPlugin> {
|
||||
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()
|
||||
}
|
||||
}
|
@ -2,10 +2,8 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.vis.DisplayGroup
|
||||
import hep.dataforge.vis.DisplayLeaf
|
||||
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) {
|
||||
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) }
|
@ -2,9 +2,8 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.DisplayGroup
|
||||
import hep.dataforge.vis.DisplayLeaf
|
||||
import hep.dataforge.vis.DisplayObject
|
||||
import hep.dataforge.vis.DisplayObjectList
|
||||
|
||||
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) }
|
||||
|
||||
class ConvexBuilder {
|
||||
|
@ -2,9 +2,8 @@ package hep.dataforge.vis.spatial
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.DisplayGroup
|
||||
import hep.dataforge.vis.DisplayLeaf
|
||||
import hep.dataforge.vis.DisplayObject
|
||||
import hep.dataforge.vis.DisplayObjectList
|
||||
|
||||
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) }
|
@ -3,17 +3,17 @@ package hep.dataforge.vis.spatial
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.output.Output
|
||||
import hep.dataforge.vis.DisplayGroup
|
||||
import hep.dataforge.vis.DisplayNode
|
||||
import hep.dataforge.vis.DisplayObject
|
||||
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 = {}) =
|
||||
DisplayNode(this, DEFAULT_TYPE, meta).apply(action).also { addChild(it) }
|
||||
fun DisplayObjectList.group(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit = {}): DisplayGroup =
|
||||
DisplayObjectList(this, DEFAULT_TYPE, meta).apply(action).also { addChild(it) }
|
||||
|
||||
|
||||
fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) =
|
||||
render(DisplayNode(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta)
|
||||
fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayObjectList.() -> Unit) =
|
||||
render(DisplayObjectList(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta)
|
||||
|
||||
//TODO replace properties by containers?
|
||||
|
||||
@ -23,9 +23,9 @@ fun Output<DisplayObject>.render(meta: Meta = EmptyMeta, action: DisplayGroup.()
|
||||
* Visibility property. Inherited from parent
|
||||
*/
|
||||
var DisplayObject.visible
|
||||
get() = this["visible"].boolean ?: true
|
||||
get() = getProperty("visible").boolean ?: true
|
||||
set(value) {
|
||||
properties.style["visible"] = value
|
||||
properties["visible"] = value
|
||||
}
|
||||
|
||||
// 3D Object position
|
||||
@ -36,7 +36,7 @@ var DisplayObject.visible
|
||||
var DisplayObject.x
|
||||
get() = properties["pos.x"].number ?: 0.0
|
||||
set(value) {
|
||||
properties.style["pos.x"] = value
|
||||
properties["pos.x"] = value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,7 +45,7 @@ var DisplayObject.x
|
||||
var DisplayObject.y
|
||||
get() = properties["pos.y"].number ?: 0.0
|
||||
set(value) {
|
||||
properties.style["pos.y"] = value
|
||||
properties["pos.y"] = value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,7 +54,7 @@ var DisplayObject.y
|
||||
var DisplayObject.z
|
||||
get() = properties["pos.z"].number ?: 0.0
|
||||
set(value) {
|
||||
properties.style["pos.z"] = value
|
||||
properties["pos.z"] = value
|
||||
}
|
||||
|
||||
// 3D Object rotation
|
||||
@ -65,7 +65,7 @@ var DisplayObject.z
|
||||
var DisplayObject.rotationX
|
||||
get() = properties["rotation.x"].number ?: 0.0
|
||||
set(value) {
|
||||
properties.style["rotation.x"] = value
|
||||
properties["rotation.x"] = value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,7 +74,7 @@ var DisplayObject.rotationX
|
||||
var DisplayObject.rotationY
|
||||
get() = properties["rotation.y"].number ?: 0.0
|
||||
set(value) {
|
||||
properties.style["rotation.y"] = value
|
||||
properties["rotation.y"] = value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,7 +83,7 @@ var DisplayObject.rotationY
|
||||
var DisplayObject.rotationZ
|
||||
get() = properties["rotation.z"].number ?: 0.0
|
||||
set(value) {
|
||||
properties.style["rotation.z"] = value
|
||||
properties["rotation.z"] = value
|
||||
}
|
||||
|
||||
enum class RotationOrder {
|
||||
@ -101,7 +101,7 @@ enum class RotationOrder {
|
||||
var DisplayObject.rotationOrder: RotationOrder
|
||||
get() = properties["rotation.order"].enum<RotationOrder>() ?: RotationOrder.XYZ
|
||||
set(value) {
|
||||
properties.style["rotation.order"] = value
|
||||
properties["rotation.order"] = value
|
||||
}
|
||||
|
||||
// 3D object scale
|
||||
@ -112,7 +112,7 @@ var DisplayObject.rotationOrder: RotationOrder
|
||||
var DisplayObject.scaleX
|
||||
get() = properties["scale.x"].number ?: 1.0
|
||||
set(value) {
|
||||
properties.style["scale.x"] = value
|
||||
properties["scale.x"] = value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,7 +121,7 @@ var DisplayObject.scaleX
|
||||
var DisplayObject.scaleY
|
||||
get() = properties["scale.y"].number ?: 1.0
|
||||
set(value) {
|
||||
properties.style["scale.y"] = value
|
||||
properties["scale.y"] = value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,15 +130,15 @@ var DisplayObject.scaleY
|
||||
var DisplayObject.scaleZ
|
||||
get() = properties["scale.z"].number ?: 1.0
|
||||
set(value) {
|
||||
properties.style["scale.z"] = value
|
||||
properties["scale.z"] = value
|
||||
}
|
||||
|
||||
fun DisplayObject.color(rgb: Int) {
|
||||
this.properties.style["color"] = rgb
|
||||
this.properties["color"] = rgb
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -1,17 +1,14 @@
|
||||
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.vis.DisplayNode
|
||||
import hep.dataforge.vis.DisplayObjectList
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ConvexTest {
|
||||
@Test
|
||||
fun testConvexBuilder() {
|
||||
val group = DisplayNode().apply {
|
||||
val group = DisplayObjectList().apply {
|
||||
convex {
|
||||
point(50, 50, -50)
|
||||
point(50, -50, -50)
|
||||
|
@ -22,15 +22,16 @@ rootProject.name = "dataforge-vis"
|
||||
|
||||
include(
|
||||
":dataforge-vis-common",
|
||||
":dataforge-vis-fx",
|
||||
":dataforge-vis-spatial",
|
||||
":dataforge-vis-spatial-fx",
|
||||
":dataforge-vis-spatial-js"
|
||||
)
|
||||
|
||||
if(file("../dataforge-core").exists()) {
|
||||
includeBuild("../dataforge-core"){
|
||||
dependencySubstitution {
|
||||
substitute(module("hep.dataforge:dataforge-output")).with(project(":dataforge-output"))
|
||||
}
|
||||
}
|
||||
}
|
||||
//if(file("../dataforge-core").exists()) {
|
||||
// includeBuild("../dataforge-core"){
|
||||
// dependencySubstitution {
|
||||
// substitute(module("hep.dataforge:dataforge-output")).with(project(":dataforge-output"))
|
||||
// }
|
||||
// }
|
||||
//}
|
Loading…
Reference in New Issue
Block a user