diff --git a/build.gradle.kts b/build.gradle.kts index a49a9e8a..b0cc2184 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ -val dataforgeVersion by extra("0.1.3-dev-1") +val dataforgeVersion by extra("0.1.3-dev-2") allprojects { repositories { diff --git a/dataforge-vis-common/build.gradle.kts b/dataforge-vis-common/build.gradle.kts index 900391e9..069181bb 100644 --- a/dataforge-vis-common/build.gradle.kts +++ b/dataforge-vis-common/build.gradle.kts @@ -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") } } } diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayList.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayList.kt new file mode 100644 index 00000000..b03a1fc6 --- /dev/null +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayList.kt @@ -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() + + /** + * An ordered list of direct descendants + */ + val children: List get() = _children + + override fun iterator(): Iterator = children.iterator() + + + override val properties = Styled(meta) + private val listeners = HashSet() + + /** + * 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 } + } +} + diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt index 504871f4..1f40a835 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayObject.kt @@ -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 - - /** - * 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 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() - override val children: List get() = _children - override val properties = Styled(meta) - private val listeners = HashSet() - - 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) -} +///** +// * 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 diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt index 5b11d5e4..2f444b46 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayObjectDelegates.kt @@ -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( 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( 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 } } diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayTree.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayTree.kt new file mode 100644 index 00000000..116cbe52 --- /dev/null +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/DisplayTree.kt @@ -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") + } + } + } \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt deleted file mode 100644 index bc15d9e6..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/NamedObject.kt +++ /dev/null @@ -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") - } - } -} \ No newline at end of file diff --git a/dataforge-vis-fx/build.gradle.kts b/dataforge-vis-fx/build.gradle.kts new file mode 100644 index 00000000..a1a13be8 --- /dev/null +++ b/dataforge-vis-fx/build.gradle.kts @@ -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 { + modules("javafx.controls") +} + +tasks.withType { + kotlinOptions{ + jvmTarget = "1.8" + } +} \ No newline at end of file diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/FXPlugin.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/FXPlugin.kt new file mode 100644 index 00000000..01a873fb --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/FXPlugin.kt @@ -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 = 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() + }.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 { + override val type: KClass = 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().display(component(), width, height) +} \ No newline at end of file diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/ConfigEditor.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/ConfigEditor.kt new file mode 100644 index 00000000..5cafce9a --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/ConfigEditor.kt @@ -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 { + 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() { + 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 -> + param.value.valueProperty() + }.setCellFactory { + ValueCell() + } + + column("Description") { param: TreeTableColumn.CellDataFeatures -> param.value.value.descriptionProperty } + .setCellFactory { param: TreeTableColumn -> + val cell = TreeTableCell() + 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() { + + 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 + + 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" + } +} diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/ConfigFX.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/ConfigFX.kt new file mode 100644 index 00000000..fc06e4e7 --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/ConfigFX.kt @@ -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) { +// 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() +// val parent by parentProperty +// +// abstract val hasValueProperty: ObservableBooleanValue +// //abstract val hasDefaultProperty: ObservableBooleanValue +// +// abstract val descriptionProperty: ObservableStringValue +// +// abstract val children: ObservableList +// +// /** +// * 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() +// +// /** +// * 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() +// +// 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 = FXCollections.observableArrayList() +// +// 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() +// val valueNames = ArrayList() +// +// 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().find { it.name == path.first.unescaped }?.updateValue( +// path.cutFirst(), +// value +// ) +// } +// } +// +// fun updateNode(path: Name, list: List) { +// 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().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, newItem: List) { +// 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 = 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() +// } +//} diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/FXMeta.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/FXMeta.kt new file mode 100644 index 00000000..2b33a767 --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/FXMeta.kt @@ -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 > root(node: M, descriptor: NodeDescriptor? = null): FXMetaNode = + FXMetaNode(NameToken("root"), null, node, descriptor) + + fun root(node: Meta, descriptor: NodeDescriptor? = null): FXMetaNode = + root(node.seal(), descriptor) + } +} + +class FXMetaNode>( + override val name: NameToken, + override val parent: FXMetaNode?, + 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 = 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() { + override fun computeValue(): ObservableList { + 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 > FXMetaNode.remove(name: NameToken) { + node?.remove(name.asName()) + children.invalidate() +} + +fun FXMeta.remove() { + (parent?.node as? MutableMeta<*>)?.remove(name.asName()) +} + +fun > FXMetaNode.addValue(key: String){ + TODO() +} + +fun > FXMetaNode.addNode(key: String){ + TODO() +} + +fun FXMetaValue.set(value: Value?){ + TODO() +} \ No newline at end of file diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/MetaViewer.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/MetaViewer.kt new file mode 100644 index 00000000..3eddfb78 --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/meta/MetaViewer.kt @@ -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 { + 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("Value"){ + when(val item = it.value.value){ + is FXMetaValue -> item.valueProperty.stringBinding{it?.string?: ""} + is FXMetaNode<*> -> SimpleStringProperty("[node]") + } + } + } + } + } +} \ No newline at end of file diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ColorValueChooser.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ColorValueChooser.kt new file mode 100644 index 00000000..e0e0f94a --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ColorValueChooser.kt @@ -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() { + 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() + } +} diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ComboBoxValueChooser.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ComboBoxValueChooser.kt new file mode 100644 index 00000000..0f6460e0 --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ComboBoxValueChooser.kt @@ -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? = null) : ValueChooserBase>() { + + // @Override + // protected void displayError(String error) { + // //TODO ControlsFX decorator here + // } + + private fun allowedValues(): Collection { + return values ?: descriptor?.allowedValues ?: Collections.emptyList(); + } + + override fun buildNode(): ComboBox { + val node = ComboBox(FXCollections.observableArrayList(allowedValues())) + node.maxWidth = java.lang.Double.MAX_VALUE + node.isEditable = false + node.selectionModel.select(currentValue()) + node.converter = object : StringConverter() { + 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) + } + +} diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/TextValueChooser.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/TextValueChooser.kt new file mode 100644 index 00000000..b273b71f --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/TextValueChooser.kt @@ -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() { + + 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, 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() + } +} diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ValueCallback.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ValueCallback.kt new file mode 100644 index 00000000..c327ab22 --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ValueCallback.kt @@ -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 + diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ValueChooser.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ValueChooser.kt new file mode 100644 index 00000000..4635e87c --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ValueChooser.kt @@ -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 + var descriptor: ValueDescriptor? + + val valueProperty: ObjectProperty + 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, + 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 + } + } +} diff --git a/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ValueChooserBase.kt b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ValueChooserBase.kt new file mode 100644 index 00000000..a9e2fc01 --- /dev/null +++ b/dataforge-vis-fx/src/main/kotlin/hep/dataforge/vis/fx/values/ValueChooserBase.kt @@ -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 : ValueChooser { + + override val node by lazy { buildNode() } + final override val valueProperty = SimpleObjectProperty(Null) + final override val descriptorProperty = SimpleObjectProperty() + + 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) + } + } + } +} diff --git a/dataforge-vis-fx/src/main/resources/img/df.png b/dataforge-vis-fx/src/main/resources/img/df.png new file mode 100644 index 00000000..076e26a2 Binary files /dev/null and b/dataforge-vis-fx/src/main/resources/img/df.png differ diff --git a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLPlugin.kt b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLPlugin.kt index 6445755a..364f5637 100644 --- a/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLPlugin.kt +++ b/dataforge-vis-spatial-js/src/main/kotlin/hep/dataforge/vis/spatial/gdml/GDMLPlugin.kt @@ -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 { override val tag = PluginTag("vis.gdml", "hep.dataforge") - override val type = GDMLPlugin::class + override val type: KClass = GDMLPlugin::class override fun invoke(meta: Meta) = GDMLPlugin() } } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt index b970927e..92d40a08 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Box.kt @@ -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) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt index 1f57be85..9b921e22 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Convex.kt @@ -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 { diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt index 773191fc..6cdd04c8 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/Extruded.kt @@ -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) } \ No newline at end of file diff --git a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt index 1f1ccd62..bd5d2e6d 100644 --- a/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt +++ b/dataforge-vis-spatial/src/commonMain/kotlin/hep/dataforge/vis/spatial/displayObject3D.kt @@ -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.render(meta: Meta = EmptyMeta, action: DisplayGroup.() -> Unit) = - render(DisplayNode(null, DEFAULT_TYPE, EmptyMeta).apply(action), meta) +fun Output.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.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.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 +fun DisplayObject.color(rgb: Int) { + this.properties["color"] = rgb } -fun DisplayObject.color(meta: Meta){ - this.properties.style["color"] = meta +fun DisplayObject.color(meta: Meta) { + this.properties["color"] = meta } fun DisplayObject.color(r: Int, g: Int, b: Int) = color(buildMeta { diff --git a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt index 1b83c1e0..7d8fbb58 100644 --- a/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt +++ b/dataforge-vis-spatial/src/commonTest/kotlin/hep/dataforge/vis/spatial/ConvexTest.kt @@ -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) diff --git a/settings.gradle.kts b/settings.gradle.kts index 95d02740..3cb6cf55 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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")) - } - } -} \ No newline at end of file +//if(file("../dataforge-core").exists()) { +// includeBuild("../dataforge-core"){ +// dependencySubstitution { +// substitute(module("hep.dataforge:dataforge-output")).with(project(":dataforge-output")) +// } +// } +//} \ No newline at end of file