Name breadcrumbs in property editor for #10

This commit is contained in:
Alexander Nozik 2020-01-02 11:23:09 +03:00
parent d36542ac5b
commit 9e43d2c572
7 changed files with 73 additions and 55 deletions

View File

@ -61,6 +61,7 @@ data class StyleRef(val group: VisualGroup, val styleName: Name)
val VisualGroup.isEmpty: Boolean get() = this.children.isEmpty()
interface MutableVisualGroup : VisualGroup {
/**

View File

@ -1,6 +1,8 @@
package hep.dataforge.vis.js.editor
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.plus
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.isEmpty
@ -15,58 +17,62 @@ import kotlin.dom.clear
fun Element.objectTree(
token: NameToken,
obj: VisualObject,
clickCallback: (VisualObject) -> Unit = {}
clickCallback: (Name, VisualObject) -> Unit = {_,_->}
) {
clear()
append {
card("Object tree") {
subTree(token, obj, clickCallback)
subTree(Name.EMPTY, token, obj, clickCallback)
}
}
}
private fun TagConsumer<HTMLElement>.subTree(
parentName: Name,
token: NameToken,
obj: VisualObject,
clickCallback: (VisualObject) -> Unit
clickCallback: (Name, VisualObject) -> Unit
) {
val fullName = parentName + token
if (obj is VisualGroup && !obj.isEmpty) {
//display as node if any child is visible
if (obj is VisualGroup && obj.children.keys.any { !it.body.startsWith("@") }) {
lateinit var toggle: HTMLSpanElement
div("d-inline-block text-truncate") {
toggle = span("objTree-caret")
label("objTree-label") {
+token.toString()
onClickFunction = { clickCallback(obj) }
onClickFunction = { clickCallback(fullName, obj) }
}
}
val subtree = ul("objTree-subtree")
toggle.onclick = {
toggle.classList.toggle("objTree-caret-down")
subtree.apply {
//If expanded, add children dynamically
if (toggle.classList.contains("objTree-caret-down")) {
obj.children.entries
.filter { !it.key.toString().startsWith("@") }
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
.sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true }
.forEach { (token, child) ->
.forEach { (childToken, child) ->
append {
li().apply {
subTree(token, child, clickCallback)
subTree(fullName, childToken, child, clickCallback)
}
}
}
} else {
// if not, clear them to conserve memory on very long lists
this.clear()
}
}
//jQuery(subtree).asDynamic().collapse("toggle")
}
} else {
div("d-inline-block text-truncate") {
val div = div("d-inline-block text-truncate") {
span("objTree-leaf")
label("objTree-label") {
+token.toString()
onClickFunction = { clickCallback(obj) }
onClickFunction = { clickCallback(fullName, obj) }
}
}
}

View File

@ -5,11 +5,12 @@ import hep.dataforge.js.jsObject
import hep.dataforge.meta.DynamicMeta
import hep.dataforge.meta.Meta
import hep.dataforge.meta.update
import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.findStyle
import kotlinx.html.dom.append
import kotlinx.html.js.div
import kotlinx.html.js.h4
import kotlinx.html.js.*
import org.w3c.dom.Element
import kotlin.dom.clear
@ -17,40 +18,51 @@ import kotlin.dom.clear
fun Meta.toDynamic() = JSON.parse<dynamic>(toJson().toString())
//TODO add node descriptor instead of configuring property selector
fun Element.propertyEditor(item: VisualObject?, propertySelector: (VisualObject) -> Meta = { it.config }) {
fun Element.propertyEditor(name: Name, item: VisualObject, propertySelector: (VisualObject) -> Meta = { it.config }) {
clear()
if (item != null) {
append {
card("Properties") {
val dMeta: dynamic = propertySelector(item).toDynamic()
val options: JSONEditorOptions = jsObject {
mode = "form"
onChangeJSON = { item.config.update(DynamicMeta(it.asDynamic())) }
}
JSONEditor(div(), options, dMeta)
}
val styles = item.styles
if (styles.isNotEmpty()) {
card("Styles") {
item.styles.forEach { style ->
val styleMeta = item.findStyle(style)
h4("container") { +style }
if (styleMeta != null) {
div("container").apply {
val options: JSONEditorOptions = jsObject {
mode = "view"
}
JSONEditor(
this,
options,
styleMeta.toDynamic()
)
append {
card("Properties") {
if(!name.isEmpty()) {
nav {
attributes["aria-label"] = "breadcrumb"
ol("breadcrumb") {
name.tokens.forEach {token->
li("breadcrumb-item") {
+token.toString()
}
}
}
}
}
val dMeta: dynamic = propertySelector(item).toDynamic()
val options: JSONEditorOptions = jsObject {
mode = "form"
onChangeJSON = { item.config.update(DynamicMeta(it.asDynamic())) }
}
JSONEditor(div(), options, dMeta)
}
val styles = item.styles
if (styles.isNotEmpty()) {
card("Styles") {
item.styles.forEach { style ->
val styleMeta = item.findStyle(style)
h4("container") { +style }
if (styleMeta != null) {
div("container").apply {
val options: JSONEditorOptions = jsObject {
mode = "view"
}
JSONEditor(
this,
options,
styleMeta.toDynamic()
)
}
}
}
}
}
}
}

View File

@ -49,6 +49,7 @@ class Visual3DPlugin(meta: Meta) : AbstractPlugin(meta) {
Box::class with Box.serializer()
Convex::class with Convex.serializer()
Extruded::class with Extruded.serializer()
addSubclass(PolyLine.serializer())
addSubclass(Label3D.serializer())
}
}

View File

@ -2,11 +2,11 @@ package hep.dataforge.vis.spatial.gdml.demo
import hep.dataforge.context.Global
import hep.dataforge.js.Application
import hep.dataforge.vis.js.editor.objectTree
import hep.dataforge.js.startApplication
import hep.dataforge.meta.buildMeta
import hep.dataforge.meta.withBottom
import hep.dataforge.names.NameToken
import hep.dataforge.vis.js.editor.objectTree
import hep.dataforge.vis.js.editor.propertyEditor
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
@ -159,8 +159,8 @@ private class GDMLDemoApp : Application {
output.camera.layers.set(0)
configElement.threeSettings(output)
//tree.visualObjectTree(visual, editor::propertyEditor)
treeElement.objectTree(NameToken("World"), visual) {
editorElement.propertyEditor(it) { item ->
treeElement.objectTree(NameToken("World"), visual) { objName, obj ->
editorElement.propertyEditor(objName, obj) { item ->
//val descriptorMeta = Material3D.descriptor
val properties = item.allProperties()

View File

@ -35,7 +35,7 @@ private class GDMLDemoApp : Application {
private val model = Model()
private val connection = HttpClient{
private val connection = HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer(Visual3DPlugin.json)
}
@ -52,7 +52,6 @@ private class GDMLDemoApp : Application {
document.getElementById("settings") ?: error("Element with id 'settings' not found on page")
val treeElement = document.getElementById("tree") ?: error("Element with id 'tree' not found on page")
val editorElement = document.getElementById("editor") ?: error("Element with id 'editor' not found on page")
canvasElement.clear()
canvasElement.clear()
val visual: VisualObject3D = model.root
@ -63,7 +62,7 @@ private class GDMLDemoApp : Application {
output.camera.layers.set(0)
output.camera.position.z = -2000.0
output.camera.position.y = 500.0
settingsElement.threeSettings(output){
settingsElement.threeSettings(output) {
card("Events") {
button {
+"Next"
@ -83,8 +82,8 @@ private class GDMLDemoApp : Application {
}
}
//tree.visualObjectTree(visual, editor::propertyEditor)
treeElement.objectTree(NameToken("World"), visual) {
editorElement.propertyEditor(it) { item ->
treeElement.objectTree(NameToken("World"), visual) { name, obj ->
editorElement.propertyEditor(name, obj) { item ->
//val descriptorMeta = Material3D.descriptor
val properties = item.allProperties()
@ -99,10 +98,7 @@ private class GDMLDemoApp : Application {
properties.withBottom(bottom)
}
}
output.render(visual)
}
}

View File

@ -19,13 +19,15 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-3">
<div class="row" id ="settings"></div>
<div class="row" id ="tree"></div>
<div class="row" id="tree"></div>
</div>
<div class="col-lg-6">
<div class="container" id = "canvas"></div>
<div id="canvas"></div>
</div>
<div class="col-lg-3">
<div class="row" id="settings"></div>
<div class="row" id="editor"></div>
</div>
<div class="col-lg-3" id="editor"></div>
</div>
</div>
</body>