Adjust property editors
This commit is contained in:
parent
787c841a51
commit
8d21d3cd74
@ -1,18 +1,13 @@
|
||||
plugins {
|
||||
id("ru.mipt.npm.gradle.project")
|
||||
|
||||
//Override kotlin version
|
||||
// val kotlinVersion = "1.5.20-RC"
|
||||
// kotlin("multiplatform") version(kotlinVersion) apply false
|
||||
// kotlin("jvm") version(kotlinVersion) apply false
|
||||
// kotlin("js") version(kotlinVersion) apply false
|
||||
}
|
||||
|
||||
val dataforgeVersion by extra("0.5.0-dev-2")
|
||||
val dataforgeVersion by extra("0.5.0-dev-9")
|
||||
val fxVersion by extra("11")
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven("https://repo.kotlin.link")
|
||||
@ -29,7 +24,7 @@ subprojects {
|
||||
}
|
||||
}
|
||||
|
||||
ksciencePublish{
|
||||
ksciencePublish {
|
||||
github("visionforge")
|
||||
space()
|
||||
sonatype()
|
||||
|
@ -1,7 +1,7 @@
|
||||
package space.kscience.visionforge.gdml
|
||||
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.values.asValue
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.setProperty
|
||||
@ -24,7 +24,7 @@ class GDMLVisionTest {
|
||||
@Test
|
||||
fun testPrototypeProperty() {
|
||||
val vision = GdmlShowCase.cubes().toVision()
|
||||
val child = vision["composite-000.segment-0".toName()]
|
||||
val child = vision[Name.of("composite-000","segment-0")]
|
||||
assertNotNull(child)
|
||||
child.setProperty(SolidMaterial.MATERIAL_COLOR_KEY, "red".asValue())
|
||||
assertEquals("red", child.getProperty(SolidMaterial.MATERIAL_COLOR_KEY).string)
|
||||
|
@ -8,8 +8,8 @@ import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.gdml.GdmlShowCase
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.describedProperties
|
||||
import space.kscience.visionforge.editor.VisualObjectEditorFragment
|
||||
import space.kscience.visionforge.editor.VisualObjectTreeFragment
|
||||
import space.kscience.visionforge.editor.VisionEditorFragment
|
||||
import space.kscience.visionforge.editor.VisionTreeFragment
|
||||
import space.kscience.visionforge.gdml.toVision
|
||||
import space.kscience.visionforge.solid.FX3DPlugin
|
||||
import space.kscience.visionforge.solid.FXCanvas3D
|
||||
@ -29,25 +29,24 @@ class GDMLView : View() {
|
||||
private val visionManager = context.fetch(VisionManager)
|
||||
private val canvas = FXCanvas3D(fx3d)
|
||||
|
||||
private val treeFragment = VisualObjectTreeFragment().apply {
|
||||
private val treeFragment = VisionTreeFragment().apply {
|
||||
this.itemProperty.bind(canvas.rootObjectProperty)
|
||||
}
|
||||
|
||||
private val propertyEditor = VisualObjectEditorFragment {
|
||||
private val propertyEditor = VisionEditorFragment {
|
||||
it.describedProperties
|
||||
}.apply {
|
||||
descriptorProperty.set(SolidMaterial.descriptor)
|
||||
itemProperty.bind(treeFragment.selectedProperty)
|
||||
}
|
||||
|
||||
|
||||
override val root: Parent = borderpane {
|
||||
top {
|
||||
buttonbar {
|
||||
button("Load GDML/json") {
|
||||
action {
|
||||
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
|
||||
if(file!= null) {
|
||||
if (file != null) {
|
||||
runAsync {
|
||||
visionManager.readFile(file) as Solid
|
||||
} ui {
|
||||
|
@ -6,6 +6,7 @@ import ru.mipt.npm.muon.monitor.Monitor.UPPER_LAYER_Z
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import space.kscience.visionforge.removeAll
|
||||
import space.kscience.visionforge.root
|
||||
import space.kscience.visionforge.setProperty
|
||||
import space.kscience.visionforge.solid.*
|
||||
import kotlin.math.PI
|
||||
|
||||
|
@ -67,6 +67,7 @@ kotlin {
|
||||
val jvmMain by getting{
|
||||
dependencies {
|
||||
api(project(":visionforge-server"))
|
||||
api(project(":visionforge-markdown"))
|
||||
api("ch.qos.logback:logback-classic:1.2.3")
|
||||
implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
|
||||
}
|
||||
|
91
demo/playground/src/jvmMain/kotlin/markdownDemo.kt
Normal file
91
demo/playground/src/jvmMain/kotlin/markdownDemo.kt
Normal file
@ -0,0 +1,91 @@
|
||||
package space.kscience.visionforge.examples
|
||||
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.h1
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.plotly.layout
|
||||
import space.kscience.plotly.models.ScatterMode
|
||||
import space.kscience.plotly.models.TextPosition
|
||||
import space.kscience.plotly.scatter
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.markup.markdown
|
||||
import space.kscience.visionforge.plotly.PlotlyPlugin
|
||||
import space.kscience.visionforge.plotly.plotly
|
||||
import space.kscience.visionforge.solid.*
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
plugin(Solids)
|
||||
plugin(PlotlyPlugin)
|
||||
}
|
||||
|
||||
context.makeVisionFile(resourceLocation = ResourceLocation.SYSTEM) {
|
||||
markdown {
|
||||
//language=markdown
|
||||
"""
|
||||
# Section
|
||||
|
||||
**TBD**
|
||||
|
||||
## Subsection
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
div {
|
||||
h1 { +"Canvas" }
|
||||
vision("canvas") {
|
||||
solid {
|
||||
box(100, 100, 100)
|
||||
material {
|
||||
emissiveColor("red")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vision("plot") {
|
||||
plotly {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.markers
|
||||
name = "Team A"
|
||||
text("A-1", "A-2", "A-3", "A-4", "A-5")
|
||||
textposition = TextPosition.`top center`
|
||||
textfont {
|
||||
family = "Raleway, sans-serif"
|
||||
}
|
||||
marker { size = 12 }
|
||||
}
|
||||
|
||||
scatter {
|
||||
x(2, 3, 4, 5)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.lines
|
||||
name = "Team B"
|
||||
text("B-a", "B-b", "B-c", "B-d", "B-e")
|
||||
textposition = TextPosition.`bottom center`
|
||||
textfont {
|
||||
family = "Times New Roman"
|
||||
}
|
||||
marker { size = 12 }
|
||||
}
|
||||
|
||||
layout {
|
||||
title = "Data Labels Hover"
|
||||
xaxis {
|
||||
range(0.75..5.25)
|
||||
}
|
||||
legend {
|
||||
y = 0.5
|
||||
font {
|
||||
family = "Arial, sans-serif"
|
||||
size = 20
|
||||
color("grey")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ public fun Context.makeVisionFile(
|
||||
content: VisionTagConsumer<*>.() -> Unit
|
||||
): Unit {
|
||||
val actualPath = page(title, content = content).makeFile(path) { actualPath ->
|
||||
mapOf("threeJs" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath))
|
||||
mapOf("playground" to scriptHeader("js/visionforge-playground.js", resourceLocation, actualPath))
|
||||
}
|
||||
if (show) Desktop.getDesktop().browse(actualPath.toFile().toURI())
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ package space.kscience.visionforge.examples
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.visionforge.html.ResourceLocation
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
import space.kscience.visionforge.solid.box
|
||||
import space.kscience.visionforge.solid.solid
|
||||
import space.kscience.visionforge.solid.*
|
||||
|
||||
fun main() {
|
||||
val context = Context {
|
||||
@ -15,6 +13,9 @@ fun main() {
|
||||
vision("canvas") {
|
||||
solid {
|
||||
box(100, 100, 100)
|
||||
material {
|
||||
emissiveColor("red")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.h1
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.three.server.*
|
||||
import space.kscience.visionforge.visionManager
|
||||
@ -42,7 +42,7 @@ fun main() {
|
||||
val randomLayer = Random.nextInt(1, 11)
|
||||
val randomI = Random.nextInt(1, 4)
|
||||
val randomJ = Random.nextInt(1, 4)
|
||||
val target = "layer[$randomLayer].segment[$randomI,$randomJ]".toName()
|
||||
val target = Name.parse("layer[$randomLayer].segment[$randomI,$randomJ]")
|
||||
val targetVision = sat[target] as Solid
|
||||
targetVision.color("red")
|
||||
delay(1000)
|
||||
|
@ -1,7 +1,8 @@
|
||||
package space.kscience.visionforge
|
||||
package space.kscience.visionforge.solid.demo
|
||||
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Vision
|
||||
|
||||
public interface VisionLayout<in V: Vision> {
|
||||
public fun render(name: Name, vision: V, meta: Meta = Meta.EMPTY)
|
@ -6,9 +6,8 @@ import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.invoke
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.VisionLayout
|
||||
import space.kscience.visionforge.solid.*
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
import space.kscience.visionforge.visible
|
||||
@ -23,7 +22,7 @@ fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGro
|
||||
"title" put title
|
||||
}
|
||||
val vision = SolidGroup(block)
|
||||
render(name.toName(), vision)
|
||||
render(Name.parse(name), vision)
|
||||
}
|
||||
|
||||
val canvasOptions = Canvas3DOptions {
|
||||
|
@ -15,7 +15,6 @@ import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.VisionLayout
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.three.ThreeCanvas
|
||||
import space.kscience.visionforge.solid.three.ThreePlugin
|
||||
|
@ -7,7 +7,6 @@ import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.VisionLayout
|
||||
import space.kscience.visionforge.solid.FX3DPlugin
|
||||
import space.kscience.visionforge.solid.FXCanvas3D
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
|
@ -2,12 +2,14 @@ package space.kscience.visionforge.demo
|
||||
|
||||
import javafx.geometry.Orientation
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.asConfig
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.node
|
||||
import space.kscience.dataforge.meta.descriptors.value
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.visionforge.editor.ConfigEditor
|
||||
import space.kscience.visionforge.editor.FXMeta
|
||||
import space.kscience.visionforge.editor.FXMetaModel
|
||||
import space.kscience.visionforge.editor.MetaViewer
|
||||
import space.kscience.visionforge.editor.MutableMetaEditor
|
||||
import tornadofx.*
|
||||
|
||||
|
||||
@ -15,7 +17,7 @@ class MetaEditorDemoApp : App(MetaEditorDemo::class)
|
||||
|
||||
class MetaEditorDemo : View("Meta editor demo") {
|
||||
|
||||
val meta = Meta {
|
||||
val meta = MutableMeta {
|
||||
"aNode" put {
|
||||
"innerNode" put {
|
||||
"innerValue" put true
|
||||
@ -23,18 +25,16 @@ class MetaEditorDemo : View("Meta editor demo") {
|
||||
"b" put 223
|
||||
"c" put "StringValue"
|
||||
}
|
||||
}.asConfig()
|
||||
}
|
||||
|
||||
val descriptor = NodeDescriptor {
|
||||
val descriptor = MetaDescriptor {
|
||||
node("aNode") {
|
||||
info = "A root demo node"
|
||||
value("b") {
|
||||
value("b", ValueType.NUMBER) {
|
||||
info = "b number value"
|
||||
type(ValueType.NUMBER)
|
||||
}
|
||||
node("otherNode") {
|
||||
value("otherValue") {
|
||||
type(ValueType.BOOLEAN)
|
||||
value("otherValue", ValueType.BOOLEAN) {
|
||||
default(false)
|
||||
info = "default value"
|
||||
}
|
||||
@ -46,12 +46,13 @@ class MetaEditorDemo : View("Meta editor demo") {
|
||||
}
|
||||
}
|
||||
|
||||
private val rootNode = FXMeta.root(meta, descriptor)
|
||||
private val rootNode = FXMetaModel.root(meta, descriptor)
|
||||
|
||||
override val root =
|
||||
splitpane(Orientation.HORIZONTAL, MetaViewer(rootNode).root, ConfigEditor(
|
||||
rootNode
|
||||
).root)
|
||||
override val root = splitpane(
|
||||
Orientation.HORIZONTAL,
|
||||
MetaViewer(rootNode as Meta).root,
|
||||
MutableMetaEditor(rootNode as FXMetaModel<MutableMeta>).root
|
||||
)
|
||||
}
|
||||
|
||||
fun main() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
## Library design
|
||||
The central point of the library design is the `Vision` interface. The `Vision` stores an optional reference to its parent and is able to store a number of mutable or read-only properties. Each property is represented by its `Name`, and a `MetaItem` value-tree, both following DataForge library specification (discussed in the [Appendix](appendix.md)). The `Vision` objects are organized in a tree using `VisionGroup` as nodes. `VisionGroup` additionally to all `Vision` properties holds a `children` container that holds named references to its direct children `Vision`s. Thus, `Vision`s form a doubly linked tree (a parent stores references to all its children and children store a reference to the parent).
|
||||
The central point of the library design is the `Vision` interface. The `Vision` stores an optional reference to its parent and is able to store a number of mutable or read-only properties. Each property is represented by its `Name`, and a `Meta` value-tree, both following DataForge library specification (discussed in the [Appendix](appendix.md)). The `Vision` objects are organized in a tree using `VisionGroup` as nodes. `VisionGroup` additionally to all `Vision` properties holds a `children` container that holds named references to its direct children `Vision`s. Thus, `Vision`s form a doubly linked tree (a parent stores references to all its children and children store a reference to the parent).
|
||||
|
||||
An important concept using in the VisionForge is the property layering mechanism. It means that if the property with a given name is not found in the `Vision` it is requested from, it could be requested from the parent `Vision`, form the style declaration, the prototype for the vision or any other place defined by the component author. For example, let's take a `color` attribute used in 3D visualization. When one draws a group of objects, he usually wants to make the color of all objects in the group to be defined by a single handle in the group common ancestor. So when the parent color changes, all children color must follow suite, but we also want to change children color individually without changing the parent. In this case two property layers are defined:
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
interface Vision{
|
||||
val parent: Vision?
|
||||
fun getProperty(name): MetaItem?
|
||||
fun getProperty(name): Meta?
|
||||
fun setProperty(name, value)
|
||||
}
|
||||
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@ -1,6 +1,6 @@
|
||||
pluginManagement {
|
||||
|
||||
val toolsVersion = "0.10.0"
|
||||
val toolsVersion = "0.10.2"
|
||||
|
||||
repositories {
|
||||
maven("https://repo.kotlin.link")
|
||||
|
@ -11,5 +11,5 @@ dependencies {
|
||||
implementation(npm("file-saver", "2.0.2"))
|
||||
implementation(npm("bootstrap","4.6.0"))
|
||||
implementation(npm("jquery","3.5.1"))
|
||||
implementation(npm("popper.js","1.16.1"))
|
||||
implementation(npm("@popperjs/core","2.9.3"))
|
||||
}
|
@ -12,6 +12,7 @@ import org.w3c.files.BlobPropertyBag
|
||||
import react.*
|
||||
import react.dom.attrs
|
||||
import react.dom.button
|
||||
import space.kscience.dataforge.meta.descriptors.defaultNode
|
||||
import space.kscience.dataforge.meta.withDefault
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.react.flexColumn
|
||||
@ -43,7 +44,7 @@ public external interface CanvasControlsProps : RProps {
|
||||
public var vision: Vision?
|
||||
}
|
||||
|
||||
public val CanvasControls: FunctionalComponent<CanvasControlsProps> = functionalComponent("CanvasControls") { props ->
|
||||
public val CanvasControls: FunctionComponent<CanvasControlsProps> = functionalComponent("CanvasControls") { props ->
|
||||
flexColumn {
|
||||
flexRow {
|
||||
css {
|
||||
@ -66,7 +67,7 @@ public val CanvasControls: FunctionalComponent<CanvasControlsProps> = functional
|
||||
}
|
||||
propertyEditor(
|
||||
ownProperties = props.canvasOptions,
|
||||
allProperties = props.canvasOptions.withDefault(Canvas3DOptions.descriptor.defaultMeta),
|
||||
allProperties = props.canvasOptions.meta.withDefault(Canvas3DOptions.descriptor.defaultNode),
|
||||
descriptor = Canvas3DOptions.descriptor,
|
||||
expanded = false
|
||||
)
|
||||
|
@ -18,7 +18,7 @@ public external class TabProps : RProps {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val Tab: FunctionalComponent<TabProps> = functionalComponent { props ->
|
||||
public val Tab: FunctionComponent<TabProps> = functionalComponent { props ->
|
||||
props.children()
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ public external class TabPaneProps : RProps {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val TabPane: FunctionalComponent<TabPaneProps> = functionalComponent("TabPane") { props ->
|
||||
public val TabPane: FunctionComponent<TabPaneProps> = functionalComponent("TabPane") { props ->
|
||||
var activeTab: String? by useState(props.activeTab)
|
||||
|
||||
val children: Array<out ReactElement?> = Children.map(props.children) {
|
||||
|
@ -3,22 +3,25 @@ package space.kscience.visionforge.bootstrap
|
||||
import org.w3c.dom.Element
|
||||
import react.RBuilder
|
||||
import react.dom.render
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.getStyle
|
||||
import space.kscience.visionforge.meta
|
||||
import space.kscience.visionforge.react.metaViewer
|
||||
import space.kscience.visionforge.react.propertyEditor
|
||||
import space.kscience.visionforge.solid.SolidReference
|
||||
import space.kscience.visionforge.styles
|
||||
|
||||
public fun RBuilder.visionPropertyEditor(
|
||||
vision: Vision,
|
||||
descriptor: NodeDescriptor? = vision.descriptor,
|
||||
descriptor: MetaDescriptor? = vision.descriptor,
|
||||
key: Any? = null,
|
||||
) {
|
||||
|
||||
card("Properties") {
|
||||
propertyEditor(
|
||||
ownProperties = vision.ownProperties,
|
||||
allProperties = vision.allProperties(),
|
||||
ownProperties = vision.meta(false,false,false),
|
||||
allProperties = vision.meta(),
|
||||
updateFlow = vision.propertyChanges,
|
||||
descriptor = descriptor,
|
||||
key = key
|
||||
@ -47,7 +50,7 @@ public fun RBuilder.visionPropertyEditor(
|
||||
|
||||
public fun Element.visionPropertyEditor(
|
||||
item: Vision,
|
||||
descriptor: NodeDescriptor? = item.descriptor,
|
||||
descriptor: MetaDescriptor? = item.descriptor,
|
||||
): Unit = render(this) {
|
||||
visionPropertyEditor(item, descriptor = descriptor)
|
||||
}
|
@ -8,12 +8,11 @@ import react.*
|
||||
import react.dom.a
|
||||
import react.dom.attrs
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaItemNode
|
||||
import space.kscience.dataforge.meta.MetaItemValue
|
||||
import space.kscience.dataforge.meta.descriptors.ItemDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.defaultNode
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.isLeaf
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.lastOrNull
|
||||
@ -41,18 +40,19 @@ public external interface MetaViewerProps : RProps {
|
||||
/**
|
||||
* Root descriptor
|
||||
*/
|
||||
public var descriptor: NodeDescriptor?
|
||||
public var descriptor: MetaDescriptor?
|
||||
}
|
||||
|
||||
private val MetaViewerItem: FunctionalComponent<MetaViewerProps> = functionalComponent("MetaViewerItem") { props ->
|
||||
private val MetaViewerItem: FunctionComponent<MetaViewerProps> = functionalComponent("MetaViewerItem") { props ->
|
||||
metaViewerItem(props)
|
||||
}
|
||||
|
||||
private fun RBuilder.metaViewerItem(props: MetaViewerProps) {
|
||||
var expanded: Boolean by useState { true }
|
||||
val item = props.root[props.name]
|
||||
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
||||
val actualItem = item ?: descriptorItem?.defaultValue
|
||||
val descriptorItem: MetaDescriptor? = props.descriptor?.get(props.name)
|
||||
val actualValue = item?.value ?: descriptorItem?.defaultValue
|
||||
val actualMeta = item ?: descriptorItem?.defaultNode
|
||||
|
||||
val token = props.name.lastOrNull()?.toString() ?: props.rootName ?: ""
|
||||
|
||||
@ -60,90 +60,75 @@ private fun RBuilder.metaViewerItem(props: MetaViewerProps) {
|
||||
expanded = !expanded
|
||||
}
|
||||
|
||||
when (actualItem) {
|
||||
is MetaItemNode -> {
|
||||
flexRow {
|
||||
flexRow {
|
||||
css {
|
||||
alignItems = Align.center
|
||||
}
|
||||
if (actualMeta?.isLeaf == false) {
|
||||
styledSpan {
|
||||
css {
|
||||
alignItems = Align.center
|
||||
}
|
||||
styledSpan {
|
||||
css {
|
||||
+TreeStyles.treeCaret
|
||||
if (expanded) {
|
||||
+TreeStyles.treeCaredDown
|
||||
}
|
||||
}
|
||||
attrs {
|
||||
onClickFunction = expanderClick
|
||||
+TreeStyles.treeCaret
|
||||
if (expanded) {
|
||||
+TreeStyles.treeCaredDown
|
||||
}
|
||||
}
|
||||
styledSpan {
|
||||
css {
|
||||
+TreeStyles.treeLabel
|
||||
if (item == null) {
|
||||
+TreeStyles.treeLabelInactive
|
||||
}
|
||||
}
|
||||
+token
|
||||
}
|
||||
}
|
||||
if (expanded) {
|
||||
flexColumn {
|
||||
css {
|
||||
+TreeStyles.tree
|
||||
}
|
||||
val keys = buildSet {
|
||||
(descriptorItem as? NodeDescriptor)?.items?.keys?.forEach {
|
||||
add(NameToken(it))
|
||||
}
|
||||
actualItem.node.items.keys.let { addAll(it) }
|
||||
}
|
||||
|
||||
keys.filter { !it.body.startsWith("@") }.forEach { token ->
|
||||
styledDiv {
|
||||
css {
|
||||
+TreeStyles.treeItem
|
||||
}
|
||||
child(MetaViewerItem) {
|
||||
attrs {
|
||||
this.key = props.name.toString()
|
||||
this.root = props.root
|
||||
this.name = props.name + token
|
||||
this.descriptor = props.descriptor
|
||||
}
|
||||
}
|
||||
//configEditor(props.root, props.name + token, props.descriptor, props.default)
|
||||
}
|
||||
}
|
||||
attrs {
|
||||
onClickFunction = expanderClick
|
||||
}
|
||||
}
|
||||
}
|
||||
is MetaItemValue -> {
|
||||
flexRow {
|
||||
css {
|
||||
alignItems = Align.center
|
||||
|
||||
styledSpan {
|
||||
css {
|
||||
+TreeStyles.treeLabel
|
||||
if (item == null) {
|
||||
+TreeStyles.treeLabelInactive
|
||||
}
|
||||
styledSpan {
|
||||
}
|
||||
+token
|
||||
}
|
||||
styledDiv {
|
||||
a {
|
||||
+actualValue.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (expanded) {
|
||||
flexColumn {
|
||||
css {
|
||||
+TreeStyles.tree
|
||||
}
|
||||
val keys = buildSet {
|
||||
descriptorItem?.children?.keys?.forEach {
|
||||
add(NameToken(it))
|
||||
}
|
||||
actualMeta!!.items.keys.let { addAll(it) }
|
||||
}
|
||||
|
||||
keys.filter { !it.body.startsWith("@") }.forEach { token ->
|
||||
styledDiv {
|
||||
css {
|
||||
+TreeStyles.treeLabel
|
||||
if (item == null) {
|
||||
+TreeStyles.treeLabelInactive
|
||||
+TreeStyles.treeItem
|
||||
}
|
||||
child(MetaViewerItem) {
|
||||
attrs {
|
||||
this.key = props.name.toString()
|
||||
this.root = props.root
|
||||
this.name = props.name + token
|
||||
this.descriptor = props.descriptor
|
||||
}
|
||||
}
|
||||
+token
|
||||
}
|
||||
styledDiv {
|
||||
a {
|
||||
+actualItem.value.toString()
|
||||
}
|
||||
//configEditor(props.root, props.name + token, props.descriptor, props.default)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val MetaViewer: FunctionalComponent<MetaViewerProps> =
|
||||
public val MetaViewer: FunctionComponent<MetaViewerProps> =
|
||||
functionalComponent<MetaViewerProps>("MetaViewer") { props ->
|
||||
child(MetaViewerItem) {
|
||||
attrs {
|
||||
@ -155,7 +140,7 @@ public val MetaViewer: FunctionalComponent<MetaViewerProps> =
|
||||
}
|
||||
}
|
||||
|
||||
public fun RBuilder.metaViewer(meta: Meta, descriptor: NodeDescriptor? = null, key: Any? = null) {
|
||||
public fun RBuilder.metaViewer(meta: Meta, descriptor: MetaDescriptor? = null, key: Any? = null) {
|
||||
child(MetaViewer) {
|
||||
attrs {
|
||||
this.key = key?.toString() ?: ""
|
||||
|
@ -5,23 +5,23 @@ import org.w3c.dom.HTMLOptionElement
|
||||
import org.w3c.dom.HTMLSelectElement
|
||||
import org.w3c.dom.asList
|
||||
import org.w3c.dom.events.Event
|
||||
import react.FunctionalComponent
|
||||
import react.FunctionComponent
|
||||
import react.dom.attrs
|
||||
import react.dom.option
|
||||
import react.dom.select
|
||||
import react.functionalComponent
|
||||
import react.useState
|
||||
import space.kscience.dataforge.meta.value
|
||||
import space.kscience.dataforge.meta.descriptors.allowedValues
|
||||
import space.kscience.dataforge.values.asValue
|
||||
import space.kscience.dataforge.values.string
|
||||
|
||||
@JsExport
|
||||
public val MultiSelectChooser: FunctionalComponent<ValueChooserProps> =
|
||||
public val MultiSelectChooser: FunctionComponent<ValueChooserProps> =
|
||||
functionalComponent("MultiSelectChooser") { props ->
|
||||
var selectedItems by useState { props.item.value?.list ?: emptyList() }
|
||||
var selectedItems by useState { props.item?.value?.list ?: emptyList() }
|
||||
|
||||
val onChange: (Event) -> Unit = { event: Event ->
|
||||
val newSelected= (event.target as HTMLSelectElement).selectedOptions.asList()
|
||||
val newSelected = (event.target as HTMLSelectElement).selectedOptions.asList()
|
||||
.map { (it as HTMLOptionElement).value.asValue() }
|
||||
props.valueChanged?.invoke(newSelected.asValue())
|
||||
selectedItems = newSelected
|
||||
|
@ -18,14 +18,10 @@ import react.*
|
||||
import react.dom.attrs
|
||||
import react.dom.render
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.ItemDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.ValueRequirement
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.lastOrNull
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.dataforge.values.Value
|
||||
import space.kscience.visionforge.hidden
|
||||
import styled.css
|
||||
@ -38,12 +34,12 @@ public external interface PropertyEditorProps : RProps {
|
||||
/**
|
||||
* Root config object - always non null
|
||||
*/
|
||||
public var ownProperties: MutableItemProvider
|
||||
public var ownProperties: MutableMetaProvider
|
||||
|
||||
/**
|
||||
* Provide default item (greyed out if used)
|
||||
*/
|
||||
public var allProperties: ItemProvider?
|
||||
public var allProperties: MetaProvider?
|
||||
|
||||
/**
|
||||
* Full path to the displayed node in [ownProperties]. Could be empty
|
||||
@ -53,7 +49,7 @@ public external interface PropertyEditorProps : RProps {
|
||||
/**
|
||||
* Root descriptor
|
||||
*/
|
||||
public var descriptor: NodeDescriptor?
|
||||
public var descriptor: MetaDescriptor?
|
||||
|
||||
/**
|
||||
* A coroutine scope for updates
|
||||
@ -71,21 +67,21 @@ public external interface PropertyEditorProps : RProps {
|
||||
public var expanded: Boolean?
|
||||
}
|
||||
|
||||
private val PropertyEditorItem: FunctionalComponent<PropertyEditorProps> =
|
||||
private val PropertyEditorItem: FunctionComponent<PropertyEditorProps> =
|
||||
functionalComponent("ConfigEditorItem") { props ->
|
||||
propertyEditorItem(props)
|
||||
}
|
||||
|
||||
private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
||||
var expanded: Boolean by useState { props.expanded ?: true }
|
||||
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
||||
var ownProperty: MetaItem? by useState { props.ownProperties.getItem(props.name) }
|
||||
val actualItem: MetaItem? = props.allProperties?.getItem(props.name)
|
||||
val descriptor: MetaDescriptor? = props.descriptor?.get(props.name)
|
||||
var ownProperty: Meta? by useState { props.ownProperties.getMeta(props.name) }
|
||||
val actualMeta = props.allProperties?.getMeta(props.name)
|
||||
|
||||
val token = props.name.lastOrNull()?.toString() ?: "Properties"
|
||||
|
||||
fun update() {
|
||||
ownProperty = props.ownProperties.getItem(props.name)
|
||||
ownProperty = props.ownProperties.getMeta(props.name)
|
||||
}
|
||||
|
||||
if (props.updateFlow != null) {
|
||||
@ -109,7 +105,7 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
||||
if (it == null) {
|
||||
props.ownProperties.remove(props.name)
|
||||
} else {
|
||||
props.ownProperties[props.name] = it
|
||||
props.ownProperties.setValue(props.name, it)
|
||||
}
|
||||
update()
|
||||
}
|
||||
@ -119,19 +115,20 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
||||
update()
|
||||
}
|
||||
|
||||
if (actualItem is MetaItemNode) {
|
||||
val keys = buildSet {
|
||||
(descriptorItem as? NodeDescriptor)?.items?.filterNot {
|
||||
it.key.startsWith("@") || it.value.hidden
|
||||
}?.forEach {
|
||||
add(NameToken(it.key))
|
||||
}
|
||||
ownProperty?.node?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) }
|
||||
val keys = buildSet {
|
||||
descriptor?.children?.filterNot {
|
||||
it.key.startsWith("@") || it.value.hidden
|
||||
}?.forEach {
|
||||
add(NameToken(it.key))
|
||||
}
|
||||
// Do not show nodes without visible children
|
||||
if (keys.isEmpty()) return
|
||||
ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) }
|
||||
}
|
||||
|
||||
flexRow {
|
||||
flexRow {
|
||||
css {
|
||||
alignItems = Align.center
|
||||
}
|
||||
if(keys.isNotEmpty()) {
|
||||
styledSpan {
|
||||
css {
|
||||
+TreeStyles.treeCaret
|
||||
@ -143,55 +140,17 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
||||
onClickFunction = expanderClick
|
||||
}
|
||||
}
|
||||
styledSpan {
|
||||
css {
|
||||
+TreeStyles.treeLabel
|
||||
if (ownProperty == null) {
|
||||
+TreeStyles.treeLabelInactive
|
||||
}
|
||||
}
|
||||
+token
|
||||
}
|
||||
}
|
||||
if (expanded) {
|
||||
flexColumn {
|
||||
css {
|
||||
+TreeStyles.tree
|
||||
}
|
||||
keys.forEach { token ->
|
||||
styledDiv {
|
||||
css {
|
||||
+TreeStyles.treeItem
|
||||
}
|
||||
child(PropertyEditorItem) {
|
||||
attrs {
|
||||
this.key = props.name.toString()
|
||||
this.ownProperties = props.ownProperties
|
||||
this.allProperties = props.allProperties
|
||||
this.name = props.name + token
|
||||
this.descriptor = props.descriptor
|
||||
}
|
||||
}
|
||||
//configEditor(props.root, props.name + token, props.descriptor, props.default)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
flexRow {
|
||||
styledSpan {
|
||||
css {
|
||||
alignItems = Align.center
|
||||
}
|
||||
styledSpan {
|
||||
css {
|
||||
+TreeStyles.treeLabel
|
||||
if (ownProperty == null) {
|
||||
+TreeStyles.treeLabelInactive
|
||||
}
|
||||
+TreeStyles.treeLabel
|
||||
if (ownProperty == null) {
|
||||
+TreeStyles.treeLabelInactive
|
||||
}
|
||||
+token
|
||||
}
|
||||
|
||||
+token
|
||||
}
|
||||
if(!props.name.isEmpty() && descriptor?.valueRequirement != ValueRequirement.ABSENT) {
|
||||
styledDiv {
|
||||
css {
|
||||
//+TreeStyles.resizeableInput
|
||||
@ -200,8 +159,8 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
||||
}
|
||||
valueChooser(
|
||||
props.name,
|
||||
actualItem,
|
||||
descriptorItem as? ValueDescriptor,
|
||||
actualMeta,
|
||||
descriptor,
|
||||
valueChanged
|
||||
)
|
||||
}
|
||||
@ -232,14 +191,38 @@ private fun RBuilder.propertyEditorItem(props: PropertyEditorProps) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (expanded) {
|
||||
flexColumn {
|
||||
css {
|
||||
+TreeStyles.tree
|
||||
}
|
||||
keys.forEach { token ->
|
||||
styledDiv {
|
||||
css {
|
||||
+TreeStyles.treeItem
|
||||
}
|
||||
child(PropertyEditorItem) {
|
||||
attrs {
|
||||
this.key = props.name.toString()
|
||||
this.ownProperties = props.ownProperties
|
||||
this.allProperties = props.allProperties
|
||||
this.name = props.name + token
|
||||
this.descriptor = props.descriptor
|
||||
}
|
||||
}
|
||||
//configEditor(props.root, props.name + token, props.descriptor, props.default)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@JsExport
|
||||
public val PropertyEditor: FunctionalComponent<PropertyEditorProps> = functionalComponent("PropertyEditor") { props ->
|
||||
public val PropertyEditor: FunctionComponent<PropertyEditorProps> = functionalComponent("PropertyEditor") { props ->
|
||||
child(PropertyEditorItem) {
|
||||
attrs {
|
||||
this.key = ""
|
||||
@ -254,10 +237,10 @@ public val PropertyEditor: FunctionalComponent<PropertyEditorProps> = functional
|
||||
}
|
||||
|
||||
public fun RBuilder.propertyEditor(
|
||||
ownProperties: MutableItemProvider,
|
||||
allProperties: ItemProvider? = ownProperties,
|
||||
ownProperties: MutableMetaProvider,
|
||||
allProperties: MetaProvider? = ownProperties,
|
||||
updateFlow: Flow<Name>? = null,
|
||||
descriptor: NodeDescriptor? = null,
|
||||
descriptor: MetaDescriptor? = null,
|
||||
scope: CoroutineScope? = null,
|
||||
key: Any? = null,
|
||||
expanded: Boolean? = null
|
||||
@ -276,8 +259,8 @@ public fun RBuilder.propertyEditor(
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun Config.flowUpdates(): Flow<Name> = callbackFlow {
|
||||
onChange(this) { name, _, _ ->
|
||||
private fun ObservableMutableMeta.flowUpdates(): Flow<Name> = callbackFlow {
|
||||
onChange(this) { name ->
|
||||
launch {
|
||||
send(name)
|
||||
}
|
||||
@ -289,16 +272,16 @@ private fun Config.flowUpdates(): Flow<Name> = callbackFlow {
|
||||
|
||||
|
||||
public fun RBuilder.configEditor(
|
||||
config: Config,
|
||||
default: ItemProvider? = null,
|
||||
descriptor: NodeDescriptor? = null,
|
||||
config: ObservableMutableMeta,
|
||||
default: MetaProvider? = null,
|
||||
descriptor: MetaDescriptor? = null,
|
||||
key: Any? = null,
|
||||
scope: CoroutineScope? = null,
|
||||
): Unit = propertyEditor(config, default, config.flowUpdates(), descriptor, scope, key = key)
|
||||
|
||||
public fun Element.configEditor(
|
||||
config: Config,
|
||||
descriptor: NodeDescriptor? = null,
|
||||
config: ObservableMutableMeta,
|
||||
descriptor: MetaDescriptor? = null,
|
||||
default: Meta? = null,
|
||||
key: Any? = null,
|
||||
scope: CoroutineScope? = null,
|
||||
|
@ -6,7 +6,7 @@ import kotlinx.html.InputType
|
||||
import kotlinx.html.js.onChangeFunction
|
||||
import org.w3c.dom.HTMLInputElement
|
||||
import org.w3c.dom.events.Event
|
||||
import react.FunctionalComponent
|
||||
import react.FunctionComponent
|
||||
import react.dom.attrs
|
||||
import react.functionalComponent
|
||||
import react.useState
|
||||
@ -18,7 +18,7 @@ import styled.css
|
||||
import styled.styledInput
|
||||
|
||||
@JsExport
|
||||
public val RangeValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
public val RangeValueChooser: FunctionComponent<ValueChooserProps> =
|
||||
functionalComponent("RangeValueChooser") { props ->
|
||||
var innerValue by useState(props.item.double)
|
||||
var rangeDisabled: Boolean by useState(props.item == null)
|
||||
|
@ -21,7 +21,7 @@ public external interface ThreeCanvasProps : RProps {
|
||||
public var selected: Name?
|
||||
}
|
||||
|
||||
public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functionalComponent(
|
||||
public val ThreeCanvasComponent: FunctionComponent<ThreeCanvasProps> = functionalComponent(
|
||||
"ThreeCanvasComponent"
|
||||
) { props ->
|
||||
val elementRef = useRef<Element>(null)
|
||||
|
@ -107,7 +107,7 @@ private fun RBuilder.visionTree(props: ObjectTreeProps): Unit {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ObjectTree: FunctionalComponent<ObjectTreeProps> = functionalComponent("ObjectTree") { props ->
|
||||
public val ObjectTree: FunctionComponent<ObjectTreeProps> = functionalComponent("ObjectTree") { props ->
|
||||
visionTree(props)
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,12 @@ import org.w3c.dom.events.Event
|
||||
import react.*
|
||||
import react.dom.attrs
|
||||
import react.dom.option
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.boolean
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.allowedValues
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.values.*
|
||||
import space.kscience.visionforge.Colors
|
||||
@ -24,14 +28,15 @@ import styled.styledInput
|
||||
import styled.styledSelect
|
||||
|
||||
public external interface ValueChooserProps : RProps {
|
||||
public var item: MetaItem?
|
||||
public var descriptor: ValueDescriptor?
|
||||
public var item: Meta?
|
||||
public var descriptor: MetaDescriptor?
|
||||
|
||||
//public var nullable: Boolean?
|
||||
public var valueChanged: ((Value?) -> Unit)?
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val StringValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
public val StringValueChooser: FunctionComponent<ValueChooserProps> =
|
||||
functionalComponent("StringValueChooser") { props ->
|
||||
var value by useState(props.item.string ?: "")
|
||||
val keyDown: (Event) -> Unit = { event ->
|
||||
@ -46,7 +51,7 @@ public val StringValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
value = (it.target as HTMLInputElement).value
|
||||
}
|
||||
styledInput(type = InputType.text) {
|
||||
css{
|
||||
css {
|
||||
width = 100.pct
|
||||
}
|
||||
attrs {
|
||||
@ -58,14 +63,14 @@ public val StringValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val BooleanValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
public val BooleanValueChooser: FunctionComponent<ValueChooserProps> =
|
||||
functionalComponent("BooleanValueChooser") { props ->
|
||||
val handleChange: (Event) -> Unit = {
|
||||
val newValue = (it.target as HTMLInputElement).checked
|
||||
props.valueChanged?.invoke(newValue.asValue())
|
||||
}
|
||||
styledInput(type = InputType.checkBox) {
|
||||
css{
|
||||
css {
|
||||
width = 100.pct
|
||||
}
|
||||
attrs {
|
||||
@ -77,7 +82,7 @@ public val BooleanValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val NumberValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
public val NumberValueChooser: FunctionComponent<ValueChooserProps> =
|
||||
functionalComponent("NumberValueChooser") { props ->
|
||||
var innerValue by useState(props.item.string ?: "")
|
||||
val keyDown: (Event) -> Unit = { event ->
|
||||
@ -95,7 +100,7 @@ public val NumberValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
innerValue = (it.target as HTMLInputElement).value
|
||||
}
|
||||
styledInput(type = InputType.number) {
|
||||
css{
|
||||
css {
|
||||
width = 100.pct
|
||||
}
|
||||
attrs {
|
||||
@ -116,7 +121,7 @@ public val NumberValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ComboValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
public val ComboValueChooser: FunctionComponent<ValueChooserProps> =
|
||||
functionalComponent("ComboValueChooser") { props ->
|
||||
var selected by useState(props.item.string ?: "")
|
||||
val handleChange: (Event) -> Unit = {
|
||||
@ -124,7 +129,7 @@ public val ComboValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
props.valueChanged?.invoke(selected.asValue())
|
||||
}
|
||||
styledSelect {
|
||||
css{
|
||||
css {
|
||||
width = 100.pct
|
||||
}
|
||||
props.descriptor?.allowedValues?.forEach {
|
||||
@ -141,10 +146,10 @@ public val ComboValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ColorValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
public val ColorValueChooser: FunctionComponent<ValueChooserProps> =
|
||||
functionalComponent("ColorValueChooser") { props ->
|
||||
var value by useState(
|
||||
props.item.value?.let { value ->
|
||||
props.item?.value?.let { value ->
|
||||
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
|
||||
else value.string
|
||||
} ?: "#000000"
|
||||
@ -154,7 +159,7 @@ public val ColorValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
props.valueChanged?.invoke(value.asValue())
|
||||
}
|
||||
styledInput(type = InputType.color) {
|
||||
css{
|
||||
css {
|
||||
width = 100.pct
|
||||
margin(0.px)
|
||||
}
|
||||
@ -166,11 +171,11 @@ public val ColorValueChooser: FunctionalComponent<ValueChooserProps> =
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ValueChooser: FunctionalComponent<ValueChooserProps> = functionalComponent("ValueChooser") { props ->
|
||||
public val ValueChooser: FunctionComponent<ValueChooserProps> = functionalComponent("ValueChooser") { props ->
|
||||
val rawInput by useState(false)
|
||||
|
||||
val descriptor = props.descriptor
|
||||
val type = descriptor?.type?.firstOrNull()
|
||||
val type = descriptor?.valueTypes?.firstOrNull()
|
||||
|
||||
when {
|
||||
rawInput -> child(StringValueChooser, props)
|
||||
@ -187,8 +192,8 @@ public val ValueChooser: FunctionalComponent<ValueChooserProps> = functionalComp
|
||||
|
||||
internal fun RBuilder.valueChooser(
|
||||
name: Name,
|
||||
item: MetaItem?,
|
||||
descriptor: ValueDescriptor? = null,
|
||||
item: Meta?,
|
||||
descriptor: MetaDescriptor? = null,
|
||||
callback: (Value?) -> Unit,
|
||||
) {
|
||||
child(ValueChooser) {
|
||||
|
@ -17,14 +17,13 @@ kotlin{
|
||||
|
||||
dependencies{
|
||||
api(project(":ui:react"))
|
||||
//TODO replace by kotlin-wrappers
|
||||
api("ru.mipt.npm:ring-ui:0.1.0")
|
||||
//api("ru.mipt.npm:ring-ui:0.1.0")
|
||||
api("org.jetbrains.kotlin-wrappers:kotlin-ring-ui")
|
||||
|
||||
implementation(npm("@jetbrains/icons", "3.14.1"))
|
||||
implementation(npm("@jetbrains/ring-ui", "4.0.7"))
|
||||
implementation(npm("core-js","3.12.1"))
|
||||
implementation(npm("file-saver", "2.0.2"))
|
||||
compileOnly(npm("url-loader","4.1.1"))
|
||||
compileOnly(npm("postcss-loader","5.2.0"))
|
||||
compileOnly(npm("source-map-loader","2.0.1"))
|
||||
// compileOnly(npm("url-loader","4.1.1"))
|
||||
// compileOnly(npm("postcss-loader","5.2.0"))
|
||||
}
|
@ -14,8 +14,7 @@ import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.isEmpty
|
||||
import space.kscience.dataforge.names.length
|
||||
import space.kscience.visionforge.VisionGroup
|
||||
import space.kscience.visionforge.allProperties
|
||||
import space.kscience.visionforge.ownProperties
|
||||
import space.kscience.visionforge.meta
|
||||
import space.kscience.visionforge.react.ThreeCanvasComponent
|
||||
import space.kscience.visionforge.react.flexColumn
|
||||
import space.kscience.visionforge.react.flexRow
|
||||
@ -70,7 +69,7 @@ public fun RBuilder.nameCrumbs(name: Name?, link: (Name) -> Unit): ReactElement
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ThreeCanvasWithControls: FunctionalComponent<ThreeCanvasWithControlsProps> =
|
||||
public val ThreeCanvasWithControls: FunctionComponent<ThreeCanvasWithControlsProps> =
|
||||
functionalComponent("ThreeViewWithControls") { props ->
|
||||
var selected by useState { props.selected }
|
||||
|
||||
@ -120,23 +119,23 @@ public val ThreeCanvasWithControls: FunctionalComponent<ThreeCanvasWithControlsP
|
||||
|
||||
selectedVision?.let { vision ->
|
||||
styledDiv {
|
||||
css{
|
||||
css {
|
||||
position = Position.absolute
|
||||
top = 5.px
|
||||
right = 5.px
|
||||
width = 450.px
|
||||
}
|
||||
Island{
|
||||
IslandHeader{
|
||||
Island {
|
||||
IslandHeader {
|
||||
attrs {
|
||||
border = true
|
||||
}
|
||||
nameCrumbs(selected) { selected = it }
|
||||
}
|
||||
IslandContent{
|
||||
IslandContent {
|
||||
propertyEditor(
|
||||
ownProperties = vision.ownProperties,
|
||||
allProperties = vision.allProperties(),
|
||||
ownProperties = vision.meta(false, false, false),
|
||||
allProperties = vision.meta(),
|
||||
updateFlow = vision.propertyChanges,
|
||||
descriptor = vision.descriptor,
|
||||
key = selected
|
||||
|
@ -7,16 +7,19 @@ import react.dom.render
|
||||
import ringui.Island
|
||||
import ringui.SmartTabs
|
||||
import ringui.Tab
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.getStyle
|
||||
import space.kscience.visionforge.meta
|
||||
import space.kscience.visionforge.react.flexColumn
|
||||
import space.kscience.visionforge.react.metaViewer
|
||||
import space.kscience.visionforge.react.propertyEditor
|
||||
import space.kscience.visionforge.solid.SolidReference
|
||||
import space.kscience.visionforge.styles
|
||||
|
||||
public fun RBuilder.ringPropertyEditor(
|
||||
vision: Vision,
|
||||
descriptor: NodeDescriptor? = vision.descriptor,
|
||||
descriptor: MetaDescriptor? = vision.descriptor,
|
||||
key: Any? = null,
|
||||
) {
|
||||
val styles = if (vision is SolidReference) {
|
||||
@ -28,8 +31,8 @@ public fun RBuilder.ringPropertyEditor(
|
||||
flexColumn {
|
||||
Island("Properties") {
|
||||
propertyEditor(
|
||||
ownProperties = vision.ownProperties,
|
||||
allProperties = vision.allProperties(),
|
||||
ownProperties = vision.meta(false,false,false),
|
||||
allProperties = vision.meta(),
|
||||
updateFlow = vision.propertyChanges,
|
||||
descriptor = descriptor,
|
||||
key = key
|
||||
@ -69,7 +72,7 @@ public fun RBuilder.ringPropertyEditor(
|
||||
|
||||
public fun Element.ringPropertyEditor(
|
||||
item: Vision,
|
||||
descriptor: NodeDescriptor? = item.descriptor,
|
||||
descriptor: MetaDescriptor? = item.descriptor,
|
||||
): Unit = render(this) {
|
||||
ringPropertyEditor(item, descriptor = descriptor)
|
||||
}
|
@ -15,6 +15,7 @@ import react.dom.button
|
||||
import ringui.Island
|
||||
import ringui.SmartTabs
|
||||
import ringui.Tab
|
||||
import space.kscience.dataforge.meta.descriptors.defaultNode
|
||||
import space.kscience.dataforge.meta.withDefault
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.Vision
|
||||
@ -49,7 +50,7 @@ internal external interface CanvasControlsProps : RProps {
|
||||
public var vision: Vision?
|
||||
}
|
||||
|
||||
internal val CanvasControls: FunctionalComponent<CanvasControlsProps> = functionalComponent("CanvasControls") { props ->
|
||||
internal val CanvasControls: FunctionComponent<CanvasControlsProps> = functionalComponent("CanvasControls") { props ->
|
||||
flexColumn {
|
||||
flexRow {
|
||||
css {
|
||||
@ -73,7 +74,7 @@ internal val CanvasControls: FunctionalComponent<CanvasControlsProps> = function
|
||||
}
|
||||
propertyEditor(
|
||||
ownProperties = props.options,
|
||||
allProperties = props.options.withDefault(Canvas3DOptions.descriptor.defaultMeta),
|
||||
allProperties = props.options.meta.withDefault(Canvas3DOptions.descriptor.defaultNode),
|
||||
descriptor = Canvas3DOptions.descriptor,
|
||||
expanded = false
|
||||
)
|
||||
@ -91,7 +92,7 @@ public external interface ThreeControlsProps : RProps {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
public val ThreeControls: FunctionalComponent<ThreeControlsProps> = functionalComponent { props ->
|
||||
public val ThreeControls: FunctionComponent<ThreeControlsProps> = functionalComponent { props ->
|
||||
SmartTabs("Tree") {
|
||||
props.vision?.let {
|
||||
Tab("Tree") {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.number
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.dataforge.values.int
|
||||
import space.kscience.dataforge.values.string
|
||||
@ -190,25 +192,18 @@ public object Colors {
|
||||
/**
|
||||
* Convert color represented as Meta to string of format #rrggbb
|
||||
*/
|
||||
fun fromMeta(item: MetaItem): String {
|
||||
return when (item) {
|
||||
is MetaItemNode -> {
|
||||
val node = item.node
|
||||
rgbToString(
|
||||
node[RED_KEY].number?.toByte()?.toUByte() ?: 0u,
|
||||
node[GREEN_KEY].number?.toByte()?.toUByte() ?: 0u,
|
||||
node[BLUE_KEY].number?.toByte()?.toUByte() ?: 0u
|
||||
)
|
||||
}
|
||||
is MetaItemValue -> {
|
||||
if (item.value.type == ValueType.NUMBER) {
|
||||
rgbToString(item.value.int)
|
||||
} else {
|
||||
item.value.string
|
||||
}
|
||||
}
|
||||
fun fromMeta(meta: Meta): String = meta.value?.let { value ->
|
||||
//if value is present, use it
|
||||
if (value.type == ValueType.NUMBER) {
|
||||
rgbToString(value.int)
|
||||
} else {
|
||||
value.string
|
||||
}
|
||||
}
|
||||
} ?: rgbToString(
|
||||
meta[RED_KEY].number?.toByte()?.toUByte() ?: 0u,
|
||||
meta[GREEN_KEY].number?.toByte()?.toUByte() ?: 0u,
|
||||
meta[BLUE_KEY].number?.toByte()?.toUByte() ?: 0u
|
||||
)
|
||||
|
||||
/**
|
||||
* Convert Int color to string of format #rrggbb
|
||||
|
@ -1,7 +1,7 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaBuilder
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.Scheme
|
||||
import space.kscience.dataforge.meta.Specification
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
@ -27,7 +27,7 @@ public fun Vision.useStyle(reference: StyleReference) {
|
||||
@VisionBuilder
|
||||
public fun VisionGroup.style(
|
||||
styleKey: String? = null,
|
||||
builder: MetaBuilder.() -> Unit,
|
||||
builder: MutableMeta.() -> Unit,
|
||||
): ReadOnlyProperty<Any?, StyleReference> = ReadOnlyProperty { _, property ->
|
||||
val styleName = styleKey ?: property.name
|
||||
styleSheet.define(styleName, Meta(builder))
|
||||
|
@ -5,6 +5,7 @@ import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.values.asValue
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
/**
|
||||
@ -13,9 +14,9 @@ import kotlin.jvm.JvmInline
|
||||
@JvmInline
|
||||
public value class StyleSheet(private val owner: VisionGroup) {
|
||||
|
||||
private val styleNode get() = owner.ownProperties[STYLESHEET_KEY].node
|
||||
private val styleNode: Meta? get() = owner.getOwnProperty(STYLESHEET_KEY)
|
||||
|
||||
public val items: Map<NameToken, Meta>? get() = styleNode?.items?.mapValues { it.value.node ?: Meta.EMPTY }
|
||||
public val items: Map<NameToken, Meta>? get() = styleNode?.items
|
||||
|
||||
public operator fun get(key: String): Meta? = owner.getStyle(key)
|
||||
|
||||
@ -23,7 +24,7 @@ public value class StyleSheet(private val owner: VisionGroup) {
|
||||
* Define a style without notifying owner
|
||||
*/
|
||||
public fun define(key: String, style: Meta?) {
|
||||
owner.setProperty(STYLESHEET_KEY + key, style)
|
||||
owner.setPropertyNode(STYLESHEET_KEY + key, style)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,7 +41,7 @@ public value class StyleSheet(private val owner: VisionGroup) {
|
||||
/**
|
||||
* Create and set a style
|
||||
*/
|
||||
public operator fun set(key: String, builder: MetaBuilder.() -> Unit) {
|
||||
public operator fun set(key: String, builder: MutableMeta.() -> Unit) {
|
||||
val newStyle = get(key)?.toMutableMeta()?.apply(builder) ?: Meta(builder)
|
||||
set(key, newStyle.seal())
|
||||
}
|
||||
@ -70,9 +71,9 @@ internal fun Vision.styleChanged(key: String, oldStyle: Meta?, newStyle: Meta?)
|
||||
* List of names of styles applied to this object. Order matters. Not inherited.
|
||||
*/
|
||||
public var Vision.styles: List<String>
|
||||
get() = ownProperties[Vision.STYLE_KEY]?.stringList ?: emptyList()
|
||||
get() = getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()
|
||||
set(value) {
|
||||
setProperty(Vision.STYLE_KEY, value)
|
||||
setPropertyValue(Vision.STYLE_KEY, value.map { it.asValue() }.asValue())
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,7 +86,7 @@ public val VisionGroup.styleSheet: StyleSheet get() = StyleSheet(this)
|
||||
* Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment.
|
||||
*/
|
||||
public fun Vision.useStyle(name: String) {
|
||||
styles = (ownProperties[Vision.STYLE_KEY]?.stringList ?: emptyList()) + name
|
||||
styles = (getOwnProperty(Vision.STYLE_KEY)?.stringList ?: emptyList()) + name
|
||||
}
|
||||
|
||||
|
||||
@ -93,13 +94,13 @@ public fun Vision.useStyle(name: String) {
|
||||
* Find a style with given name for given [Vision]. The style is not necessary applied to this [Vision].
|
||||
*/
|
||||
public tailrec fun Vision.getStyle(name: String): Meta? =
|
||||
ownProperties[StyleSheet.STYLESHEET_KEY + name].node ?: parent?.getStyle(name)
|
||||
getOwnProperty(StyleSheet.STYLESHEET_KEY + name) ?: parent?.getStyle(name)
|
||||
|
||||
/**
|
||||
* Resolve an item in all style layers
|
||||
*/
|
||||
public fun Vision.getStyleItems(name: Name): List<MetaItem> = styles.mapNotNull {
|
||||
getStyle(it)[name]
|
||||
public fun Vision.getStyleItems(name: Name): List<Meta> = styles.mapNotNull {
|
||||
getStyle(it)?.get(name)
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,14 +4,15 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.descriptors.Described
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.update
|
||||
import space.kscience.dataforge.misc.Type
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.values.Value
|
||||
import space.kscience.visionforge.Vision.Companion.TYPE
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
@ -45,13 +46,13 @@ public interface Vision : Described, CoroutineScope {
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
): MetaItem?
|
||||
): Meta?
|
||||
|
||||
/**
|
||||
* Get an intrinsic property of this Vision excluding any inheritance or defaults. In most cases should be the same as
|
||||
* `getProperty(name, false, false, false`.
|
||||
*/
|
||||
public fun getOwnProperty(name: Name): MetaItem? = getProperty(
|
||||
public fun getOwnProperty(name: Name): Meta? = getProperty(
|
||||
name,
|
||||
inherit = false,
|
||||
includeStyles = false,
|
||||
@ -59,9 +60,14 @@ public interface Vision : Described, CoroutineScope {
|
||||
)
|
||||
|
||||
/**
|
||||
* Set the property value
|
||||
* Replace the property node. If [node] is null remove node and its descendants
|
||||
*/
|
||||
public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true)
|
||||
public fun setPropertyNode(name: Name, node: Meta?, notify: Boolean = true)
|
||||
|
||||
/**
|
||||
* Set a value of specific property node
|
||||
*/
|
||||
public fun setPropertyValue(name: Name, value: Value?, notify: Boolean = true)
|
||||
|
||||
/**
|
||||
* Flow of property invalidation events. It does not contain property values after invalidation since it is not clear
|
||||
@ -77,9 +83,9 @@ public interface Vision : Described, CoroutineScope {
|
||||
/**
|
||||
* Update this vision using a dif represented by [VisionChange].
|
||||
*/
|
||||
public fun update(change: VisionChange)
|
||||
public fun change(change: VisionChange)
|
||||
|
||||
override val descriptor: NodeDescriptor?
|
||||
override val descriptor: MetaDescriptor?
|
||||
|
||||
public companion object {
|
||||
public const val TYPE: String = "vision"
|
||||
@ -89,11 +95,6 @@ public interface Vision : Described, CoroutineScope {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Root property node
|
||||
*/
|
||||
public val Vision.meta: Meta get() = ownProperties[Name.EMPTY]?.node ?: Meta.EMPTY
|
||||
|
||||
/**
|
||||
* Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled
|
||||
*/
|
||||
@ -105,31 +106,26 @@ public fun Vision.onPropertyChange(scope: CoroutineScope, callback: suspend (Nam
|
||||
/**
|
||||
* Own properties, excluding inheritance, styles and descriptor
|
||||
*/
|
||||
public val Vision.ownProperties: MutableItemProvider
|
||||
get() = object : MutableItemProvider {
|
||||
override fun getItem(name: Name): MetaItem? = getOwnProperty(name)
|
||||
override fun setItem(name: Name, item: MetaItem?): Unit = setProperty(name, item)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient accessor for all properties of a vision.
|
||||
* @param inherit - inherit property value from the parent by default. If null, inheritance is inferred from descriptor
|
||||
*/
|
||||
public fun Vision.allProperties(
|
||||
inherit: Boolean? = null,
|
||||
includeStyles: Boolean? = null,
|
||||
public fun Vision.meta(
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
): MutableItemProvider = object : MutableItemProvider {
|
||||
override fun getItem(name: Name): MetaItem? = getProperty(
|
||||
name,
|
||||
inherit = inherit ?: (descriptor?.get(name)?.inherited == true),
|
||||
includeStyles = includeStyles ?: (descriptor?.get(name)?.usesStyles != false),
|
||||
includeDefaults = includeDefaults
|
||||
)
|
||||
): MutableMeta = VisionProperties(this, Name.EMPTY, inherit, includeStyles, includeDefaults)
|
||||
|
||||
override fun setItem(name: Name, item: MetaItem?): Unit = setProperty(name, item)
|
||||
public fun Vision.configure(target: Name = Name.EMPTY, block: MutableMeta.() -> Unit): Unit {
|
||||
VisionProperties(this, target).apply(block)
|
||||
}
|
||||
|
||||
public fun Vision.configure(meta: Meta) {
|
||||
configure(Name.EMPTY) {
|
||||
update(meta)
|
||||
}
|
||||
}
|
||||
|
||||
public fun Vision.configure(block: MutableMeta.() -> Unit): Unit = configure(Meta(block))
|
||||
|
||||
public fun Vision.getOwnProperty(key: String): Meta? = getOwnProperty(Name.parse(key))
|
||||
|
||||
/**
|
||||
* Get [Vision] property using key as a String
|
||||
*/
|
||||
@ -138,18 +134,20 @@ public fun Vision.getProperty(
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
): MetaItem? = getProperty(key.toName(), inherit, includeStyles, includeDefaults)
|
||||
): Meta? = getProperty(Name.parse(key), inherit, includeStyles, includeDefaults)
|
||||
|
||||
|
||||
/**
|
||||
* A convenience method to pair [getProperty]
|
||||
* A convenience method to set property node or value. If Item is null, then node is removed, not a value
|
||||
*/
|
||||
public fun Vision.setProperty(key: Name, item: Any?) {
|
||||
setProperty(key, MetaItem.of(item))
|
||||
public fun Vision.setProperty(name: Name, item: Any?) {
|
||||
when (item) {
|
||||
null -> setPropertyNode(name, null)
|
||||
is Meta -> setPropertyNode(name, item)
|
||||
is Value -> setPropertyValue(name, item)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to pair [getProperty]
|
||||
*/
|
||||
public fun Vision.setProperty(key: String, item: Any?) {
|
||||
setProperty(key.toName(), MetaItem.of(item))
|
||||
public fun Vision.setPropertyNode(key: String, item: Any?) {
|
||||
setProperty(Name.parse(key), item)
|
||||
}
|
||||
|
@ -7,21 +7,18 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.defaultNode
|
||||
import space.kscience.dataforge.meta.descriptors.value
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.values.Null
|
||||
import space.kscience.dataforge.values.Value
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.visionforge.Vision.Companion.STYLE_KEY
|
||||
import kotlin.jvm.Synchronized
|
||||
|
||||
internal data class PropertyListener(
|
||||
val owner: Any? = null,
|
||||
val action: (name: Name) -> Unit,
|
||||
)
|
||||
|
||||
/**
|
||||
* A full base implementation for a [Vision]
|
||||
* @param properties Object own properties excluding styles and inheritance
|
||||
@ -29,14 +26,17 @@ internal data class PropertyListener(
|
||||
@Serializable
|
||||
@SerialName("vision")
|
||||
public open class VisionBase(
|
||||
override @Transient var parent: VisionGroup? = null,
|
||||
protected var properties: MutableItemProvider? = null
|
||||
@Transient override var parent: VisionGroup? = null,
|
||||
@Serializable(MutableMetaSerializer::class)
|
||||
protected var properties: MutableMeta? = null
|
||||
) : Vision {
|
||||
|
||||
//protected val observableProperties: ObservableMutableMeta by lazy { properties.asObservable() }
|
||||
|
||||
@Synchronized
|
||||
protected fun getOrCreateProperties(): MutableItemProvider {
|
||||
protected fun getOrCreateProperties(): MutableMeta {
|
||||
if (properties == null) {
|
||||
val newProperties = MetaBuilder()
|
||||
val newProperties = MutableMeta()
|
||||
properties = newProperties
|
||||
}
|
||||
return properties!!
|
||||
@ -45,18 +45,14 @@ public open class VisionBase(
|
||||
/**
|
||||
* A fast accessor method to get own property (no inheritance or styles
|
||||
*/
|
||||
override fun getOwnProperty(name: Name): MetaItem? = if (name == Name.EMPTY) {
|
||||
properties?.rootItem
|
||||
} else {
|
||||
properties?.getItem(name)
|
||||
}
|
||||
override fun getOwnProperty(name: Name): Meta? = properties?.getMeta(name)
|
||||
|
||||
override fun getProperty(
|
||||
name: Name,
|
||||
inherit: Boolean,
|
||||
includeStyles: Boolean,
|
||||
includeDefaults: Boolean,
|
||||
): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) {
|
||||
): Meta? = if (!inherit && !includeStyles && !includeDefaults) {
|
||||
getOwnProperty(name)
|
||||
} else {
|
||||
buildList {
|
||||
@ -68,22 +64,32 @@ public open class VisionBase(
|
||||
add(parent?.getProperty(name, inherit, includeStyles, includeDefaults))
|
||||
}
|
||||
if (includeDefaults) {
|
||||
add(descriptor?.defaultMeta?.get(name))
|
||||
add(descriptor?.defaultNode?.get(name))
|
||||
}
|
||||
}.merge()
|
||||
}
|
||||
|
||||
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
|
||||
val oldItem = properties?.getItem(name)
|
||||
if(oldItem!= item) {
|
||||
getOrCreateProperties().setItem(name, item)
|
||||
override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) {
|
||||
val oldItem = properties?.get(name)
|
||||
if (oldItem != node) {
|
||||
getOrCreateProperties().setMeta(name, node)
|
||||
if (notify) {
|
||||
invalidateProperty(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val descriptor: NodeDescriptor? get() = null
|
||||
override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) {
|
||||
val oldItem = properties?.get(name)?.value
|
||||
if (oldItem != value) {
|
||||
getOrCreateProperties()[name] = value
|
||||
if (notify) {
|
||||
invalidateProperty(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val descriptor: MetaDescriptor? get() = null
|
||||
|
||||
private suspend fun updateStyles(names: List<String>) {
|
||||
names.mapNotNull { getStyle(it) }.asSequence()
|
||||
@ -111,31 +117,23 @@ public open class VisionBase(
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(change: VisionChange) {
|
||||
override fun change(change: VisionChange) {
|
||||
change.properties?.let {
|
||||
updateProperties(Name.EMPTY, it.asMetaItem())
|
||||
updateProperties(Name.EMPTY, it)
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
public val descriptor: NodeDescriptor = NodeDescriptor {
|
||||
value(STYLE_KEY) {
|
||||
type(ValueType.STRING)
|
||||
public val descriptor: MetaDescriptor = MetaDescriptor {
|
||||
value(STYLE_KEY, ValueType.STRING) {
|
||||
multiple = true
|
||||
}
|
||||
}
|
||||
|
||||
public fun Vision.updateProperties(at: Name, item: MetaItem) {
|
||||
when (item) {
|
||||
is MetaItemValue -> {
|
||||
if (item.value == Null) {
|
||||
setProperty(at, null)
|
||||
} else
|
||||
setProperty(at, item)
|
||||
}
|
||||
is MetaItemNode -> item.node.items.forEach { (token, childItem) ->
|
||||
updateProperties(at + token, childItem)
|
||||
}
|
||||
public fun Vision.updateProperties(at: Name, item: Meta) {
|
||||
setPropertyValue(at, item.value)
|
||||
item.items.forEach { (token, item) ->
|
||||
updateProperties(at + token, item)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
|
||||
|
||||
private var reset: Boolean = false
|
||||
private var vision: Vision? = null
|
||||
private val propertyChange = MetaBuilder()
|
||||
private val propertyChange = MutableMeta()
|
||||
private val children: HashMap<Name, VisionChangeBuilder> = HashMap()
|
||||
|
||||
public fun isEmpty(): Boolean = propertyChange.isEmpty() && propertyChange.isEmpty() && children.isEmpty()
|
||||
@ -30,17 +30,17 @@ public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
|
||||
private fun getOrPutChild(visionName: Name): VisionChangeBuilder =
|
||||
children.getOrPut(visionName) { VisionChangeBuilder() }
|
||||
|
||||
public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem?) {
|
||||
public fun propertyChanged(visionName: Name, propertyName: Name, item: Meta?) {
|
||||
if (visionName == Name.EMPTY) {
|
||||
//Write property removal as [Null]
|
||||
propertyChange[propertyName] = (item ?: Null.asMetaItem())
|
||||
propertyChange[propertyName] = (item ?: Meta(Null))
|
||||
} else {
|
||||
getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun set(name: Name?, child: Vision?) {
|
||||
if(name == null) error("Static children are not allowed in VisionChange")
|
||||
if (name == null) error("Static children are not allowed in VisionChange")
|
||||
getOrPutChild(name).apply {
|
||||
vision = child
|
||||
reset = vision == null
|
||||
@ -90,7 +90,7 @@ private fun CoroutineScope.collectChange(
|
||||
|
||||
//Collect properties change
|
||||
source.onPropertyChange(this) { propertyName ->
|
||||
val newItem = source.ownProperties[propertyName]
|
||||
val newItem = source.getOwnProperty(propertyName)
|
||||
collector().propertyChanged(name, propertyName, newItem)
|
||||
}
|
||||
|
||||
|
@ -75,12 +75,12 @@ public interface MutableVisionGroup : VisionGroup, VisionContainerBuilder<Vision
|
||||
public val structureChanges: Flow<StructureChange>
|
||||
}
|
||||
|
||||
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(str.toName())
|
||||
public operator fun <V : Vision> VisionContainer<V>.get(str: String): V? = get(Name.parse(str))
|
||||
|
||||
public operator fun <V : Vision> VisionContainerBuilder<V>.set(token: NameToken, child: V?): Unit =
|
||||
set(token.asName(), child)
|
||||
|
||||
public operator fun <V : Vision> VisionContainerBuilder<V>.set(key: String?, child: V?): Unit =
|
||||
set(key?.toName(), child)
|
||||
set(key?.let(Name::parse), child)
|
||||
|
||||
public fun MutableVisionGroup.removeAll(): Unit = children.keys.map { it.asName() }.forEach { this[it] = null }
|
@ -131,15 +131,15 @@ public open class VisionGroupBase(
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(change: VisionChange) {
|
||||
override fun change(change: VisionChange) {
|
||||
change.children?.forEach { (name, change) ->
|
||||
when {
|
||||
change.delete -> set(name, null)
|
||||
change.vision != null -> set(name, change.vision)
|
||||
else -> get(name)?.update(change)
|
||||
else -> get(name)?.change(change)
|
||||
}
|
||||
}
|
||||
super.update(change)
|
||||
super.change(change)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,12 +8,10 @@ import kotlinx.serialization.modules.polymorphic
|
||||
import kotlinx.serialization.modules.subclass
|
||||
import space.kscience.dataforge.context.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.node
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.toJson
|
||||
import space.kscience.dataforge.meta.toMetaItem
|
||||
import space.kscience.dataforge.meta.toMeta
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.toName
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
@ -48,12 +46,11 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta) {
|
||||
jsonFormat.encodeToJsonElement(visionSerializer, vision)
|
||||
|
||||
//TODO remove double transformation with dedicated Meta serial format
|
||||
public fun decodeFromMeta(meta: Meta, descriptor: NodeDescriptor? = null): Vision =
|
||||
public fun decodeFromMeta(meta: Meta, descriptor: MetaDescriptor? = null): Vision =
|
||||
decodeFromJson(meta.toJson(descriptor))
|
||||
|
||||
public fun encodeToMeta(vision: Vision, descriptor: NodeDescriptor? = null): Meta =
|
||||
encodeToJsonElement(vision).toMetaItem(descriptor).node
|
||||
?: error("Expected node, but value found. Check your serializer!")
|
||||
public fun encodeToMeta(vision: Vision, descriptor: MetaDescriptor? = null): Meta =
|
||||
encodeToJsonElement(vision).toMeta(descriptor)
|
||||
|
||||
public companion object : PluginFactory<VisionManager> {
|
||||
override val tag: PluginTag = PluginTag(name = "vision", group = PluginTag.DATAFORGE_GROUP)
|
||||
@ -89,7 +86,7 @@ public abstract class VisionPlugin(meta: Meta = Meta.EMPTY) : AbstractPlugin(met
|
||||
protected abstract val visionSerializersModule: SerializersModule
|
||||
|
||||
override fun content(target: String): Map<Name, Any> = when (target) {
|
||||
VisionManager.VISION_SERIALIZER_MODULE_TARGET -> mapOf(tag.toString().toName() to visionSerializersModule)
|
||||
VisionManager.VISION_SERIALIZER_MODULE_TARGET -> mapOf(Name.parse(tag.toString()) to visionSerializersModule)
|
||||
else -> super.content(target)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.values.Value
|
||||
|
||||
internal class VisionProperties(
|
||||
val vision: Vision,
|
||||
val rootName: Name,
|
||||
val inherit: Boolean = false,
|
||||
val includeStyles: Boolean = true,
|
||||
val includeDefaults: Boolean = true,
|
||||
) : MutableMeta {
|
||||
|
||||
override val items: Map<NameToken, MutableMeta>
|
||||
get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.items?.mapValues {
|
||||
VisionProperties(vision, rootName + it.key, inherit, includeStyles, includeDefaults)
|
||||
} ?: emptyMap()
|
||||
|
||||
override var value: Value?
|
||||
get() = vision.getProperty(rootName, inherit, includeStyles, includeDefaults)?.value
|
||||
set(value) {
|
||||
vision.setPropertyValue(rootName, value)
|
||||
}
|
||||
|
||||
override fun getOrCreate(name: Name): MutableMeta = VisionProperties(vision, rootName + name)
|
||||
|
||||
override fun setMeta(name: Name, node: Meta?) {
|
||||
vision.setPropertyNode(rootName + name, node)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||
override fun hashCode(): Int = Meta.hashCode(this)
|
||||
override fun toString(): String = Meta.toString(this)
|
||||
}
|
@ -1,32 +1,49 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.Configurable
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.ObservableMutableMeta
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.values.Value
|
||||
|
||||
/**
|
||||
* Property containers are used to create a symmetric behaviors for vision properties and style builders
|
||||
*/
|
||||
public interface VisionPropertyContainer<out V: Vision> {
|
||||
public interface VisionPropertyContainer<out V : Vision> {
|
||||
public fun getProperty(
|
||||
name: Name,
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
): MetaItem?
|
||||
): Meta?
|
||||
|
||||
public fun setProperty(name: Name, item: MetaItem?, notify: Boolean = true)
|
||||
/**
|
||||
* Replace the property node. If [node] is null remove node and its descendants
|
||||
*/
|
||||
public fun setPropertyNode(name: Name, node: Meta?, notify: Boolean = true)
|
||||
|
||||
/**
|
||||
* Set a value of specific property node
|
||||
*/
|
||||
public fun setPropertyValue(name: Name, value: Value?, notify: Boolean = true)
|
||||
}
|
||||
|
||||
public open class SimpleVisionPropertyContainer<out V: Vision>(protected val config: ObservableMeta): VisionPropertyContainer<V>{
|
||||
public open class SimpleVisionPropertyContainer<out V : Vision>(
|
||||
override val meta: ObservableMutableMeta,
|
||||
) : VisionPropertyContainer<V>, Configurable {
|
||||
override fun getProperty(
|
||||
name: Name,
|
||||
inherit: Boolean,
|
||||
includeStyles: Boolean,
|
||||
includeDefaults: Boolean
|
||||
): MetaItem? = config[name]
|
||||
): Meta? = meta[name]
|
||||
|
||||
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
|
||||
config[name] = item
|
||||
override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) {
|
||||
this.meta.setMeta(name, node)
|
||||
}
|
||||
|
||||
override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) {
|
||||
this.meta.setValue(name, value)
|
||||
}
|
||||
}
|
@ -2,12 +2,13 @@ package space.kscience.visionforge.html
|
||||
|
||||
import kotlinx.html.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaBuilder
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.isEmpty
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionManager
|
||||
import kotlin.collections.set
|
||||
@ -25,7 +26,7 @@ public class VisionOutput @PublishedApi internal constructor(public val manager:
|
||||
|
||||
//TODO expose a way to define required plugins.
|
||||
|
||||
public inline fun meta(block: MetaBuilder.() -> Unit) {
|
||||
public inline fun meta(block: MutableMeta.() -> Unit) {
|
||||
this.meta = Meta(block)
|
||||
}
|
||||
}
|
||||
@ -36,7 +37,7 @@ public class VisionOutput @PublishedApi internal constructor(public val manager:
|
||||
@VisionDSL
|
||||
public abstract class VisionTagConsumer<R>(
|
||||
private val root: TagConsumer<R>,
|
||||
public val manager:VisionManager,
|
||||
public val manager: VisionManager,
|
||||
private val idPrefix: String? = null,
|
||||
) : TagConsumer<R> by root {
|
||||
|
||||
@ -94,11 +95,11 @@ public abstract class VisionTagConsumer<R>(
|
||||
public inline fun <T> TagConsumer<T>.vision(
|
||||
name: String = DEFAULT_VISION_NAME,
|
||||
visionProvider: VisionOutput.() -> Vision,
|
||||
): T = vision(name.toName(), visionProvider)
|
||||
): T = vision(Name.parse(name), visionProvider)
|
||||
|
||||
public fun <T> TagConsumer<T>.vision(
|
||||
vision: Vision,
|
||||
): T = vision("vision[${vision.hashCode()}]".toName(), vision)
|
||||
): T = vision(NameToken("vision", vision.hashCode().toString()).asName(), vision)
|
||||
|
||||
/**
|
||||
* Process the resulting object produced by [TagConsumer]
|
||||
|
@ -1,18 +1,20 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.Laminate
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.boolean
|
||||
import space.kscience.dataforge.meta.isLeaf
|
||||
import space.kscience.dataforge.values.asValue
|
||||
|
||||
@DslMarker
|
||||
public annotation class VisionBuilder
|
||||
|
||||
public fun List<MetaItem?>.merge(): MetaItem? = when (val first = firstOrNull { it != null }) {
|
||||
null -> null
|
||||
is MetaItemValue -> first //fast search for first entry if it is value
|
||||
is MetaItemNode -> {
|
||||
//merge nodes if first encountered node is meta
|
||||
val laminate: Laminate = Laminate(mapNotNull { it.node })
|
||||
MetaItemNode(laminate)
|
||||
public fun List<Meta?>.merge(): Meta? {
|
||||
val first = firstOrNull { it != null }
|
||||
return when {
|
||||
first == null -> null
|
||||
first.isLeaf -> first //fast search for first entry if it is value
|
||||
else -> Laminate(filterNotNull()) //merge nodes if first encountered node is meta
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,8 +23,4 @@ public fun List<MetaItem?>.merge(): MetaItem? = when (val first = firstOrNull {
|
||||
*/
|
||||
public var Vision.visible: Boolean?
|
||||
get() = getProperty(Vision.VISIBLE_KEY).boolean
|
||||
set(value) = setProperty(Vision.VISIBLE_KEY, value?.asValue())
|
||||
|
||||
public fun Vision.configure(meta: Meta?): Unit = update(VisionChange(properties = meta))
|
||||
|
||||
public fun Vision.configure(block: MetaBuilder.() -> Unit): Unit = configure(Meta(block))
|
||||
set(value) = setPropertyValue(Vision.VISIBLE_KEY, value?.asValue())
|
@ -2,11 +2,11 @@ package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.Scheme
|
||||
import space.kscience.dataforge.meta.SchemeSpec
|
||||
import space.kscience.dataforge.meta.asObservable
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptorBuilder
|
||||
import space.kscience.dataforge.meta.descriptors.ValueDescriptorBuilder
|
||||
import space.kscience.dataforge.meta.toMutableMeta
|
||||
import space.kscience.dataforge.meta.descriptors.Described
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder
|
||||
import space.kscience.dataforge.meta.descriptors.item
|
||||
import space.kscience.dataforge.meta.descriptors.value
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.typeOf
|
||||
@ -14,55 +14,53 @@ import kotlin.reflect.typeOf
|
||||
/**
|
||||
* TODO to be moved into the core
|
||||
*/
|
||||
public inline fun <S : Scheme, reified T> NodeDescriptorBuilder.value(
|
||||
public inline fun <S : Scheme, reified T> MetaDescriptorBuilder.value(
|
||||
property: KProperty1<S, T>,
|
||||
noinline block: ValueDescriptorBuilder.() -> Unit = {},
|
||||
noinline block: MetaDescriptorBuilder.() -> Unit = {},
|
||||
) {
|
||||
when (typeOf<T>()) {
|
||||
typeOf<Number>(), typeOf<Int>(), typeOf<Double>(), typeOf<Short>(), typeOf<Long>(), typeOf<Float>() ->
|
||||
value(property.name) {
|
||||
type(ValueType.NUMBER)
|
||||
value(property.name, ValueType.NUMBER) {
|
||||
block()
|
||||
}
|
||||
typeOf<Number?>(), typeOf<Int?>(), typeOf<Double?>(), typeOf<Short?>(), typeOf<Long?>(), typeOf<Float?>() ->
|
||||
value(property.name) {
|
||||
type(ValueType.NUMBER)
|
||||
value(property.name, ValueType.NUMBER) {
|
||||
block()
|
||||
}
|
||||
typeOf<Boolean>() -> value(property.name) {
|
||||
type(ValueType.BOOLEAN)
|
||||
typeOf<Boolean>() -> value(property.name, ValueType.BOOLEAN) {
|
||||
block()
|
||||
}
|
||||
typeOf<List<Number>>(), typeOf<List<Int>>(), typeOf<List<Double>>(), typeOf<List<Short>>(), typeOf<List<Long>>(), typeOf<List<Float>>(),
|
||||
typeOf<IntArray>(), typeOf<DoubleArray>(), typeOf<ShortArray>(), typeOf<LongArray>(), typeOf<FloatArray>(),
|
||||
-> value(property.name) {
|
||||
type(ValueType.NUMBER)
|
||||
-> value(property.name, ValueType.NUMBER) {
|
||||
multiple = true
|
||||
block()
|
||||
}
|
||||
typeOf<String>() -> value(property.name) {
|
||||
type(ValueType.STRING)
|
||||
typeOf<String>() -> value(property.name, ValueType.STRING) {
|
||||
block()
|
||||
}
|
||||
typeOf<List<String>>(), typeOf<Array<String>>() -> value(property.name) {
|
||||
type(ValueType.STRING)
|
||||
typeOf<List<String>>(), typeOf<Array<String>>() -> value(property.name, ValueType.STRING) {
|
||||
multiple = true
|
||||
block()
|
||||
}
|
||||
else -> value(property.name, block)
|
||||
else -> item(property.name, block)
|
||||
}
|
||||
}
|
||||
|
||||
public fun NodeDescriptor.copy(block: NodeDescriptorBuilder.() -> Unit = {}): NodeDescriptor {
|
||||
return NodeDescriptorBuilder(toMeta().toMutableMeta().asObservable()).apply(block)
|
||||
public fun MetaDescriptorBuilder.item(
|
||||
key: String,
|
||||
described: Described,
|
||||
block: MetaDescriptorBuilder.() -> Unit = {},
|
||||
) {
|
||||
described.descriptor?.let {
|
||||
item(Name.parse(key), it, block)
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <S : Scheme, reified T : Scheme> NodeDescriptorBuilder.scheme(
|
||||
public inline fun <S : Scheme, reified T : Scheme> MetaDescriptorBuilder.scheme(
|
||||
property: KProperty1<S, T>,
|
||||
spec: SchemeSpec<T>,
|
||||
noinline block: NodeDescriptorBuilder.() -> Unit = {},
|
||||
noinline block: MetaDescriptorBuilder.() -> Unit = {},
|
||||
) {
|
||||
spec.descriptor?.let { descriptor ->
|
||||
item(property.name, descriptor.copy(block))
|
||||
}
|
||||
}
|
||||
item(property.name, spec, block)
|
||||
}
|
||||
|
@ -0,0 +1,93 @@
|
||||
package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.values.Value
|
||||
import space.kscience.dataforge.values.number
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
public fun Vision.propertyNode(
|
||||
name: Name? = null,
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
): ReadWriteProperty<Any?, Meta?> = object : ReadWriteProperty<Any?, Meta?> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? =
|
||||
getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) {
|
||||
setPropertyNode(name ?: Name.parse(property.name), value)
|
||||
}
|
||||
}
|
||||
|
||||
public fun <T> Vision.propertyNode(
|
||||
converter: MetaConverter<T>,
|
||||
name: Name? = null,
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
): ReadWriteProperty<Any?, T?> = object : ReadWriteProperty<Any?, T?> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T? = getProperty(
|
||||
name ?: Name.parse(property.name),
|
||||
inherit,
|
||||
includeStyles,
|
||||
includeDefaults
|
||||
)?.let(converter::metaToObject)
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
|
||||
setPropertyNode(name ?: Name.parse(property.name), value?.let(converter::objectToMeta))
|
||||
}
|
||||
}
|
||||
|
||||
public fun Vision.propertyValue(
|
||||
name: Name? = null,
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
): ReadWriteProperty<Any?, Value?> = object : ReadWriteProperty<Any?, Value?> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Value? =
|
||||
getProperty(name ?: Name.parse(property.name), inherit, includeStyles, includeDefaults)?.value
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) {
|
||||
setPropertyValue(name ?: Name.parse(property.name), value)
|
||||
}
|
||||
}
|
||||
|
||||
public fun <T> Vision.propertyValue(
|
||||
name: Name? = null,
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
setter: (T) -> Value? = { it?.let(Value::of) },
|
||||
getter: (Value?) -> T,
|
||||
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T = getProperty(
|
||||
name ?: Name.parse(property.name),
|
||||
inherit,
|
||||
includeStyles,
|
||||
includeDefaults
|
||||
)?.value.let(getter)
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
setPropertyValue(name ?: Name.parse(property.name), value?.let(setter))
|
||||
}
|
||||
}
|
||||
|
||||
public fun Vision.numberProperty(
|
||||
name: Name? = null,
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true
|
||||
): ReadWriteProperty<Any?, Number?> = propertyValue(name, inherit, includeStyles, includeDefaults) { it?.number }
|
||||
|
||||
public fun Vision.numberProperty(
|
||||
name: Name? = null,
|
||||
inherit: Boolean = false,
|
||||
includeStyles: Boolean = true,
|
||||
includeDefaults: Boolean = true,
|
||||
default: () -> Number
|
||||
): ReadWriteProperty<Any?, Number> = propertyValue(name, inherit, includeStyles, includeDefaults) {
|
||||
it?.number ?: default()
|
||||
}
|
@ -2,87 +2,63 @@ package space.kscience.visionforge
|
||||
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.values.asValue
|
||||
|
||||
private const val INHERITED_DESCRIPTOR_ATTRIBUTE = "inherited"
|
||||
private const val STYLE_DESCRIPTOR_ATTRIBUTE = "useStyles"
|
||||
|
||||
public val ItemDescriptor.inherited: Boolean
|
||||
public val MetaDescriptor.inherited: Boolean
|
||||
get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false
|
||||
|
||||
public var ItemDescriptorBuilder.inherited: Boolean
|
||||
public var MetaDescriptorBuilder.inherited: Boolean
|
||||
get() = attributes[INHERITED_DESCRIPTOR_ATTRIBUTE].boolean ?: false
|
||||
set(value) = attributes {
|
||||
set(INHERITED_DESCRIPTOR_ATTRIBUTE, value)
|
||||
}
|
||||
set(value) = attributes.set(INHERITED_DESCRIPTOR_ATTRIBUTE, value)
|
||||
|
||||
public val ItemDescriptor.usesStyles: Boolean
|
||||
|
||||
public val MetaDescriptor.usesStyles: Boolean
|
||||
get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true
|
||||
|
||||
public var ItemDescriptorBuilder.usesStyles: Boolean
|
||||
public var MetaDescriptorBuilder.usesStyles: Boolean
|
||||
get() = attributes[STYLE_DESCRIPTOR_ATTRIBUTE].boolean ?: true
|
||||
set(value) = attributes {
|
||||
set(STYLE_DESCRIPTOR_ATTRIBUTE, value)
|
||||
}
|
||||
set(value) = attributes.set(STYLE_DESCRIPTOR_ATTRIBUTE, value)
|
||||
|
||||
|
||||
public val Vision.describedProperties: Meta
|
||||
get() = Meta {
|
||||
descriptor?.items?.forEach { (key, descriptor) ->
|
||||
key put getProperty(key, inherit = descriptor.inherited)
|
||||
descriptor?.children?.forEach { (key, descriptor) ->
|
||||
this.setMeta(key.asName(), getProperty(key, inherit = descriptor.inherited))
|
||||
}
|
||||
}
|
||||
|
||||
public val ValueDescriptor.widget: Meta
|
||||
get() = attributes["widget"].node ?: Meta.EMPTY
|
||||
public val MetaDescriptor.widget: Meta
|
||||
get() = attributes["widget"] ?: Meta.EMPTY
|
||||
|
||||
/**
|
||||
* Extension property to access the "widget" key of [ValueDescriptor]
|
||||
*/
|
||||
public var ValueDescriptorBuilder.widget: Meta
|
||||
get() = attributes["widget"].node ?: Meta.EMPTY
|
||||
public var MetaDescriptorBuilder.widget: Meta
|
||||
get() = attributes["widget"] ?: Meta.EMPTY
|
||||
set(value) {
|
||||
attributes {
|
||||
set("widget", value)
|
||||
}
|
||||
attributes["widget"] = value
|
||||
}
|
||||
|
||||
public val ValueDescriptor.widgetType: String?
|
||||
public val MetaDescriptor.widgetType: String?
|
||||
get() = attributes["widget.type"].string
|
||||
|
||||
/**
|
||||
* Extension property to access the "widget.type" key of [ValueDescriptor]
|
||||
*/
|
||||
public var ValueDescriptorBuilder.widgetType: String?
|
||||
public var MetaDescriptorBuilder.widgetType: String?
|
||||
get() = attributes["widget.type"].string
|
||||
set(value) {
|
||||
attributes {
|
||||
set("widget.type", value)
|
||||
}
|
||||
attributes["widget.type"] = value?.asValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, this item is hidden in property editor. Default is false
|
||||
*/
|
||||
public val ItemDescriptor.hidden: Boolean
|
||||
public val MetaDescriptor.hidden: Boolean
|
||||
get() = attributes["widget.hide"].boolean ?: false
|
||||
|
||||
public fun ItemDescriptorBuilder.hide(): Unit = attributes {
|
||||
set("widget.hide", true)
|
||||
}
|
||||
|
||||
|
||||
public inline fun <reified E : Enum<E>> NodeDescriptorBuilder.enum(
|
||||
key: Name,
|
||||
default: E?,
|
||||
crossinline modifier: ValueDescriptorBuilder.() -> Unit = {},
|
||||
): Unit = value(key) {
|
||||
type(ValueType.STRING)
|
||||
default?.let {
|
||||
default(default)
|
||||
}
|
||||
allowedValues = enumValues<E>().map { it.asValue() }
|
||||
modifier()
|
||||
}
|
||||
public fun MetaDescriptorBuilder.hide(): Unit = attributes.set("widget.hide", true)
|
@ -57,7 +57,7 @@ class HtmlTagTest {
|
||||
div {
|
||||
h2 { +"Properties" }
|
||||
ul {
|
||||
(vision as? VisionBase)?.meta?.items?.forEach {
|
||||
(vision as? VisionBase)?.meta()?.items?.forEach {
|
||||
li {
|
||||
a { +it.key.toString() }
|
||||
p { +it.value.toString() }
|
||||
|
@ -92,7 +92,7 @@ public class VisionClient : AbstractPlugin() {
|
||||
}
|
||||
|
||||
logger.debug { "Got update $change for output with name $name" }
|
||||
vision.update(change)
|
||||
vision.change(change)
|
||||
} else {
|
||||
console.error("WebSocket message data is not a string")
|
||||
}
|
||||
|
@ -115,5 +115,5 @@ public class ApplicationSurrogate : App() {
|
||||
}
|
||||
|
||||
public fun Context.display(width: Double = 800.0, height: Double = 600.0, component: () -> UIComponent) {
|
||||
plugins.fetch(FXPlugin).display(component(), width, height)
|
||||
fetch(FXPlugin).display(component(), width, height)
|
||||
}
|
@ -9,8 +9,8 @@ import javafx.collections.FXCollections
|
||||
import javafx.scene.control.ComboBox
|
||||
import javafx.util.StringConverter
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.descriptors.allowedValues
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.value
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.values.Value
|
||||
@ -56,7 +56,7 @@ public class ComboBoxValueChooser(public val values: Collection<Value>? = null)
|
||||
override val name: Name = "combo".asName()
|
||||
|
||||
override fun invoke(meta: Meta): ValueChooser =
|
||||
ComboBoxValueChooser(meta["values"].value?.list)
|
||||
ComboBoxValueChooser(meta["values"]?.value?.list)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,189 +0,0 @@
|
||||
/*
|
||||
* 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 space.kscience.visionforge.editor
|
||||
|
||||
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
|
||||
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.control.*
|
||||
import javafx.scene.control.cell.TextFieldTreeTableCell
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.HBox
|
||||
import javafx.scene.layout.Priority
|
||||
import javafx.scene.paint.Color
|
||||
import javafx.scene.text.Text
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.Config
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.visionforge.dfIconView
|
||||
import tornadofx.*
|
||||
|
||||
/**
|
||||
* A configuration editor fragment
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public class ConfigEditor(
|
||||
public val rootNode: FXMetaNode<Config>,
|
||||
public val allowNew: Boolean = true,
|
||||
title: String = "Configuration editor"
|
||||
) : Fragment(title = title, icon = dfIconView) {
|
||||
//TODO replace parameters by properties
|
||||
|
||||
public constructor(config: Config, descriptor: NodeDescriptor?, title: String = "Configuration editor") :
|
||||
this(FXMeta.root(config, descriptor = descriptor), title = title)
|
||||
|
||||
override val root: BorderPane = borderpane {
|
||||
center = treetableview<FXMeta<Config>> {
|
||||
root = TreeItem(rootNode)
|
||||
root.isExpanded = true
|
||||
sortMode = TreeSortMode.ALL_DESCENDANTS
|
||||
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
|
||||
populate {
|
||||
when (val fxMeta = it.value) {
|
||||
is FXMetaNode -> {
|
||||
fxMeta.children
|
||||
}
|
||||
is FXMetaValue -> null
|
||||
}
|
||||
}
|
||||
column("Name", FXMeta<Config>::name) {
|
||||
setCellFactory {
|
||||
object : TextFieldTreeTableCell<FXMeta<Config>, 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.parent != null && treeTableRow.treeItem.value.hasValue.get()) {
|
||||
contextmenu {
|
||||
item("Remove") {
|
||||
action {
|
||||
treeTableRow.item.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
column("Value") { param: TreeTableColumn.CellDataFeatures<FXMeta<Config>, FXMeta<Config>> ->
|
||||
param.value.valueProperty()
|
||||
}.setCellFactory {
|
||||
ValueCell()
|
||||
}
|
||||
|
||||
column("Description") { param: TreeTableColumn.CellDataFeatures<FXMeta<Config>, String> -> param.value.value.descriptionProperty }
|
||||
.setCellFactory { param: TreeTableColumn<FXMeta<Config>, String> ->
|
||||
val cell = TreeTableCell<FXMeta<Config>, 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<Config>, FXMeta<Config>?>() {
|
||||
|
||||
public override fun updateItem(item: FXMeta<Config>?, empty: Boolean) {
|
||||
if (!empty) {
|
||||
if (item != null) {
|
||||
when (item) {
|
||||
is FXMetaValue<Config> -> {
|
||||
text = null
|
||||
val chooser = ValueChooser.build(
|
||||
Global,
|
||||
item.valueProperty,
|
||||
item.descriptor
|
||||
) {
|
||||
item.set(it)
|
||||
}
|
||||
graphic = chooser.node
|
||||
}
|
||||
is FXMetaNode<Config> -> {
|
||||
if (allowNew) {
|
||||
text = null
|
||||
graphic = HBox().apply {
|
||||
val glyph: Node = FontAwesomeIconView(FontAwesomeIcon.PLUS_CIRCLE)
|
||||
button("node", graphic = glyph) {
|
||||
hgrow = Priority.ALWAYS
|
||||
maxWidth = Double.POSITIVE_INFINITY
|
||||
action {
|
||||
showNodeDialog()?.let {
|
||||
item.addNode(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
button("value", graphic = FontAwesomeIconView(FontAwesomeIcon.PLUS_SQUARE)) {
|
||||
hgrow = Priority.ALWAYS
|
||||
maxWidth = Double.POSITIVE_INFINITY
|
||||
action {
|
||||
showValueDialog()?.let {
|
||||
item.addValue(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} 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"
|
||||
}
|
||||
}
|
@ -1,223 +0,0 @@
|
||||
package space.kscience.visionforge.editor
|
||||
|
||||
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 space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.ItemDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.dataforge.values.Null
|
||||
import space.kscience.dataforge.values.Value
|
||||
import tornadofx.*
|
||||
|
||||
/**
|
||||
* A display for meta and descriptor
|
||||
*/
|
||||
sealed class FXMeta<M : TypedMeta<M>> : Comparable<FXMeta<*>> {
|
||||
abstract val name: NameToken
|
||||
abstract val parent: FXMetaNode<M>?
|
||||
abstract val descriptionProperty: ObservableStringValue
|
||||
abstract val descriptor: ItemDescriptor?
|
||||
|
||||
abstract val hasValue: ObservableBooleanValue
|
||||
|
||||
override fun compareTo(other: FXMeta<*>): Int {
|
||||
return if (this.hasValue.get() == other.hasValue.get()) {
|
||||
this.name.toString().compareTo(other.name.toString())
|
||||
} else {
|
||||
this.hasValue.get().compareTo(other.hasValue.get())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <M : TypedMeta<M>> root(
|
||||
node: M,
|
||||
descriptor: NodeDescriptor? = null,
|
||||
rootName: String = "root"
|
||||
): FXMetaNode<M> =
|
||||
FXMetaNode(NameToken(rootName), null, node, descriptor)
|
||||
|
||||
fun root(node: Meta, descriptor: NodeDescriptor? = null, rootName: String = "root"): FXMetaNode<SealedMeta> =
|
||||
root(node.seal(), descriptor, rootName)
|
||||
}
|
||||
}
|
||||
|
||||
class FXMetaNode<M : TypedMeta<M>>(
|
||||
override val name: NameToken,
|
||||
override val parent: FXMetaNode<M>?,
|
||||
nodeValue: M? = null,
|
||||
descriptorValue: NodeDescriptor? = null
|
||||
) : FXMeta<M>() {
|
||||
|
||||
/**
|
||||
* A descriptor that could be manually set to the node
|
||||
*/
|
||||
private val innerDescriptorProperty = SimpleObjectProperty(descriptorValue)
|
||||
|
||||
/**
|
||||
* Actual descriptor which holds value inferred from parrent
|
||||
*/
|
||||
val descriptorProperty = objectBinding(innerDescriptorProperty) {
|
||||
value ?: parent?.descriptor?.nodes?.get(this@FXMetaNode.name.body)
|
||||
}
|
||||
|
||||
override val descriptor: NodeDescriptor? by descriptorProperty
|
||||
|
||||
private val innerNodeProperty = SimpleObjectProperty(nodeValue)
|
||||
|
||||
val nodeProperty: ObjectBinding<M?> = objectBinding(innerNodeProperty) {
|
||||
value ?: parent?.node?.get(this@FXMetaNode.name).node
|
||||
}
|
||||
|
||||
val node: M? by nodeProperty
|
||||
|
||||
override val descriptionProperty = innerDescriptorProperty.stringBinding { it?.info ?: "" }
|
||||
|
||||
override val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null }
|
||||
|
||||
private val filter: (FXMeta<M>) -> Boolean = { cfg ->
|
||||
!(cfg.descriptor?.attributes?.get(ConfigEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false)
|
||||
}
|
||||
|
||||
val children = object : ListBinding<FXMeta<M>>() {
|
||||
|
||||
init {
|
||||
bind(nodeProperty, descriptorProperty)
|
||||
|
||||
val listener: (Name, MetaItem?, MetaItem?) -> Unit = { name, _, _ ->
|
||||
if (name.length == 1) invalidate()
|
||||
}
|
||||
|
||||
(node as? Config)?.onChange(this, listener)
|
||||
|
||||
nodeProperty.addListener { _, oldValue, newValue ->
|
||||
if (newValue == null) {
|
||||
(oldValue as? Config)?.removeListener(this)
|
||||
}
|
||||
|
||||
if (newValue is Config) {
|
||||
newValue.onChange(this, listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun computeValue(): ObservableList<FXMeta<M>> {
|
||||
val nodeKeys = node?.items?.keys?.toSet() ?: emptySet()
|
||||
val descriptorKeys = descriptor?.items?.keys?.map { NameToken(it) } ?: emptyList()
|
||||
val keys: Set<NameToken> = nodeKeys + descriptorKeys
|
||||
|
||||
val items = keys.map { token ->
|
||||
val actualItem = node?.items?.get(token)
|
||||
val actualDescriptor = descriptor?.items?.get(token.body)
|
||||
|
||||
if (actualItem is MetaItemNode || actualDescriptor is NodeDescriptor) {
|
||||
FXMetaNode(token, this@FXMetaNode)
|
||||
} else {
|
||||
FXMetaValue(token, this@FXMetaNode)
|
||||
}
|
||||
}
|
||||
|
||||
return items.filter(filter).asObservable()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if (parent != null) {
|
||||
parent.descriptorProperty.onChange { descriptorProperty.invalidate() }
|
||||
parent.nodeProperty.onChange { nodeProperty.invalidate() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FXMetaValue<M : TypedMeta<M>>(
|
||||
override val name: NameToken,
|
||||
override val parent: FXMetaNode<M>
|
||||
) : FXMeta<M>() {
|
||||
|
||||
public val descriptorProperty = parent.descriptorProperty.objectBinding {
|
||||
it?.values?.get(name.body)
|
||||
}
|
||||
|
||||
/**
|
||||
* A descriptor that could be manually set to the node
|
||||
*/
|
||||
override val descriptor: ValueDescriptor? by descriptorProperty
|
||||
|
||||
//private val innerValueProperty = SimpleObjectProperty(value)
|
||||
|
||||
public val valueProperty = descriptorProperty.objectBinding { descriptor ->
|
||||
parent.node?.get(name).value ?: descriptor?.default
|
||||
}
|
||||
|
||||
override val hasValue: ObservableBooleanValue = parent.nodeProperty.booleanBinding { it?.get(name) != null }
|
||||
|
||||
public val value by valueProperty
|
||||
|
||||
override val descriptionProperty = descriptorProperty.stringBinding { it?.info ?: "" }
|
||||
}
|
||||
|
||||
public fun <M : MutableMeta<M>> FXMetaNode<M>.remove(name: NameToken) {
|
||||
node?.remove(name.asName())
|
||||
children.invalidate()
|
||||
}
|
||||
|
||||
private fun <M : MutableMeta<M>> M.createEmptyNode(token: NameToken, append: Boolean): M {
|
||||
return if (append && token.hasIndex()) {
|
||||
val name = token.asName()
|
||||
val index = (getIndexed(name).keys.mapNotNull { it?.toIntOrNull() }.maxOrNull() ?: -1) + 1
|
||||
val newName = name.withIndex(index.toString())
|
||||
set(newName, Meta.EMPTY)
|
||||
get(newName).node!!
|
||||
} else {
|
||||
this.set(token.asName(), Meta.EMPTY)
|
||||
//FIXME possible concurrency bug
|
||||
get(token).node!!
|
||||
}
|
||||
}
|
||||
|
||||
fun <M : MutableMeta<M>> FXMetaNode<out M>.getOrCreateNode(): M {
|
||||
val node = node
|
||||
return when {
|
||||
node != null -> node
|
||||
parent != null -> parent.getOrCreateNode().createEmptyNode(this.name, descriptor?.multiple == true).also {
|
||||
parent.children.invalidate()
|
||||
}
|
||||
else -> kotlin.error("Orphan empty node is not allowed")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun <M : MutableMeta<M>> FXMeta<M>.remove() {
|
||||
parent?.node?.remove(name.asName())
|
||||
}
|
||||
|
||||
fun <M : MutableMeta<M>> FXMetaNode<M>.addValue(key: String) {
|
||||
val parent = getOrCreateNode()
|
||||
if (descriptor?.multiple == true) {
|
||||
parent.append(key, Null)
|
||||
} else {
|
||||
parent[key] = Null
|
||||
}
|
||||
}
|
||||
|
||||
fun <M : MutableMeta<M>> FXMetaNode<M>.addNode(key: String) {
|
||||
val parent = getOrCreateNode()
|
||||
if (descriptor?.multiple == true) {
|
||||
parent.append(key, Meta.EMPTY)
|
||||
} else {
|
||||
parent[key] = Meta.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
fun <M : MutableMeta<M>> FXMetaValue<M>.set(value: Value?) {
|
||||
if (descriptor?.multiple == true) {
|
||||
parent.getOrCreateNode().append(this.name.body, value)
|
||||
} else {
|
||||
parent.getOrCreateNode()[this.name] = value
|
||||
}
|
||||
}
|
@ -0,0 +1,217 @@
|
||||
package space.kscience.visionforge.editor
|
||||
|
||||
import javafx.beans.binding.Binding
|
||||
import javafx.beans.binding.BooleanBinding
|
||||
import javafx.beans.binding.ListBinding
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.collections.ObservableList
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.dataforge.values.Value
|
||||
import tornadofx.*
|
||||
|
||||
/**
|
||||
* A display for meta and descriptor
|
||||
*/
|
||||
public class FXMetaModel<M : Meta>(
|
||||
public val root: M,
|
||||
public val rootDescriptor: MetaDescriptor?,
|
||||
public val nodeName: Name,
|
||||
public val title: String = nodeName.lastOrNull()?.toString() ?: "Meta"
|
||||
) : Comparable<FXMetaModel<*>> {
|
||||
|
||||
private val existingNode = SimpleObjectProperty<Meta>(root[nodeName])
|
||||
|
||||
public val children: ListBinding<FXMetaModel<M>> = object : ListBinding<FXMetaModel<M>>() {
|
||||
override fun computeValue(): ObservableList<FXMetaModel<M>> {
|
||||
val nodeKeys = existingNode.get().items.keys
|
||||
val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptySet()
|
||||
return (nodeKeys + descriptorKeys).map {
|
||||
FXMetaModel(
|
||||
root,
|
||||
rootDescriptor,
|
||||
nodeName + it
|
||||
)
|
||||
}.filter(filter).asObservable()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
//add listener to the root node if possible
|
||||
if (root is ObservableMeta) {
|
||||
root.onChange(this) { changed ->
|
||||
if (changed.startsWith(nodeName)) {
|
||||
if (nodeName.length == changed.length) existingNode.set(root[nodeName])
|
||||
else if (changed.length == nodeName.length + 1) children.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public val descriptor: MetaDescriptor? = rootDescriptor?.get(nodeName)
|
||||
|
||||
public val existsProperty: BooleanBinding = existingNode.isNotNull
|
||||
|
||||
public val exists: Boolean by existsProperty
|
||||
|
||||
public val valueProperty: Binding<Value?> = existingNode.objectBinding {
|
||||
existingNode.get().value ?: descriptor?.defaultValue
|
||||
}
|
||||
|
||||
override fun compareTo(other: FXMetaModel<*>): Int = if (this.exists == other.exists) {
|
||||
this.nodeName.toString().compareTo(other.nodeName.toString())
|
||||
} else {
|
||||
this.exists.compareTo(other.exists)
|
||||
}
|
||||
|
||||
public companion object {
|
||||
private val filter: (FXMetaModel<*>) -> Boolean = { cfg ->
|
||||
!(cfg.descriptor?.attributes?.get(MutableMetaEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false)
|
||||
}
|
||||
|
||||
public fun <M : Meta> root(
|
||||
node: M,
|
||||
descriptor: MetaDescriptor? = null,
|
||||
rootName: String = "root"
|
||||
): FXMetaModel<M> = FXMetaModel(node, descriptor, Name.EMPTY, title = rootName)
|
||||
}
|
||||
|
||||
// /**
|
||||
// * A descriptor that could be manually set to the node
|
||||
// */
|
||||
// private val innerDescriptorProperty = SimpleObjectProperty(descriptorValue)
|
||||
//
|
||||
// /**
|
||||
// * Actual descriptor which holds value inferred from parrent
|
||||
// */
|
||||
// val descriptorProperty: ObjectBinding<MetaDescriptor?> = objectBinding(innerDescriptorProperty) {
|
||||
// value ?: parent?.descriptor?.get(this@FXMeta.name.body)
|
||||
// }
|
||||
//
|
||||
// val descriptor: MetaDescriptor? by descriptorProperty
|
||||
//
|
||||
// private val innerNodeProperty = SimpleObjectProperty(nodeValue)
|
||||
//
|
||||
// val nodeProperty: ObjectBinding<M> = objectBinding(innerNodeProperty) {
|
||||
// value ?: parent?.node?.get(this@FXMeta.name)
|
||||
// }
|
||||
//
|
||||
// val node by nodeProperty
|
||||
//
|
||||
// val hasValue: ObservableBooleanValue = nodeProperty.booleanBinding { it != null }
|
||||
//
|
||||
// private val filter: (FXMeta<M>) -> Boolean = { cfg ->
|
||||
// !(cfg.descriptor?.attributes?.get(MutableMetaEditor.NO_CONFIGURATOR_TAG)?.boolean ?: false)
|
||||
// }
|
||||
//
|
||||
// val children: ListBinding<FXMeta<M>> = object : ListBinding<FXMeta<M>>() {
|
||||
//
|
||||
// init {
|
||||
// bind(nodeProperty, descriptorProperty)
|
||||
//
|
||||
// val listener: Meta.(Name) -> Unit = { name ->
|
||||
// if (name.length == 1) invalidate()
|
||||
// }
|
||||
//
|
||||
// (node as? ObservableMeta)?.onChange(this, listener)
|
||||
//
|
||||
// nodeProperty.addListener { _, oldValue, newValue ->
|
||||
// if (newValue == null) {
|
||||
// (oldValue as? ObservableMeta)?.removeListener(this)
|
||||
// }
|
||||
//
|
||||
// if (newValue is ObservableMeta) {
|
||||
// newValue.onChange(this, listener)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun computeValue(): ObservableList<FXMeta<M>> {
|
||||
// val nodeKeys = node?.items?.keys?.toSet() ?: emptySet()
|
||||
// val descriptorKeys = descriptor?.children?.keys?.map { NameToken(it) } ?: emptyList()
|
||||
// val keys: Set<NameToken> = nodeKeys + descriptorKeys
|
||||
//
|
||||
// val items = keys.map { token ->
|
||||
// val actualItem = node?.items?.get(token)
|
||||
// val actualDescriptor = descriptor?.children?.get(token.body)
|
||||
//
|
||||
// if (actualItem is MetaNode) {
|
||||
// FXMetaNode(token, this@FXMetaNode)
|
||||
// } else {
|
||||
// FXMetaValue(token, this@FXMetaNode)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return items.filter(filter).asObservable()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// init {
|
||||
// if (parent != null) {
|
||||
// parent.descriptorProperty.onChange { descriptorProperty.invalidate() }
|
||||
// parent.nodeProperty.onChange { nodeProperty.invalidate() }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
//
|
||||
//internal fun <M : MutableMeta> FXMeta<M>.remove(name: NameToken) {
|
||||
// node?.remove(name.asName())
|
||||
// children.invalidate()
|
||||
//}
|
||||
//
|
||||
//private fun <M : MutableMeta> M.createEmptyNode(token: NameToken, append: Boolean): M {
|
||||
// return if (append && token.hasIndex()) {
|
||||
// val name = token.asName()
|
||||
// val index = (getIndexed(name).keys.mapNotNull { it?.toIntOrNull() }.maxOrNull() ?: -1) + 1
|
||||
// val newName = name.withIndex(index.toString())
|
||||
// set(newName, Meta.EMPTY)
|
||||
// get(newName).node
|
||||
// } else {
|
||||
// this.set(token.asName(), Meta.EMPTY)
|
||||
// //FIXME possible concurrency bug
|
||||
// get(token).node
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//internal fun <M : MutableMeta> FXMeta<out M>.getOrCreateNode(): M {
|
||||
// val node = node
|
||||
// return when {
|
||||
// node != null -> node
|
||||
// parent != null -> parent.getOrCreateNode().createEmptyNode(this.name, descriptor?.multiple == true).also {
|
||||
// parent.children.invalidate()
|
||||
// }
|
||||
// else -> kotlin.error("Orphan empty node is not allowed")
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
internal fun <M : MutableMeta> FXMetaModel<M>.remove() {
|
||||
root.remove(nodeName)
|
||||
}
|
||||
|
||||
//
|
||||
//internal fun <M : MutableMeta> FXMeta<M>.addValue(key: String) {
|
||||
// val parent = getOrCreateNode()
|
||||
// if (descriptor?.multiple == true) {
|
||||
// parent.append(key, Null)
|
||||
// } else {
|
||||
// parent[key] = Null
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//internal fun <M : MutableMeta> FXMeta<M>.addNode(key: String) {
|
||||
// val parent = getOrCreateNode()
|
||||
// if (descriptor?.multiple == true) {
|
||||
// parent.append(key, Meta.EMPTY)
|
||||
// } else {
|
||||
// parent[key] = Meta.EMPTY
|
||||
// }
|
||||
//}
|
||||
//
|
||||
internal fun <M : MutableMeta> FXMetaModel<M>.setValue(value: Value?) {
|
||||
root.setValue(nodeName, value)
|
||||
}
|
@ -16,45 +16,42 @@
|
||||
|
||||
package space.kscience.visionforge.editor
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.scene.control.TreeItem
|
||||
import javafx.scene.control.TreeSortMode
|
||||
import javafx.scene.control.TreeTableView
|
||||
import javafx.scene.layout.BorderPane
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.values.string
|
||||
import space.kscience.visionforge.dfIconView
|
||||
import tornadofx.*
|
||||
|
||||
class MetaViewer(val rootNode: FXMetaNode<*>, title: String = "Meta viewer") : Fragment(title,
|
||||
dfIconView
|
||||
) {
|
||||
constructor(meta: Meta, title: String = "Meta viewer"): this(
|
||||
FXMeta.root(
|
||||
meta
|
||||
),title = title)
|
||||
public class MetaViewer(
|
||||
private val rootNode: FXMetaModel<Meta>,
|
||||
title: String = "Meta viewer"
|
||||
) : Fragment(title, dfIconView) {
|
||||
|
||||
override val root = borderpane {
|
||||
public constructor(meta: Meta, title: String = "Meta viewer") : this(
|
||||
FXMetaModel.root(
|
||||
meta
|
||||
), title = title
|
||||
)
|
||||
|
||||
override val root: BorderPane = borderpane {
|
||||
center {
|
||||
treetableview<FXMeta<*>> {
|
||||
treetableview<FXMetaModel<*>> {
|
||||
isShowRoot = false
|
||||
root = TreeItem(rootNode)
|
||||
populate {
|
||||
when (val fxMeta = it.value) {
|
||||
is FXMetaNode -> {
|
||||
fxMeta.children
|
||||
}
|
||||
is FXMetaValue -> null
|
||||
}
|
||||
val fxMeta = it.value
|
||||
fxMeta.children
|
||||
}
|
||||
root.isExpanded = true
|
||||
sortMode = TreeSortMode.ALL_DESCENDANTS
|
||||
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
|
||||
column("Name", FXMeta<*>::name)
|
||||
column<FXMeta<*>, String>("Value") { cellDataFeatures ->
|
||||
when (val item = cellDataFeatures.value.value) {
|
||||
is FXMetaValue -> item.valueProperty.stringBinding { it?.string ?: "" }
|
||||
is FXMetaNode -> SimpleStringProperty("[node]")
|
||||
}
|
||||
column("Name", FXMetaModel<*>::title)
|
||||
column<FXMetaModel<*>, String>("Value") { cellDataFeatures ->
|
||||
val item = cellDataFeatures.value.value
|
||||
item.valueProperty.stringBinding { it?.string ?: "" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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 space.kscience.visionforge.editor
|
||||
|
||||
import javafx.scene.control.*
|
||||
import javafx.scene.control.cell.TextFieldTreeTableCell
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.paint.Color
|
||||
import javafx.scene.text.Text
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.visionforge.dfIconView
|
||||
import tornadofx.*
|
||||
|
||||
/**
|
||||
* A Configuration editor fragment
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public class MutableMetaEditor(
|
||||
public val rootNode: FXMetaModel<MutableMeta>,
|
||||
public val allowNew: Boolean = true,
|
||||
title: String = "Configuration editor"
|
||||
) : Fragment(title = title, icon = dfIconView) {
|
||||
//TODO replace parameters by properties
|
||||
|
||||
public constructor(
|
||||
MutableMeta: MutableMeta,
|
||||
descriptor: MetaDescriptor?,
|
||||
title: String = "Configuration editor"
|
||||
) :
|
||||
this(FXMetaModel.root(MutableMeta, descriptor = descriptor), title = title)
|
||||
|
||||
override val root: BorderPane = borderpane {
|
||||
center = treetableview<FXMetaModel<MutableMeta>> {
|
||||
root = TreeItem(rootNode)
|
||||
root.isExpanded = true
|
||||
sortMode = TreeSortMode.ALL_DESCENDANTS
|
||||
columnResizePolicy = TreeTableView.CONSTRAINED_RESIZE_POLICY
|
||||
populate {
|
||||
it.value.children
|
||||
}
|
||||
column("Name", FXMetaModel<MutableMeta>::title) {
|
||||
setCellFactory {
|
||||
object : TextFieldTreeTableCell<FXMetaModel<MutableMeta>, String>() {
|
||||
override fun updateItem(item: String?, empty: Boolean) {
|
||||
super.updateItem(item, empty)
|
||||
contextMenu?.items?.removeIf { it.text == "Remove" }
|
||||
val content = treeTableRow.item
|
||||
if (!empty) {
|
||||
if (treeTableRow.item != null) {
|
||||
textFillProperty().bind(content.existsProperty.objectBinding {
|
||||
if (it == true) {
|
||||
Color.BLACK
|
||||
} else {
|
||||
Color.GRAY
|
||||
}
|
||||
})
|
||||
if (content.exists) {
|
||||
contextmenu {
|
||||
item("Remove") {
|
||||
action {
|
||||
content.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
column("Value") { param: TreeTableColumn.CellDataFeatures<FXMetaModel<MutableMeta>, FXMetaModel<MutableMeta>> ->
|
||||
param.value.valueProperty()
|
||||
}.setCellFactory {
|
||||
ValueCell()
|
||||
}
|
||||
|
||||
column("Description") { param: TreeTableColumn.CellDataFeatures<FXMetaModel<MutableMeta>, String> ->
|
||||
(param.value.value.descriptor?.info ?: "").observable()
|
||||
}.setCellFactory { param: TreeTableColumn<FXMetaModel<MutableMeta>, String> ->
|
||||
val cell = TreeTableCell<FXMetaModel<MutableMeta>, 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<FXMetaModel<MutableMeta>, FXMetaModel<MutableMeta>?>() {
|
||||
|
||||
public override fun updateItem(item: FXMetaModel<MutableMeta>?, empty: Boolean) {
|
||||
if (!empty) {
|
||||
if (item != null) {
|
||||
text = null
|
||||
val chooser = ValueChooser.build(
|
||||
Global,
|
||||
item.valueProperty,
|
||||
item.descriptor
|
||||
) {
|
||||
item.setValue(it)
|
||||
}
|
||||
graphic = chooser.node
|
||||
// when (item) {
|
||||
// is FXMetaValue<MutableMeta> -> {
|
||||
// text = null
|
||||
// val chooser = ValueChooser.build(
|
||||
// Global,
|
||||
// item.valueProperty,
|
||||
// item.descriptor
|
||||
// ) {
|
||||
// item.set(it)
|
||||
// }
|
||||
// graphic = chooser.node
|
||||
// }
|
||||
// is FXMetaNode<MutableMeta> -> {
|
||||
// if (allowNew) {
|
||||
// text = null
|
||||
// graphic = HBox().apply {
|
||||
// val glyph: Node = FontAwesomeIconView(FontAwesomeIcon.PLUS_CIRCLE)
|
||||
// button("node", graphic = glyph) {
|
||||
// hgrow = Priority.ALWAYS
|
||||
// maxWidth = Double.POSITIVE_INFINITY
|
||||
// action {
|
||||
// showNodeDialog()?.let {
|
||||
// item.addNode(it)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// button("value", graphic = FontAwesomeIconView(FontAwesomeIcon.PLUS_SQUARE)) {
|
||||
// hgrow = Priority.ALWAYS
|
||||
// maxWidth = Double.POSITIVE_INFINITY
|
||||
// action {
|
||||
// showValueDialog()?.let {
|
||||
// item.addValue(it)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// text = ""
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
} else {
|
||||
text = null
|
||||
graphic = null
|
||||
}
|
||||
} else {
|
||||
text = null
|
||||
graphic = null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* The tag not to display node or value in MutableMetaurator
|
||||
*/
|
||||
public const val NO_CONFIGURATOR_TAG: String = "nocfg"
|
||||
}
|
||||
}
|
@ -10,12 +10,13 @@ import javafx.scene.control.TextField
|
||||
import javafx.scene.input.KeyCode
|
||||
import javafx.scene.input.KeyEvent
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.descriptors.validate
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.values.*
|
||||
import tornadofx.*
|
||||
|
||||
class TextValueChooser : ValueChooserBase<TextField>() {
|
||||
public class TextValueChooser : ValueChooserBase<TextField>() {
|
||||
|
||||
private val displayText: String
|
||||
get() = currentValue().let {
|
||||
@ -85,7 +86,7 @@ class TextValueChooser : ValueChooserBase<TextField>() {
|
||||
}
|
||||
|
||||
private fun validate(value: Value): Boolean {
|
||||
return descriptor?.isAllowedValue(value) ?: true
|
||||
return descriptor?.validate(value) ?: true
|
||||
}
|
||||
|
||||
// @Override
|
||||
@ -101,7 +102,7 @@ class TextValueChooser : ValueChooserBase<TextField>() {
|
||||
}
|
||||
}
|
||||
|
||||
companion object : ValueChooser.Factory {
|
||||
public companion object : ValueChooser.Factory {
|
||||
override val name: Name = "text".asName()
|
||||
override fun invoke(meta: Meta): ValueChooser =
|
||||
TextValueChooser()
|
||||
|
@ -13,11 +13,11 @@ import space.kscience.dataforge.values.Value
|
||||
* @param value Value after change
|
||||
* @param message Message on unsuccessful change
|
||||
*/
|
||||
class ValueCallbackResponse(val success: Boolean, val value: Value, val message: String)
|
||||
public class ValueCallbackResponse(public val success: Boolean, public val value: Value, public 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
|
||||
public typealias ValueCallback = (Value) -> ValueCallbackResponse
|
||||
|
||||
|
@ -10,10 +10,12 @@ import javafx.beans.value.ObservableValue
|
||||
import javafx.scene.Node
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.allowedValues
|
||||
import space.kscience.dataforge.meta.descriptors.validate
|
||||
import space.kscience.dataforge.misc.Named
|
||||
import space.kscience.dataforge.misc.Type
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.provider.provideByType
|
||||
import space.kscience.dataforge.values.Null
|
||||
import space.kscience.dataforge.values.Value
|
||||
@ -42,8 +44,8 @@ public interface ValueChooser {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public val descriptorProperty: ObjectProperty<ValueDescriptor?>
|
||||
public var descriptor: ValueDescriptor?
|
||||
public val descriptorProperty: ObjectProperty<MetaDescriptor?>
|
||||
public var descriptor: MetaDescriptor?
|
||||
|
||||
public val valueProperty: ObjectProperty<Value?>
|
||||
public var value: Value?
|
||||
@ -71,7 +73,7 @@ public interface ValueChooser {
|
||||
public companion object {
|
||||
|
||||
private fun findWidgetByType(context: Context, type: String): Factory? {
|
||||
return when (type.toName()) {
|
||||
return when (Name.parse(type)) {
|
||||
TextValueChooser.name -> TextValueChooser
|
||||
ColorValueChooser.name -> ColorValueChooser
|
||||
ComboBoxValueChooser.name -> ComboBoxValueChooser
|
||||
@ -79,7 +81,7 @@ public interface ValueChooser {
|
||||
}
|
||||
}
|
||||
|
||||
private fun build(context: Context, descriptor: ValueDescriptor?): ValueChooser {
|
||||
private fun build(context: Context, descriptor: MetaDescriptor?): ValueChooser {
|
||||
return if (descriptor == null) {
|
||||
TextValueChooser();
|
||||
} else {
|
||||
@ -93,7 +95,7 @@ public interface ValueChooser {
|
||||
descriptor.widget
|
||||
) ?: TextValueChooser()
|
||||
}
|
||||
descriptor.allowedValues.isNotEmpty() -> ComboBoxValueChooser()
|
||||
!descriptor.allowedValues.isNullOrEmpty() -> ComboBoxValueChooser()
|
||||
else -> TextValueChooser()
|
||||
}
|
||||
chooser.descriptor = descriptor
|
||||
@ -101,10 +103,10 @@ public interface ValueChooser {
|
||||
}
|
||||
}
|
||||
|
||||
fun build(
|
||||
public fun build(
|
||||
context: Context,
|
||||
value: ObservableValue<Value?>,
|
||||
descriptor: ValueDescriptor? = null,
|
||||
descriptor: MetaDescriptor? = null,
|
||||
setter: (Value) -> Unit,
|
||||
): ValueChooser {
|
||||
val chooser = build(context, descriptor)
|
||||
@ -113,7 +115,7 @@ public interface ValueChooser {
|
||||
chooser.setDisplayValue(it ?: Null)
|
||||
}
|
||||
chooser.setCallback { result ->
|
||||
if (descriptor?.isAllowedValue(result) != false) {
|
||||
if (descriptor?.validate(result) != false) {
|
||||
setter(result)
|
||||
ValueCallbackResponse(true, result, "OK")
|
||||
} else {
|
||||
|
@ -8,7 +8,7 @@ package space.kscience.visionforge.editor
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.scene.Node
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.values.Null
|
||||
import space.kscience.dataforge.values.Value
|
||||
import tornadofx.*
|
||||
@ -18,16 +18,16 @@ import tornadofx.*
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
abstract class ValueChooserBase<out T : Node> : ValueChooser {
|
||||
public 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 val node: T by lazy { buildNode() }
|
||||
final override val valueProperty: SimpleObjectProperty<Value> = SimpleObjectProperty<Value>(Null)
|
||||
final override val descriptorProperty: SimpleObjectProperty<MetaDescriptor> = SimpleObjectProperty<MetaDescriptor>()
|
||||
|
||||
override var descriptor: ValueDescriptor? by descriptorProperty
|
||||
override var descriptor: MetaDescriptor? by descriptorProperty
|
||||
override var value: Value? by valueProperty
|
||||
|
||||
fun resetValue() {
|
||||
public fun resetValue() {
|
||||
setDisplayValue(currentValue())
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ abstract class ValueChooserBase<out T : Node> : ValueChooser {
|
||||
* @return
|
||||
*/
|
||||
protected fun currentValue(): Value {
|
||||
return value ?: descriptor?.default ?: Null
|
||||
return value ?: descriptor?.defaultValue ?: Null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,39 +5,36 @@ import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.Parent
|
||||
import javafx.scene.layout.VBox
|
||||
import space.kscience.dataforge.meta.Config
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MutableItemProvider
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.update
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.visionforge.*
|
||||
import tornadofx.*
|
||||
|
||||
class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
|
||||
public class VisionEditorFragment(public val selector: (Vision) -> Meta) : Fragment() {
|
||||
|
||||
val itemProperty = SimpleObjectProperty<Vision>()
|
||||
var item: Vision? by itemProperty
|
||||
val descriptorProperty = SimpleObjectProperty<NodeDescriptor>()
|
||||
public val itemProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
|
||||
public var item: Vision? by itemProperty
|
||||
public val descriptorProperty: SimpleObjectProperty<MetaDescriptor> = SimpleObjectProperty<MetaDescriptor>()
|
||||
|
||||
constructor(
|
||||
public constructor(
|
||||
item: Vision?,
|
||||
descriptor: NodeDescriptor?,
|
||||
selector: (Vision) -> MutableItemProvider = { it.allProperties() },
|
||||
descriptor: MetaDescriptor?,
|
||||
selector: (Vision) -> MutableMetaProvider = { it.meta() },
|
||||
) : this({ it.describedProperties }) {
|
||||
this.item = item
|
||||
this.descriptorProperty.set(descriptor)
|
||||
}
|
||||
|
||||
private var currentConfig: Config? = null
|
||||
private var currentConfig: ObservableMutableMeta? = null
|
||||
|
||||
private val configProperty: Binding<Config?> = itemProperty.objectBinding { visualObject ->
|
||||
if (visualObject == null) return@objectBinding null
|
||||
val meta = selector(visualObject)
|
||||
val config = Config().apply {
|
||||
private val configProperty: Binding<ObservableMutableMeta?> = itemProperty.objectBinding { vision ->
|
||||
if (vision == null) return@objectBinding null
|
||||
val meta = selector(vision)
|
||||
val config = MutableMeta {
|
||||
update(meta)
|
||||
onChange(this@VisualObjectEditorFragment) { key, _, after ->
|
||||
visualObject.setProperty(key, after)
|
||||
}
|
||||
}
|
||||
config.onChange(this@VisionEditorFragment) { key ->
|
||||
vision.setPropertyNode(key, config[key])
|
||||
}
|
||||
//remember old config reference to cleanup listeners
|
||||
currentConfig?.removeListener(this)
|
||||
@ -47,7 +44,7 @@ class VisualObjectEditorFragment(val selector: (Vision) -> Meta) : Fragment() {
|
||||
|
||||
private val configEditorProperty: Binding<Node?> = configProperty.objectBinding(descriptorProperty) {
|
||||
it?.let {
|
||||
ConfigEditor(it, descriptorProperty.get()).root
|
||||
MutableMetaEditor(it, descriptorProperty.get()).root
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package space.kscience.visionforge.editor
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.scene.control.SelectionMode
|
||||
import javafx.scene.control.TreeItem
|
||||
import javafx.scene.layout.VBox
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionGroup
|
||||
import tornadofx.*
|
||||
@ -29,13 +30,13 @@ private fun toTreeItem(vision: Vision, title: String): TreeItem<Pair<String, Vis
|
||||
}
|
||||
|
||||
|
||||
class VisualObjectTreeFragment : Fragment() {
|
||||
val itemProperty = SimpleObjectProperty<Vision>()
|
||||
var item: Vision? by itemProperty
|
||||
public class VisionTreeFragment : Fragment() {
|
||||
public val itemProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
|
||||
public var item: Vision? by itemProperty
|
||||
|
||||
val selectedProperty = SimpleObjectProperty<Vision>()
|
||||
public val selectedProperty: SimpleObjectProperty<Vision> = SimpleObjectProperty<Vision>()
|
||||
|
||||
override val root = vbox {
|
||||
override val root: VBox = vbox {
|
||||
titledpane("Object tree", collapsible = false) {
|
||||
treeview<Pair<String, Vision>> {
|
||||
cellFormat {
|
@ -3,11 +3,15 @@ package space.kscience.visionforge.solid
|
||||
import javafx.scene.paint.Color
|
||||
import javafx.scene.paint.Material
|
||||
import javafx.scene.paint.PhongMaterial
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.double
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.dataforge.values.int
|
||||
import space.kscience.dataforge.values.string
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.solid.FXMaterials.GREY
|
||||
|
||||
public object FXMaterials {
|
||||
public val RED: PhongMaterial = PhongMaterial().apply {
|
||||
@ -26,46 +30,43 @@ public object FXMaterials {
|
||||
}
|
||||
|
||||
public val BLUE: PhongMaterial = PhongMaterial(Color.BLUE)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Infer color based on meta item
|
||||
* @param opacity default opacity
|
||||
*/
|
||||
public fun MetaItem.color(opacity: Double = 1.0): Color {
|
||||
return when (this) {
|
||||
is MetaItemValue -> if (this.value.type == ValueType.NUMBER) {
|
||||
val int = value.int
|
||||
public fun Meta.color(opacity: Double = 1.0): Color {
|
||||
return value?.let {
|
||||
if (it.type == ValueType.NUMBER) {
|
||||
val int = it.int
|
||||
val red = int and 0x00ff0000 shr 16
|
||||
val green = int and 0x0000ff00 shr 8
|
||||
val blue = int and 0x000000ff
|
||||
Color.rgb(red, green, blue, opacity)
|
||||
} else {
|
||||
Color.web(this.value.string)
|
||||
Color.web(it.string)
|
||||
}
|
||||
is MetaItemNode -> {
|
||||
Color.rgb(
|
||||
node[Colors.RED_KEY]?.int ?: 0,
|
||||
node[Colors.GREEN_KEY]?.int ?: 0,
|
||||
node[Colors.BLUE_KEY]?.int ?: 0,
|
||||
node[SolidMaterial.OPACITY_KEY]?.double ?: opacity
|
||||
)
|
||||
}
|
||||
}
|
||||
} ?: Color.rgb(
|
||||
this[Colors.RED_KEY]?.int ?: 0,
|
||||
this[Colors.GREEN_KEY]?.int ?: 0,
|
||||
this[Colors.BLUE_KEY]?.int ?: 0,
|
||||
this[SolidMaterial.OPACITY_KEY]?.double ?: opacity
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Infer FX material based on meta item
|
||||
*/
|
||||
public fun MetaItem?.material(): Material {
|
||||
return when (this) {
|
||||
null -> FXMaterials.GREY
|
||||
is MetaItemValue -> PhongMaterial(color())
|
||||
is MetaItemNode -> PhongMaterial().apply {
|
||||
val opacity = node[SolidMaterial.OPACITY_KEY].double ?: 1.0
|
||||
diffuseColor = node[SolidMaterial.COLOR_KEY]?.color(opacity) ?: Color.DARKGREY
|
||||
specularColor = node[SolidMaterial.SPECULAR_COLOR_KEY]?.color(opacity) ?: Color.WHITE
|
||||
}
|
||||
public fun Meta?.material(): Material {
|
||||
if (this == null) return GREY
|
||||
return value?.let {
|
||||
PhongMaterial(color())
|
||||
} ?: PhongMaterial().apply {
|
||||
val opacity = get(SolidMaterial.OPACITY_KEY).double ?: 1.0
|
||||
diffuseColor = get(SolidMaterial.COLOR_KEY)?.color(opacity) ?: Color.DARKGREY
|
||||
specularColor = get(SolidMaterial.SPECULAR_COLOR_KEY)?.color(opacity) ?: Color.WHITE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,10 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import javafx.scene.Group
|
||||
import javafx.scene.Node
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.cutFirst
|
||||
import space.kscience.dataforge.names.firstOrNull
|
||||
import space.kscience.dataforge.names.isEmpty
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
import kotlin.reflect.KClass
|
||||
@ -16,7 +19,7 @@ class FXReferenceFactory(val plugin: FX3DPlugin) : FX3DFactory<SolidReferenceGro
|
||||
|
||||
obj.onPropertyChange(plugin.context) { name->
|
||||
if (name.firstOrNull()?.body == SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX) {
|
||||
val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'")
|
||||
val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
|
||||
val propertyName = name.cutFirst()
|
||||
val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found")
|
||||
val child = node.findChild(childName) ?: error("Object child with name '$childName' not found")
|
||||
|
@ -5,7 +5,6 @@ import javafx.beans.binding.*
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.startsWith
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.values.Value
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
@ -15,7 +14,7 @@ import tornadofx.*
|
||||
* A caching binding collection for [Vision] properties
|
||||
*/
|
||||
public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vision) {
|
||||
private val bindings = HashMap<Name, ObjectBinding<MetaItem?>>()
|
||||
private val bindings = HashMap<Name, ObjectBinding<Meta?>>()
|
||||
|
||||
init {
|
||||
obj.onPropertyChange(fx.context) { name ->
|
||||
@ -33,30 +32,29 @@ public class VisualObjectFXBinding(public val fx: FX3DPlugin, public val obj: Vi
|
||||
}
|
||||
}
|
||||
|
||||
public operator fun get(key: Name): ObjectBinding<MetaItem?> {
|
||||
public operator fun get(key: Name): ObjectBinding<Meta?> {
|
||||
return bindings.getOrPut(key) {
|
||||
object : ObjectBinding<MetaItem?>() {
|
||||
override fun computeValue(): MetaItem? = obj.getProperty(key)
|
||||
object : ObjectBinding<Meta?>() {
|
||||
override fun computeValue(): Meta? = obj.getProperty(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public operator fun get(key: String) = get(key.toName())
|
||||
public operator fun get(key: String): ObjectBinding<Meta?> = get(Name.parse(key))
|
||||
}
|
||||
|
||||
public fun ObjectBinding<MetaItem?>.value(): Binding<Value?> = objectBinding { it.value }
|
||||
public fun ObjectBinding<MetaItem?>.string(): StringBinding = stringBinding { it.string }
|
||||
public fun ObjectBinding<MetaItem?>.number(): Binding<Number?> = objectBinding { it.number }
|
||||
public fun ObjectBinding<MetaItem?>.double(): Binding<Double?> = objectBinding { it.double }
|
||||
public fun ObjectBinding<MetaItem?>.float(): Binding<Float?> = objectBinding { it.float }
|
||||
public fun ObjectBinding<MetaItem?>.int(): Binding<Int?> = objectBinding { it.int }
|
||||
public fun ObjectBinding<MetaItem?>.long(): Binding<Long?> = objectBinding { it.long }
|
||||
public fun ObjectBinding<MetaItem?>.node(): Binding<Meta?> = objectBinding { it.node }
|
||||
public fun ObjectBinding<Meta?>.value(): Binding<Value?> = objectBinding { it?.value }
|
||||
public fun ObjectBinding<Meta?>.string(): StringBinding = stringBinding { it.string }
|
||||
public fun ObjectBinding<Meta?>.number(): Binding<Number?> = objectBinding { it.number }
|
||||
public fun ObjectBinding<Meta?>.double(): Binding<Double?> = objectBinding { it.double }
|
||||
public fun ObjectBinding<Meta?>.float(): Binding<Float?> = objectBinding { it.float }
|
||||
public fun ObjectBinding<Meta?>.int(): Binding<Int?> = objectBinding { it.int }
|
||||
public fun ObjectBinding<Meta?>.long(): Binding<Long?> = objectBinding { it.long }
|
||||
|
||||
public fun ObjectBinding<MetaItem?>.string(default: String): StringBinding = stringBinding { it.string ?: default }
|
||||
public fun ObjectBinding<MetaItem?>.double(default: Double): DoubleBinding = doubleBinding { it.double ?: default }
|
||||
public fun ObjectBinding<MetaItem?>.float(default: Float): FloatBinding = floatBinding { it.float ?: default }
|
||||
public fun ObjectBinding<MetaItem?>.int(default: Int): IntegerBinding = integerBinding { it.int ?: default }
|
||||
public fun ObjectBinding<MetaItem?>.long(default: Long): LongBinding = longBinding { it.long ?: default }
|
||||
public fun ObjectBinding<Meta?>.string(default: String): StringBinding = stringBinding { it.string ?: default }
|
||||
public fun ObjectBinding<Meta?>.double(default: Double): DoubleBinding = doubleBinding { it.double ?: default }
|
||||
public fun ObjectBinding<Meta?>.float(default: Float): FloatBinding = floatBinding { it.float ?: default }
|
||||
public fun ObjectBinding<Meta?>.int(default: Int): IntegerBinding = integerBinding { it.int ?: default }
|
||||
public fun ObjectBinding<Meta?>.long(default: Long): LongBinding = longBinding { it.long ?: default }
|
||||
|
||||
public fun <T> ObjectBinding<MetaItem?>.transform(transform: (MetaItem) -> T): Binding<T?> = objectBinding { it?.let(transform) }
|
||||
public fun <T> ObjectBinding<Meta?>.transform(transform: (Meta) -> T): Binding<T?> = objectBinding { it?.let(transform) }
|
||||
|
@ -1,12 +1,12 @@
|
||||
package space.kscience.visionforge.gdml
|
||||
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.MetaBuilder
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.dataforge.names.toName
|
||||
|
||||
import space.kscience.gdml.*
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.visionforge.html.VisionOutput
|
||||
@ -41,8 +41,8 @@ public class GdmlTransformer {
|
||||
|
||||
internal val styleCache = HashMap<Name, Meta>()
|
||||
|
||||
public fun Solid.registerAndUseStyle(name: String, builder: MetaBuilder.() -> Unit) {
|
||||
styleCache.getOrPut(name.toName()) {
|
||||
public fun Solid.registerAndUseStyle(name: String, builder: MutableMeta.() -> Unit) {
|
||||
styleCache.getOrPut(Name.parse(name)) {
|
||||
Meta(builder)
|
||||
}
|
||||
useStyle(name)
|
||||
@ -118,7 +118,7 @@ private class GdmlTransformerEnv(val settings: GdmlTransformer) {
|
||||
private val proto = SolidGroup()
|
||||
|
||||
private val solids = proto.group(solidsName) {
|
||||
setProperty("edges.enabled", false)
|
||||
setPropertyNode("edges.enabled", false)
|
||||
}
|
||||
|
||||
private val referenceStore = HashMap<Name, MutableList<SolidReferenceGroup>>()
|
||||
|
@ -1,7 +1,7 @@
|
||||
package space.kscience.visionforge.gdml
|
||||
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.gdml.*
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.get
|
||||
@ -23,7 +23,7 @@ class TestCubes {
|
||||
fun testCubesDirect() {
|
||||
val vision = cubes.toVision()
|
||||
// println(Solids.encodeToString(vision))
|
||||
val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box
|
||||
val smallBoxPrototype = vision.getPrototype(Name.parse("solids.smallBox")) as? Box
|
||||
assertNotNull(smallBoxPrototype)
|
||||
assertEquals(30.0, smallBoxPrototype.xSize.toDouble())
|
||||
val smallBoxVision = vision["composite-111.smallBox"]?.unref as? Box
|
||||
@ -46,7 +46,7 @@ class TestCubes {
|
||||
val vision = cubes.toVision()
|
||||
val serialized = Solids.encodeToString(vision)
|
||||
val deserialized = testContext.visionManager.decodeFromString(serialized) as SolidGroup
|
||||
val smallBox = deserialized.getPrototype("solids.smallBox".toName()) as? Box
|
||||
val smallBox = deserialized.getPrototype(Name.parse("solids.smallBox")) as? Box
|
||||
assertNotNull(smallBox)
|
||||
assertEquals(30.0, smallBox.xSize.toDouble())
|
||||
//println(testContext.visionManager.encodeToString(deserialized))
|
||||
|
@ -1,7 +1,7 @@
|
||||
package space.kscience.visionforge.gdml
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.gdml.Gdml
|
||||
import space.kscience.gdml.decodeFromStream
|
||||
import space.kscience.visionforge.solid.Solids
|
||||
@ -23,7 +23,7 @@ class TestConvertor {
|
||||
val stream = javaClass.getResourceAsStream("/gdml/cubes.gdml")!!
|
||||
val gdml = Gdml.decodeFromStream(stream)
|
||||
val vision = gdml.toVision()
|
||||
assertNotNull(vision.getPrototype("solids.box".toName()))
|
||||
assertNotNull(vision.getPrototype(Name.parse("solids.box")))
|
||||
println(Solids.encodeToString(vision))
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import kotlinx.serialization.modules.polymorphic
|
||||
import kotlinx.serialization.modules.subclass
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionBase
|
||||
import space.kscience.visionforge.setProperty
|
||||
@ -27,7 +27,7 @@ public class VisionOfMarkup(
|
||||
}
|
||||
|
||||
public companion object {
|
||||
public val CONTENT_PROPERTY_KEY: Name = "content".toName()
|
||||
public val CONTENT_PROPERTY_KEY: Name = "content".asName()
|
||||
public const val COMMONMARK_FORMAT: String = "markdown.commonmark"
|
||||
public const val GFM_FORMAT: String = "markdown.gfm"
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ plugins {
|
||||
id("ru.mipt.npm.gradle.mpp")
|
||||
}
|
||||
|
||||
val plotlyVersion = "0.4.3"
|
||||
val plotlyVersion = "0.5.0-dev-1"
|
||||
|
||||
kscience {
|
||||
useSerialization()
|
||||
|
@ -2,6 +2,8 @@ package space.kscience.visionforge.plotly
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.asObservable
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.plotly.Plot
|
||||
import space.kscience.plotly.Plotly
|
||||
@ -13,10 +15,10 @@ import space.kscience.visionforge.root
|
||||
@SerialName("vision.plotly")
|
||||
public class VisionOfPlotly private constructor() : VisionBase() {
|
||||
public constructor(plot: Plot) : this() {
|
||||
properties = plot.config
|
||||
properties = plot.meta
|
||||
}
|
||||
|
||||
public val plot: Plot get() = Plot(properties ?: Config())
|
||||
public val plot: Plot get() = Plot(properties?.asObservable() ?: MutableMeta())
|
||||
}
|
||||
|
||||
public fun Plot.asVision(): VisionOfPlotly = VisionOfPlotly(this)
|
||||
|
@ -31,7 +31,7 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
|
||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
||||
val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found")
|
||||
val config = PlotlyConfig.read(meta)
|
||||
println(plot.config)
|
||||
println(plot.meta)
|
||||
println(plot.data[0].toMeta())
|
||||
element.plot(plot, config)
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionChange
|
||||
import space.kscience.visionforge.VisionManager
|
||||
@ -54,12 +53,12 @@ public class VisionServer internal constructor(
|
||||
private val application: Application,
|
||||
private val rootRoute: String,
|
||||
) : Configurable, CoroutineScope by application {
|
||||
override val config: ObservableMeta = ObservableMeta()
|
||||
public var updateInterval: Long by config.long(300, key = UPDATE_INTERVAL_KEY)
|
||||
public var cacheFragments: Boolean by config.boolean(true)
|
||||
public var dataEmbed: Boolean by config.boolean(true, "data.embed".toName())
|
||||
public var dataFetch: Boolean by config.boolean(false, "data.fetch".toName())
|
||||
public var dataConnect: Boolean by config.boolean(true, "data.connect".toName())
|
||||
override val meta: ObservableMutableMeta = MutableMeta()
|
||||
public var updateInterval: Long by meta.long(300, key = UPDATE_INTERVAL_KEY)
|
||||
public var cacheFragments: Boolean by meta.boolean(true)
|
||||
public var dataEmbed: Boolean by meta.boolean(true, Name.parse("data.embed"))
|
||||
public var dataFetch: Boolean by meta.boolean(false, Name.parse("data.fetch"))
|
||||
public var dataConnect: Boolean by meta.boolean(true, Name.parse("data.connect"))
|
||||
|
||||
/**
|
||||
* a list of headers that should be applied to all pages
|
||||
@ -131,14 +130,14 @@ public class VisionServer internal constructor(
|
||||
?: error("Vision name is not defined in parameters")
|
||||
|
||||
application.log.debug("Opened server socket for $name")
|
||||
val vision: Vision = visions[name.toName()] ?: error("Plot with id='$name' not registered")
|
||||
val vision: Vision = visions[Name.parse(name)] ?: error("Plot with id='$name' not registered")
|
||||
|
||||
launch {
|
||||
incoming.consumeEach {
|
||||
val change = visionManager.jsonFormat.decodeFromString(
|
||||
VisionChange.serializer(), it.data.decodeToString()
|
||||
)
|
||||
vision.update(change)
|
||||
vision.change(change)
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,7 +160,7 @@ public class VisionServer internal constructor(
|
||||
val name: String = call.request.queryParameters["name"]
|
||||
?: error("Vision name is not defined in parameters")
|
||||
|
||||
val vision: Vision? = visions[name.toName()]
|
||||
val vision: Vision? = visions[Name.parse(name)]
|
||||
if (vision == null) {
|
||||
call.respond(HttpStatusCode.NotFound, "Vision with name '$name' not found")
|
||||
} else {
|
||||
@ -232,7 +231,7 @@ public class VisionServer internal constructor(
|
||||
|
||||
public companion object {
|
||||
public const val DEFAULT_PAGE: String = "/"
|
||||
public val UPDATE_INTERVAL_KEY: Name = "update.interval".toName()
|
||||
public val UPDATE_INTERVAL_KEY: Name = Name.parse("update.interval")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.meta.MutableItemProvider
|
||||
import space.kscience.dataforge.meta.set
|
||||
import space.kscience.dataforge.meta.value
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.values.Value
|
||||
import space.kscience.dataforge.values.asValue
|
||||
@ -11,11 +9,17 @@ import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
|
||||
@VisionBuilder
|
||||
public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) {
|
||||
public class ColorAccessor(private val parent: MutableMetaProvider, private val colorKey: Name) {
|
||||
public var value: Value?
|
||||
get() = parent.getItem(colorKey).value
|
||||
get() = parent.getMeta(colorKey)?.value
|
||||
set(value) {
|
||||
parent[colorKey] = value
|
||||
parent.setValue(colorKey,value)
|
||||
}
|
||||
|
||||
public var item: Meta?
|
||||
get() = parent.getMeta(colorKey)
|
||||
set(value) {
|
||||
parent.setMeta(colorKey,value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ public inline fun VisionContainerBuilder<Solid>.composite(
|
||||
if (children.size != 2) error("Composite requires exactly two children")
|
||||
return Composite(type, children[0], children[1]).also { composite ->
|
||||
composite.configure {
|
||||
update(group.meta)
|
||||
update(group.meta())
|
||||
}
|
||||
if (group.position != null) {
|
||||
composite.position = group.position
|
||||
|
@ -2,7 +2,8 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.ObservableMeta
|
||||
import space.kscience.dataforge.meta.MutableMeta
|
||||
import space.kscience.dataforge.meta.ObservableMutableMeta
|
||||
import space.kscience.visionforge.*
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
@ -95,7 +96,7 @@ public class Extruded(
|
||||
public class ExtrudeBuilder(
|
||||
public var shape: List<Point2D> = emptyList(),
|
||||
public var layers: ArrayList<Layer> = ArrayList(),
|
||||
config: ObservableMeta = ObservableMeta()
|
||||
config: ObservableMutableMeta = MutableMeta()
|
||||
) : SimpleVisionPropertyContainer<Extruded>(config) {
|
||||
public fun shape(block: Shape2DBuilder.() -> Unit) {
|
||||
this.shape = Shape2DBuilder().apply(block).build()
|
||||
@ -105,7 +106,7 @@ public class ExtrudeBuilder(
|
||||
layers.add(Layer(x.toFloat(), y.toFloat(), z.toFloat(), scale.toFloat()))
|
||||
}
|
||||
|
||||
internal fun build(): Extruded = Extruded(shape, layers).apply { configure(config) }
|
||||
internal fun build(): Extruded = Extruded(shape, layers).apply { configure(meta()) }
|
||||
}
|
||||
|
||||
@VisionBuilder
|
||||
|
@ -2,13 +2,12 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.number
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
import space.kscience.visionforge.VisionBuilder
|
||||
import space.kscience.visionforge.VisionContainerBuilder
|
||||
import space.kscience.visionforge.allProperties
|
||||
import space.kscience.visionforge.numberProperty
|
||||
import space.kscience.visionforge.set
|
||||
|
||||
@Serializable
|
||||
@ -16,8 +15,8 @@ import space.kscience.visionforge.set
|
||||
public class PolyLine(public val points: List<Point3D>) : SolidBase(), Solid {
|
||||
|
||||
//var lineType by string()
|
||||
public var thickness: Number by allProperties(inherit = false).number(1.0,
|
||||
key = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY)
|
||||
public var thickness: Number by numberProperty(name = SolidMaterial.MATERIAL_KEY + THICKNESS_KEY) { 1.0 }
|
||||
|
||||
|
||||
public companion object {
|
||||
public val THICKNESS_KEY: Name = "thickness".asName()
|
||||
|
@ -1,7 +1,7 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.*
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
@ -35,7 +35,7 @@ import kotlin.reflect.KProperty
|
||||
*/
|
||||
public interface Solid : Vision {
|
||||
|
||||
override val descriptor: NodeDescriptor get() = Companion.descriptor
|
||||
override val descriptor: MetaDescriptor get() = Companion.descriptor
|
||||
|
||||
public companion object {
|
||||
// val SELECTED_KEY = "selected".asName()
|
||||
@ -69,39 +69,38 @@ public interface Solid : Vision {
|
||||
public val Y_SCALE_KEY: Name = SCALE_KEY + Y_KEY
|
||||
public val Z_SCALE_KEY: Name = SCALE_KEY + Z_KEY
|
||||
|
||||
public val descriptor: NodeDescriptor by lazy {
|
||||
NodeDescriptor {
|
||||
value(VISIBLE_KEY) {
|
||||
public val descriptor: MetaDescriptor by lazy {
|
||||
MetaDescriptor {
|
||||
value(VISIBLE_KEY, ValueType.BOOLEAN) {
|
||||
inherited = false
|
||||
type(ValueType.BOOLEAN)
|
||||
default(true)
|
||||
}
|
||||
|
||||
//TODO replace by descriptor merge
|
||||
value(Vision.STYLE_KEY) {
|
||||
type(ValueType.STRING)
|
||||
value(Vision.STYLE_KEY, ValueType.STRING) {
|
||||
multiple = true
|
||||
hide()
|
||||
}
|
||||
|
||||
node(POSITION_KEY){
|
||||
node(POSITION_KEY) {
|
||||
hide()
|
||||
}
|
||||
|
||||
node(ROTATION_KEY){
|
||||
node(ROTATION_KEY) {
|
||||
hide()
|
||||
}
|
||||
|
||||
node(SCALE_KEY){
|
||||
node(SCALE_KEY) {
|
||||
hide()
|
||||
}
|
||||
|
||||
value(DETAIL_KEY) {
|
||||
type(ValueType.NUMBER)
|
||||
value(DETAIL_KEY, ValueType.NUMBER) {
|
||||
hide()
|
||||
}
|
||||
|
||||
item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial.descriptor)
|
||||
item(SolidMaterial.MATERIAL_KEY.toString(), SolidMaterial){
|
||||
valueRequirement = ValueRequirement.ABSENT
|
||||
}
|
||||
|
||||
enum(ROTATION_ORDER_KEY, default = RotationOrder.XYZ) {
|
||||
hide()
|
||||
@ -115,7 +114,7 @@ public interface Solid : Vision {
|
||||
* Get the layer number this solid belongs to. Return 0 if layer is not defined.
|
||||
*/
|
||||
public var Solid.layer: Int
|
||||
get() = allProperties().getItem(LAYER_KEY).int ?: 0
|
||||
get() = getProperty(LAYER_KEY, inherit = true).int ?: 0
|
||||
set(value) {
|
||||
setProperty(LAYER_KEY, value)
|
||||
}
|
||||
@ -136,7 +135,7 @@ public enum class RotationOrder {
|
||||
*/
|
||||
public var Solid.rotationOrder: RotationOrder
|
||||
get() = getProperty(Solid.ROTATION_ORDER_KEY).enum<RotationOrder>() ?: RotationOrder.XYZ
|
||||
set(value) = setProperty(Solid.ROTATION_ORDER_KEY, value.name.asValue())
|
||||
set(value) = setPropertyValue(Solid.ROTATION_ORDER_KEY, value.name.asValue())
|
||||
|
||||
|
||||
/**
|
||||
@ -144,7 +143,7 @@ public var Solid.rotationOrder: RotationOrder
|
||||
*/
|
||||
public var Solid.detail: Int?
|
||||
get() = getProperty(DETAIL_KEY, false).int
|
||||
set(value) = setProperty(DETAIL_KEY, value?.asValue())
|
||||
set(value) = setPropertyValue(DETAIL_KEY, value?.asValue())
|
||||
|
||||
/**
|
||||
* If this property is true, the object will be ignored on render.
|
||||
@ -152,7 +151,7 @@ public var Solid.detail: Int?
|
||||
*/
|
||||
public var Vision.ignore: Boolean?
|
||||
get() = getProperty(IGNORE_KEY, false).boolean
|
||||
set(value) = setProperty(IGNORE_KEY, value?.asValue())
|
||||
set(value) = setPropertyValue(IGNORE_KEY, value?.asValue())
|
||||
|
||||
//var VisualObject.selected: Boolean?
|
||||
// get() = getProperty(SELECTED_KEY).boolean
|
||||
@ -182,7 +181,7 @@ internal fun point(name: Name, default: Float): ReadWriteProperty<Solid, Point3D
|
||||
|
||||
override fun setValue(thisRef: Solid, property: KProperty<*>, value: Point3D?) {
|
||||
if (value == null) {
|
||||
thisRef.setProperty(name, null)
|
||||
thisRef.setPropertyNode(name, null)
|
||||
} else {
|
||||
thisRef.setProperty(name + X_KEY, value.x)
|
||||
thisRef.setProperty(name + Y_KEY, value.y)
|
||||
|
@ -2,17 +2,17 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.visionforge.VisionBase
|
||||
import space.kscience.visionforge.VisionChange
|
||||
|
||||
@Serializable
|
||||
@SerialName("solid")
|
||||
public open class SolidBase : VisionBase(), Solid {
|
||||
override val descriptor: NodeDescriptor get() = Solid.descriptor
|
||||
override val descriptor: MetaDescriptor get() = Solid.descriptor
|
||||
|
||||
override fun update(change: VisionChange) {
|
||||
override fun change(change: VisionChange) {
|
||||
updatePosition(change.properties)
|
||||
super.update(change)
|
||||
super.change(change)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.visionforge.*
|
||||
@ -40,7 +40,7 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
|
||||
}
|
||||
|
||||
|
||||
override val descriptor: NodeDescriptor get() = Solid.descriptor
|
||||
override val descriptor: MetaDescriptor get() = Solid.descriptor
|
||||
|
||||
/**
|
||||
* Get a prototype redirecting the request to the parent if prototype is not found.
|
||||
@ -60,9 +60,9 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
|
||||
|
||||
override fun createGroup(): SolidGroup = SolidGroup()
|
||||
|
||||
override fun update(change: VisionChange) {
|
||||
override fun change(change: VisionChange) {
|
||||
updatePosition(change.properties)
|
||||
super.update(change)
|
||||
super.change(change)
|
||||
}
|
||||
|
||||
public companion object {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.attributes
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.value
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.names.plus
|
||||
@ -26,6 +26,8 @@ public class SolidMaterial : Scheme() {
|
||||
*/
|
||||
public val specularColor: ColorAccessor = ColorAccessor(this, SPECULAR_COLOR_KEY)
|
||||
|
||||
public val emissiveColor: ColorAccessor = ColorAccessor(this, "emissiveColor".asName())
|
||||
|
||||
/**
|
||||
* Opacity
|
||||
*/
|
||||
@ -48,43 +50,37 @@ public class SolidMaterial : Scheme() {
|
||||
public val WIREFRAME_KEY: Name = "wireframe".asName()
|
||||
public val MATERIAL_WIREFRAME_KEY: Name = MATERIAL_KEY + WIREFRAME_KEY
|
||||
|
||||
public override val descriptor: NodeDescriptor by lazy {
|
||||
public override val descriptor: MetaDescriptor by lazy {
|
||||
//must be lazy to avoid initialization bug
|
||||
NodeDescriptor {
|
||||
MetaDescriptor {
|
||||
inherited = true
|
||||
usesStyles = true
|
||||
|
||||
value(COLOR_KEY) {
|
||||
value(COLOR_KEY, ValueType.STRING, ValueType.NUMBER) {
|
||||
inherited = true
|
||||
usesStyles = true
|
||||
type(ValueType.STRING, ValueType.NUMBER)
|
||||
widgetType = "color"
|
||||
}
|
||||
|
||||
value(SPECULAR_COLOR_KEY) {
|
||||
value(SPECULAR_COLOR_KEY, ValueType.STRING, ValueType.NUMBER) {
|
||||
inherited = true
|
||||
usesStyles = true
|
||||
type(ValueType.STRING, ValueType.NUMBER)
|
||||
widgetType = "color"
|
||||
hide()
|
||||
}
|
||||
|
||||
value(OPACITY_KEY) {
|
||||
value(OPACITY_KEY, ValueType.NUMBER) {
|
||||
inherited = true
|
||||
usesStyles = true
|
||||
type(ValueType.NUMBER)
|
||||
default(1.0)
|
||||
attributes {
|
||||
this["min"] = 0.0
|
||||
this["max"] = 1.0
|
||||
this["step"] = 0.1
|
||||
}
|
||||
attributes["min"] = 0.0
|
||||
attributes["max"] = 1.0
|
||||
attributes["step"] = 0.1
|
||||
widgetType = "slider"
|
||||
}
|
||||
value(WIREFRAME_KEY) {
|
||||
value(WIREFRAME_KEY, ValueType.BOOLEAN) {
|
||||
inherited = true
|
||||
usesStyles = true
|
||||
type(ValueType.BOOLEAN)
|
||||
default(false)
|
||||
}
|
||||
}
|
||||
@ -94,21 +90,23 @@ public class SolidMaterial : Scheme() {
|
||||
|
||||
public val Solid.color: ColorAccessor
|
||||
get() = ColorAccessor(
|
||||
allProperties(inherit = true),
|
||||
meta(inherit = true),
|
||||
MATERIAL_COLOR_KEY
|
||||
)
|
||||
|
||||
public var Solid.material: SolidMaterial?
|
||||
get() = getProperty(MATERIAL_KEY, inherit = true).node?.let { SolidMaterial.read(it) }
|
||||
set(value) = setProperty(MATERIAL_KEY, value?.rootNode)
|
||||
get() = getProperty(MATERIAL_KEY, inherit = true)?.let { SolidMaterial.read(it) }
|
||||
set(value) = setPropertyNode(MATERIAL_KEY, value?.meta)
|
||||
|
||||
@VisionBuilder
|
||||
public fun Solid.material(builder: SolidMaterial.() -> Unit) {
|
||||
ownProperties.getChild(MATERIAL_KEY).update(SolidMaterial, builder)
|
||||
configure(MATERIAL_KEY){
|
||||
updateWith(SolidMaterial,builder)
|
||||
}
|
||||
}
|
||||
|
||||
public var Solid.opacity: Number?
|
||||
get() = getProperty(MATERIAL_OPACITY_KEY, inherit = true).number
|
||||
set(value) {
|
||||
setProperty(MATERIAL_OPACITY_KEY, value?.asValue())
|
||||
setPropertyValue(MATERIAL_OPACITY_KEY, value?.asValue())
|
||||
}
|
@ -4,11 +4,11 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.meta.MetaItem
|
||||
import space.kscience.dataforge.meta.asMetaItem
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.*
|
||||
import space.kscience.dataforge.values.Value
|
||||
import space.kscience.visionforge.*
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ private fun SolidReference.getRefProperty(
|
||||
inherit: Boolean,
|
||||
includeStyles: Boolean,
|
||||
includeDefaults: Boolean,
|
||||
): MetaItem? = if (!inherit && !includeStyles && !includeDefaults) {
|
||||
): Meta? = if (!inherit && !includeStyles && !includeDefaults) {
|
||||
getOwnProperty(name)
|
||||
} else {
|
||||
buildList {
|
||||
@ -88,9 +88,9 @@ public class SolidReferenceGroup(
|
||||
inherit: Boolean,
|
||||
includeStyles: Boolean,
|
||||
includeDefaults: Boolean,
|
||||
): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults)
|
||||
): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults)
|
||||
|
||||
override val descriptor: NodeDescriptor get() = prototype.descriptor
|
||||
override val descriptor: MetaDescriptor get() = prototype.descriptor
|
||||
|
||||
|
||||
/**
|
||||
@ -118,11 +118,15 @@ public class SolidReferenceGroup(
|
||||
ReferenceChild(owner, refName + key.asName())
|
||||
} ?: emptyMap()
|
||||
|
||||
override fun getOwnProperty(name: Name): MetaItem? =
|
||||
override fun getOwnProperty(name: Name): Meta? =
|
||||
owner.getOwnProperty(childPropertyName(refName, name))
|
||||
|
||||
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
|
||||
owner.setProperty(childPropertyName(refName, name), item, notify)
|
||||
override fun setPropertyNode(name: Name, node: Meta?, notify: Boolean) {
|
||||
owner.setPropertyNode(childPropertyName(refName, name), node, notify)
|
||||
}
|
||||
|
||||
override fun setPropertyValue(name: Name, value: Value?, notify: Boolean) {
|
||||
owner.setPropertyValue(childPropertyName(refName, name), value, notify)
|
||||
}
|
||||
|
||||
override fun getProperty(
|
||||
@ -130,7 +134,7 @@ public class SolidReferenceGroup(
|
||||
inherit: Boolean,
|
||||
includeStyles: Boolean,
|
||||
includeDefaults: Boolean,
|
||||
): MetaItem? = getRefProperty(name, inherit, includeStyles, includeDefaults)
|
||||
): Meta? = getRefProperty(name, inherit, includeStyles, includeDefaults)
|
||||
|
||||
override var parent: VisionGroup?
|
||||
get() {
|
||||
@ -155,13 +159,13 @@ public class SolidReferenceGroup(
|
||||
owner.invalidateProperty(childPropertyName(refName, propertyName))
|
||||
}
|
||||
|
||||
override fun update(change: VisionChange) {
|
||||
override fun change(change: VisionChange) {
|
||||
change.properties?.let {
|
||||
updateProperties(Name.EMPTY, it.asMetaItem())
|
||||
updateProperties(Name.EMPTY, it)
|
||||
}
|
||||
}
|
||||
|
||||
override val descriptor: NodeDescriptor get() = prototype.descriptor
|
||||
override val descriptor: MetaDescriptor get() = prototype.descriptor
|
||||
|
||||
}
|
||||
|
||||
@ -184,7 +188,7 @@ public fun SolidGroup.ref(
|
||||
public fun SolidGroup.ref(
|
||||
name: String,
|
||||
obj: Solid,
|
||||
templateName: Name = name.toName(),
|
||||
templateName: Name = Name.parse(name),
|
||||
): SolidReferenceGroup {
|
||||
val existing = getPrototype(templateName)
|
||||
if (existing == null) {
|
||||
|
@ -95,13 +95,13 @@ public fun MutablePoint3D.normalizeInPlace() {
|
||||
z /= norm
|
||||
}
|
||||
|
||||
internal fun ItemProvider.point3D(default: Float = 0f) = object : Point3D {
|
||||
internal fun MetaProvider.point3D(default: Float = 0f) = object : Point3D {
|
||||
override val x: Float by float(default)
|
||||
override val y: Float by float(default)
|
||||
override val z: Float by float(default)
|
||||
}
|
||||
|
||||
public fun Point3D.toMeta(): MetaBuilder = Meta {
|
||||
public fun Point3D.toMeta(): Meta = Meta {
|
||||
X_KEY put x
|
||||
Y_KEY put y
|
||||
Z_KEY put z
|
||||
@ -115,7 +115,7 @@ internal fun Meta.toVector(default: Float = 0f) = Point3D(
|
||||
)
|
||||
|
||||
internal fun Solid.updatePosition(meta: Meta?) {
|
||||
meta[Solid.POSITION_KEY].node?.toVector()?.let { position = it }
|
||||
meta[Solid.ROTATION_KEY].node?.toVector()?.let { rotation = it }
|
||||
meta[Solid.SCALE_KEY].node?.toVector(1f)?.let { scale = it }
|
||||
meta?.get(Solid.POSITION_KEY)?.toVector()?.let { position = it }
|
||||
meta?.get(Solid.ROTATION_KEY)?.toVector()?.let { rotation = it }
|
||||
meta?.get(Solid.SCALE_KEY)?.toVector(1f)?.let { scale = it }
|
||||
}
|
@ -3,7 +3,7 @@ package space.kscience.visionforge.solid.specifications
|
||||
import space.kscience.dataforge.meta.Scheme
|
||||
import space.kscience.dataforge.meta.SchemeSpec
|
||||
import space.kscience.dataforge.meta.boolean
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.double
|
||||
import space.kscience.visionforge.value
|
||||
|
||||
@ -16,8 +16,8 @@ public class Axes : Scheme() {
|
||||
public const val AXIS_SIZE: Double = 1000.0
|
||||
public const val AXIS_WIDTH: Double = 3.0
|
||||
|
||||
override val descriptor: NodeDescriptor by lazy {
|
||||
NodeDescriptor {
|
||||
override val descriptor: MetaDescriptor by lazy {
|
||||
MetaDescriptor {
|
||||
value(Axes::visible){
|
||||
default(false)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package space.kscience.visionforge.solid.specifications
|
||||
|
||||
import space.kscience.dataforge.meta.Scheme
|
||||
import space.kscience.dataforge.meta.SchemeSpec
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.meta.double
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.visionforge.value
|
||||
@ -27,8 +27,8 @@ public class Camera : Scheme() {
|
||||
public const val FAR_CLIP: Double = 10000.0
|
||||
public const val FIELD_OF_VIEW: Int = 75
|
||||
|
||||
override val descriptor: NodeDescriptor by lazy {
|
||||
NodeDescriptor {
|
||||
override val descriptor: MetaDescriptor by lazy {
|
||||
MetaDescriptor {
|
||||
value(Camera::fov){
|
||||
default(FIELD_OF_VIEW)
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
package space.kscience.visionforge.solid.specifications
|
||||
|
||||
import space.kscience.dataforge.meta.*
|
||||
import space.kscience.dataforge.meta.descriptors.NodeDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.attributes
|
||||
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.visionforge.hide
|
||||
@ -16,30 +15,26 @@ public class Clipping : Scheme() {
|
||||
public var z: Double? by double()
|
||||
|
||||
public companion object : SchemeSpec<Clipping>(::Clipping) {
|
||||
override val descriptor: NodeDescriptor = NodeDescriptor {
|
||||
override val descriptor: MetaDescriptor = MetaDescriptor {
|
||||
value(Clipping::x) {
|
||||
widgetType = "range"
|
||||
attributes {
|
||||
set("min", 0.0)
|
||||
set("max", 1.0)
|
||||
set("step", 0.01)
|
||||
}
|
||||
attributes["min"] = 0.0
|
||||
attributes["max"] = 1.0
|
||||
attributes["step"] = 0.01
|
||||
}
|
||||
value(Clipping::y) {
|
||||
widgetType = "range"
|
||||
attributes {
|
||||
set("min", 0.0)
|
||||
set("max", 1.0)
|
||||
set("step", 0.01)
|
||||
}
|
||||
attributes["min"] = 0.0
|
||||
attributes["max"] = 1.0
|
||||
attributes["step"] = 0.01
|
||||
|
||||
}
|
||||
value(Clipping::z) {
|
||||
widgetType = "range"
|
||||
attributes {
|
||||
set("min", 0.0)
|
||||
set("max", 1.0)
|
||||
set("step", 0.01)
|
||||
}
|
||||
attributes["min"] = 0.0
|
||||
attributes["max"] = 1.0
|
||||
attributes["step"] = 0.01
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,7 +50,7 @@ public class CanvasSize : Scheme() {
|
||||
public var maxHeight: Number by number { maxSize }
|
||||
|
||||
public companion object : SchemeSpec<CanvasSize>(::CanvasSize) {
|
||||
override val descriptor: NodeDescriptor = NodeDescriptor {
|
||||
override val descriptor: MetaDescriptor = MetaDescriptor {
|
||||
value(CanvasSize::minSize)
|
||||
value(CanvasSize::minWith)
|
||||
value(CanvasSize::minHeight)
|
||||
@ -82,8 +77,8 @@ public class Canvas3DOptions : Scheme() {
|
||||
|
||||
|
||||
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) {
|
||||
override val descriptor: NodeDescriptor by lazy {
|
||||
NodeDescriptor {
|
||||
override val descriptor: MetaDescriptor by lazy {
|
||||
MetaDescriptor {
|
||||
scheme(Canvas3DOptions::axes, Axes)
|
||||
scheme(Canvas3DOptions::light, Light)
|
||||
|
||||
@ -104,7 +99,7 @@ public class Canvas3DOptions : Scheme() {
|
||||
multiple = true
|
||||
default(listOf(0))
|
||||
widgetType = "multiSelect"
|
||||
allow(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
||||
allowedValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
||||
}
|
||||
scheme(Canvas3DOptions::clipping, Clipping)
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
package space.kscience.visionforge.solid.transform
|
||||
|
||||
import space.kscience.dataforge.meta.itemSequence
|
||||
import space.kscience.dataforge.meta.update
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.visionforge.MutableVisionGroup
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionGroup
|
||||
import space.kscience.visionforge.meta
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.visionforge.solid.*
|
||||
|
||||
private operator fun Number.plus(other: Number) = toFloat() + other.toFloat()
|
||||
@ -24,10 +21,8 @@ internal fun Vision.updateFrom(other: Vision): Vision {
|
||||
scaleX *= other.scaleX
|
||||
scaleY *= other.scaleY
|
||||
scaleZ *= other.scaleZ
|
||||
other.meta.itemSequence().forEach { (name, item) ->
|
||||
if (getProperty(name) == null) {
|
||||
setProperty(name, item)
|
||||
}
|
||||
configure{
|
||||
update(other.meta())
|
||||
}
|
||||
}
|
||||
return this
|
||||
|
@ -1,9 +1,8 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.meta.MetaItemNode
|
||||
import space.kscience.dataforge.meta.getIndexed
|
||||
import space.kscience.dataforge.meta.node
|
||||
import space.kscience.dataforge.meta.toMetaItem
|
||||
import space.kscience.dataforge.meta.toMeta
|
||||
import space.kscience.dataforge.misc.DFExperimental
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
@ -29,9 +28,9 @@ class ConvexTest {
|
||||
val convex = group.children.values.first() as Convex
|
||||
|
||||
val json = Solids.jsonForSolids.encodeToJsonElement(Convex.serializer(), convex)
|
||||
val meta = json.toMetaItem().node!!
|
||||
val meta = json.toMeta()
|
||||
|
||||
val points = meta.getIndexed("points").values.map { (it as MetaItemNode<*>).node.point3D() }
|
||||
val points = meta.getIndexed("points").values.map { it.point3D() }
|
||||
assertEquals(8, points.count())
|
||||
|
||||
assertEquals(8, convex.points.size)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.meta.descriptors.ValueDescriptor
|
||||
import space.kscience.dataforge.meta.descriptors.get
|
||||
import space.kscience.dataforge.values.ValueType
|
||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||
@ -16,7 +15,7 @@ class DescriptorTest {
|
||||
val axesSize = descriptor["axes.size"]
|
||||
assertNotNull(axesSize)
|
||||
assertTrue {
|
||||
ValueType.NUMBER in (axesSize as ValueDescriptor).type!!
|
||||
ValueType.NUMBER in axesSize.valueTypes!!
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ class PropertyTest {
|
||||
fun testInheritedProperty() {
|
||||
var box: Box? = null
|
||||
val group = SolidGroup().apply {
|
||||
setProperty("test", 22)
|
||||
setPropertyNode("test", 22)
|
||||
group {
|
||||
box = box(100, 100, 100)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.visionforge.MutableVisionGroup
|
||||
import space.kscience.visionforge.get
|
||||
import space.kscience.visionforge.meta
|
||||
@ -14,7 +13,7 @@ import kotlin.test.assertEquals
|
||||
*/
|
||||
fun SolidGroup.refGroup(
|
||||
name: String,
|
||||
templateName: Name = name.toName(),
|
||||
templateName: Name = Name.parse(name),
|
||||
block: MutableVisionGroup.() -> Unit
|
||||
): SolidReferenceGroup {
|
||||
val group = SolidGroup().apply(block)
|
||||
@ -30,10 +29,10 @@ class SerializationTest {
|
||||
x = 100
|
||||
z = -100
|
||||
}
|
||||
val string = Solids.encodeToString(cube)
|
||||
val string = Solids.encodeToString(cube)
|
||||
println(string)
|
||||
val newCube = Solids.decodeFromString(string)
|
||||
assertEquals(cube.meta, newCube.meta)
|
||||
assertEquals(cube.meta(), newCube.meta())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -43,10 +42,10 @@ class SerializationTest {
|
||||
x = 100
|
||||
z = -100
|
||||
}
|
||||
val group = SolidGroup{
|
||||
val group = SolidGroup {
|
||||
ref("cube", cube)
|
||||
refGroup("pg", "pg.content".toName()){
|
||||
sphere(50){
|
||||
refGroup("pg", Name.parse("pg.content")) {
|
||||
sphere(50) {
|
||||
x = -100
|
||||
}
|
||||
}
|
||||
@ -54,7 +53,7 @@ class SerializationTest {
|
||||
val string = Solids.encodeToString(group)
|
||||
println(string)
|
||||
val reconstructed = Solids.decodeFromString(string) as SolidGroup
|
||||
assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta)
|
||||
assertEquals(group["cube"]?.meta(), reconstructed["cube"]?.meta())
|
||||
}
|
||||
|
||||
}
|
@ -2,8 +2,9 @@ package space.kscience.visionforge.solid
|
||||
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.context.fetch
|
||||
import space.kscience.dataforge.meta.MetaItem
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.names.asName
|
||||
import space.kscience.dataforge.values.asValue
|
||||
import space.kscience.visionforge.VisionChange
|
||||
import space.kscience.visionforge.get
|
||||
import kotlin.test.Test
|
||||
@ -24,10 +25,10 @@ class VisionUpdateTest {
|
||||
color(123)
|
||||
box(100,100,100)
|
||||
}
|
||||
propertyChanged("top".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red"))
|
||||
propertyChanged("origin".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red"))
|
||||
propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
|
||||
propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
|
||||
}
|
||||
targetVision.update(dif)
|
||||
targetVision.change(dif)
|
||||
assertTrue { targetVision["top"] is SolidGroup }
|
||||
assertEquals("red", (targetVision["origin"] as Solid).color.string) // Should work
|
||||
assertEquals("#00007b", (targetVision["top"] as Solid).color.string) // new item always takes precedence
|
||||
@ -40,8 +41,8 @@ class VisionUpdateTest {
|
||||
color(123)
|
||||
box(100,100,100)
|
||||
}
|
||||
propertyChanged("top".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red"))
|
||||
propertyChanged("origin".toName(), SolidMaterial.MATERIAL_COLOR_KEY, MetaItem.of("red"))
|
||||
propertyChanged("top".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
|
||||
propertyChanged("origin".asName(), SolidMaterial.MATERIAL_COLOR_KEY, Meta("red".asValue()))
|
||||
}
|
||||
val serialized = visionManager.jsonFormat.encodeToString(VisionChange.serializer(), change)
|
||||
println(serialized)
|
||||
|
@ -90,7 +90,7 @@ public fun Mesh.applyEdges(obj: Solid) {
|
||||
MeshThreeFactory.EDGES_MATERIAL_KEY,
|
||||
inherit = true,
|
||||
includeStyles = true
|
||||
).node,
|
||||
),
|
||||
true
|
||||
)
|
||||
if (edges == null) {
|
||||
|
@ -168,10 +168,10 @@ public class ThreeCanvas(
|
||||
}
|
||||
|
||||
//Clipping planes
|
||||
options.onChange(this@ThreeCanvas) { name, _, _ ->
|
||||
options.meta.onChange(this@ThreeCanvas) { name->
|
||||
if (name.startsWith(Canvas3DOptions::clipping.name.asName())) {
|
||||
val clipping = options.clipping
|
||||
if (!clipping.isEmpty()) {
|
||||
if (!clipping.meta.isEmpty()) {
|
||||
renderer.localClippingEnabled = true
|
||||
boundingBox?.let { boundingBox ->
|
||||
val xClippingPlane = clipping.x?.let {
|
||||
@ -212,9 +212,9 @@ public class ThreeCanvas(
|
||||
private fun Object3D.fullName(): Name {
|
||||
if (root == null) error("Can't resolve element name without the root")
|
||||
return if (parent == root) {
|
||||
name.toName()
|
||||
Name.parse(name)
|
||||
} else {
|
||||
(parent?.fullName() ?: Name.EMPTY) + name.toName()
|
||||
(parent?.fullName() ?: Name.EMPTY) + Name.parse(name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,7 +237,7 @@ public class ThreeCanvas(
|
||||
private fun buildLight(spec: Light?): info.laht.threekt.lights.Light = AmbientLight(0x404040)
|
||||
|
||||
private fun addControls(element: Node, controls: Controls) {
|
||||
when (controls["type"].string) {
|
||||
when (controls.meta["type"].string) {
|
||||
"trackball" -> TrackballControls(camera, element)
|
||||
else -> OrbitControls(camera, element)
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.math.Color
|
||||
import info.laht.threekt.objects.LineSegments
|
||||
import space.kscience.dataforge.meta.node
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
import space.kscience.visionforge.solid.PolyLine
|
||||
import space.kscience.visionforge.solid.color
|
||||
@ -20,7 +19,10 @@ public object ThreeLineFactory : ThreeFactory<PolyLine> {
|
||||
setFromPoints(Array(obj.points.size) { obj.points[it].toVector() })
|
||||
}
|
||||
|
||||
val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true)
|
||||
val material = ThreeMaterials.getLineMaterial(
|
||||
obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY),
|
||||
true
|
||||
)
|
||||
|
||||
material.linewidth = obj.thickness.toDouble()
|
||||
material.color = obj.color.string?.let { Color(it) } ?: DEFAULT_LINE_COLOR
|
||||
|
@ -13,7 +13,7 @@ import space.kscience.dataforge.values.int
|
||||
import space.kscience.dataforge.values.string
|
||||
import space.kscience.visionforge.Colors
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.ownProperties
|
||||
import space.kscience.visionforge.meta
|
||||
import space.kscience.visionforge.solid.SolidMaterial
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ public object ThreeMaterials {
|
||||
private val lineMaterialCache = HashMap<Meta, LineBasicMaterial>()
|
||||
|
||||
private fun buildLineMaterial(meta: Meta): LineBasicMaterial = LineBasicMaterial().apply {
|
||||
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_LINE_COLOR
|
||||
color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_LINE_COLOR
|
||||
opacity = meta[SolidMaterial.OPACITY_KEY].double ?: 1.0
|
||||
transparent = opacity < 1.0
|
||||
linewidth = meta["thickness"].double ?: 1.0
|
||||
@ -59,11 +59,12 @@ public object ThreeMaterials {
|
||||
private val materialCache = HashMap<Meta, Material>()
|
||||
|
||||
internal fun buildMaterial(meta: Meta): Material {
|
||||
val material = SolidMaterial.read(meta)
|
||||
return meta[SolidMaterial.SPECULAR_COLOR_KEY]?.let { specularColor ->
|
||||
MeshPhongMaterial().apply {
|
||||
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR
|
||||
specular = specularColor.getColor()
|
||||
emissive = specular
|
||||
color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
|
||||
specular = specularColor.threeColor()
|
||||
emissive = material.emissiveColor.item?.threeColor() ?: specular
|
||||
reflectivity = 0.5
|
||||
refractionRatio = 1.0
|
||||
shininess = 100.0
|
||||
@ -73,7 +74,7 @@ public object ThreeMaterials {
|
||||
needsUpdate = true
|
||||
}
|
||||
} ?: MeshBasicMaterial().apply {
|
||||
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR
|
||||
color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
|
||||
opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0
|
||||
transparent = opacity < 1.0
|
||||
wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
|
||||
@ -93,22 +94,19 @@ public object ThreeMaterials {
|
||||
/**
|
||||
* Infer color based on meta item
|
||||
*/
|
||||
public fun MetaItem.getColor(): Color {
|
||||
return when (this) {
|
||||
is MetaItemValue -> if (this.value.type == ValueType.NUMBER) {
|
||||
public fun Meta.threeColor(): Color {
|
||||
return value?.let { value ->
|
||||
if (value.type == ValueType.NUMBER) {
|
||||
val int = value.int
|
||||
Color(int)
|
||||
} else {
|
||||
Color(this.value.string)
|
||||
Color(value.string)
|
||||
}
|
||||
is MetaItemNode -> {
|
||||
Color(
|
||||
node[Colors.RED_KEY]?.int ?: 0,
|
||||
node[Colors.GREEN_KEY]?.int ?: 0,
|
||||
node[Colors.BLUE_KEY]?.int ?: 0
|
||||
)
|
||||
}
|
||||
}
|
||||
} ?: Color(
|
||||
this[Colors.RED_KEY]?.int ?: 0,
|
||||
this[Colors.GREEN_KEY]?.int ?: 0,
|
||||
this[Colors.BLUE_KEY]?.int ?: 0
|
||||
)
|
||||
}
|
||||
|
||||
private var Material.cached: Boolean
|
||||
@ -119,7 +117,7 @@ private var Material.cached: Boolean
|
||||
|
||||
public fun Mesh.updateMaterial(vision: Vision) {
|
||||
//val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node
|
||||
val ownMaterialMeta = vision.ownProperties[SolidMaterial.MATERIAL_KEY]
|
||||
val ownMaterialMeta = vision.meta()[SolidMaterial.MATERIAL_KEY]
|
||||
val parentMaterialMeta = vision.parent?.getProperty(
|
||||
SolidMaterial.MATERIAL_KEY,
|
||||
inherit = true,
|
||||
@ -135,7 +133,7 @@ public fun Mesh.updateMaterial(vision: Vision) {
|
||||
inherit = false,
|
||||
includeStyles = true,
|
||||
includeDefaults = false
|
||||
).node?.let {
|
||||
)?.let {
|
||||
ThreeMaterials.cacheMaterial(it)
|
||||
} ?: ThreeMaterials.DEFAULT
|
||||
}
|
||||
@ -143,7 +141,7 @@ public fun Mesh.updateMaterial(vision: Vision) {
|
||||
vision.getProperty(
|
||||
SolidMaterial.MATERIAL_KEY,
|
||||
inherit = true
|
||||
).node?.let {
|
||||
)?.let {
|
||||
ThreeMaterials.buildMaterial(it)
|
||||
} ?: ThreeMaterials.DEFAULT
|
||||
}
|
||||
@ -162,7 +160,7 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
|
||||
inherit = true,
|
||||
includeStyles = true,
|
||||
includeDefaults = false
|
||||
)?.getColor() ?: ThreeMaterials.DEFAULT_COLOR
|
||||
)?.threeColor() ?: ThreeMaterials.DEFAULT_COLOR
|
||||
material.needsUpdate = true
|
||||
}
|
||||
SolidMaterial.MATERIAL_OPACITY_KEY -> {
|
||||
|
@ -143,7 +143,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
||||
element,
|
||||
vision as? Solid ?: error("Solid expected but ${vision::class} found"),
|
||||
).apply {
|
||||
options.update(meta)
|
||||
options.meta.update(meta)
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,8 +195,4 @@ internal fun Object3D.findChild(name: Name): Object3D? {
|
||||
name.length == 1 -> this.children.find { it.name == name.tokens.first().toString() }
|
||||
else -> findChild(name.tokens.first().asName())?.findChild(name.cutFirst())
|
||||
}
|
||||
}
|
||||
|
||||
public fun Context.withThreeJs(): Context = apply {
|
||||
plugins.fetch(ThreePlugin)
|
||||
}
|
@ -3,9 +3,9 @@ package space.kscience.visionforge.solid.three
|
||||
import info.laht.threekt.core.BufferGeometry
|
||||
import info.laht.threekt.core.Object3D
|
||||
import info.laht.threekt.objects.Mesh
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.cutFirst
|
||||
import space.kscience.dataforge.names.firstOrNull
|
||||
import space.kscience.dataforge.names.toName
|
||||
import space.kscience.visionforge.onPropertyChange
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
import space.kscience.visionforge.solid.SolidReferenceGroup
|
||||
@ -49,7 +49,7 @@ public object ThreeReferenceFactory : ThreeFactory<SolidReferenceGroup> {
|
||||
|
||||
obj.onPropertyChange(three.updateScope) { name->
|
||||
if (name.firstOrNull()?.body == REFERENCE_CHILD_PROPERTY_PREFIX) {
|
||||
val childName = name.firstOrNull()?.index?.toName() ?: error("Wrong syntax for reference child property: '$name'")
|
||||
val childName = name.firstOrNull()?.index?.let(Name::parse) ?: error("Wrong syntax for reference child property: '$name'")
|
||||
val propertyName = name.cutFirst()
|
||||
val referenceChild = obj[childName] ?: error("Reference child with name '$childName' not found")
|
||||
val child = object3D.findChild(childName) ?: error("Object child with name '$childName' not found")
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user