forked from kscience/visionforge
Fix design issues with labels/breadcrumbs
This commit is contained in:
parent
ea73650b50
commit
ac86832616
@ -86,6 +86,10 @@ class ConfigEditorComponent : RComponent<ConfigEditorProps, TreeState>() {
|
||||
}
|
||||
}
|
||||
|
||||
private val removeValue: (Event) -> Unit = {
|
||||
props.root.remove(props.name)
|
||||
}
|
||||
|
||||
//TODO replace by separate components
|
||||
private fun RBuilder.valueChooser(value: Value, descriptor: ValueDescriptor?) {
|
||||
val type = descriptor?.type?.firstOrNull()
|
||||
@ -98,6 +102,15 @@ class ConfigEditorComponent : RComponent<ConfigEditorProps, TreeState>() {
|
||||
}
|
||||
type == ValueType.NUMBER -> input(type = InputType.number, classes = "float-right") {
|
||||
attrs {
|
||||
descriptor.attributes["step"].string?.let {
|
||||
step = it
|
||||
}
|
||||
descriptor.attributes["min"].string?.let {
|
||||
min = it
|
||||
}
|
||||
descriptor.attributes["max"].string?.let {
|
||||
max = it
|
||||
}
|
||||
defaultValue = value.string
|
||||
onChangeFunction = onValueChange
|
||||
}
|
||||
@ -195,9 +208,22 @@ class ConfigEditorComponent : RComponent<ConfigEditorProps, TreeState>() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item != null) {
|
||||
div("col-auto") {
|
||||
button(classes = "btn btn-default") {
|
||||
span("glyphicon glyphicon-remove") {
|
||||
attrs {
|
||||
attributes["aria-hidden"] = "true"
|
||||
}
|
||||
}
|
||||
attrs {
|
||||
onClickFunction = removeValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div("col") {
|
||||
valueChooser(actualItem.value, descriptorItem as? ValueDescriptor)
|
||||
//+actualItem.value.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -220,23 +246,18 @@ fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, def
|
||||
}
|
||||
|
||||
fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) {
|
||||
child(ConfigEditorComponent::class) {
|
||||
attrs {
|
||||
root = config
|
||||
name = Name.EMPTY
|
||||
this.descriptor = descriptor
|
||||
this.default = default
|
||||
div {
|
||||
child(ConfigEditorComponent::class) {
|
||||
attrs {
|
||||
root = config
|
||||
name = Name.EMPTY
|
||||
this.descriptor = descriptor
|
||||
this.default = default
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RBuilder.configEditor(obj: Configurable, descriptor: NodeDescriptor? = obj.descriptor, default: Meta? = null) {
|
||||
child(ConfigEditorComponent::class) {
|
||||
attrs {
|
||||
root = obj.config
|
||||
name = Name.EMPTY
|
||||
this.descriptor = descriptor
|
||||
this.default = default
|
||||
}
|
||||
}
|
||||
configEditor(obj.config, descriptor ?: obj.descriptor, default)
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class ObjectTree : RComponent<ObjectTreeProps, TreeState>() {
|
||||
}
|
||||
|
||||
private fun RBuilder.treeLabel(text: String) {
|
||||
a("#", classes = "tree-label") {
|
||||
button(classes = "btn btn-link tree-label p-0") {
|
||||
+text
|
||||
attrs {
|
||||
if (props.name == props.selected) {
|
||||
|
@ -2,7 +2,6 @@ package hep.dataforge.vis.editor
|
||||
|
||||
import hep.dataforge.js.card
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.isEmpty
|
||||
@ -19,10 +18,9 @@ fun RBuilder.visualPropertyEditor(
|
||||
path: Name,
|
||||
item: VisualObject,
|
||||
descriptor: NodeDescriptor? = item.descriptor,
|
||||
title: String = "Properties",
|
||||
default: MetaBuilder.() -> Unit = {}
|
||||
default: Meta? = null
|
||||
) {
|
||||
card(title) {
|
||||
card("Properties") {
|
||||
if (!path.isEmpty()) {
|
||||
nav {
|
||||
attrs {
|
||||
@ -37,7 +35,7 @@ fun RBuilder.visualPropertyEditor(
|
||||
}
|
||||
}
|
||||
}
|
||||
configEditor(item, descriptor, Meta(default))
|
||||
configEditor(item, descriptor, default)
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,8 +43,7 @@ fun Element.visualPropertyEditor(
|
||||
path: Name,
|
||||
item: VisualObject,
|
||||
descriptor: NodeDescriptor? = item.descriptor,
|
||||
title: String = "Properties",
|
||||
default: MetaBuilder.() -> Unit = {}
|
||||
default: Meta? = null
|
||||
) = render(this) {
|
||||
visualPropertyEditor(path, item, descriptor, title, default)
|
||||
this.visualPropertyEditor(path, item, descriptor, default)
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package hep.dataforge.vis.editor
|
||||
|
||||
//val TextValueChooser = functionalComponent<ConfigEditorProps> {
|
||||
//
|
||||
// input(type = InputType.number, classes = "float-right") {
|
||||
// attrs {
|
||||
// defaultValue = value.string
|
||||
// onChangeFunction = onValueChange
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -42,3 +42,7 @@ ul, .tree {
|
||||
.tree-label-selected{
|
||||
background-color: lightblue;
|
||||
}
|
||||
|
||||
.no-padding{
|
||||
padding: 0;
|
||||
}
|
@ -56,6 +56,13 @@ class Material3D : Scheme() {
|
||||
defineValue(OPACITY_KEY) {
|
||||
type(ValueType.NUMBER)
|
||||
default(1.0)
|
||||
configure {
|
||||
"attributes" to {
|
||||
this["min"] = 0.0
|
||||
this["max"] = 1.0
|
||||
this["step"] = 0.1
|
||||
}
|
||||
}
|
||||
}
|
||||
defineValue(WIREFRAME_KEY) {
|
||||
type(ValueType.BOOLEAN)
|
||||
|
@ -168,11 +168,16 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(obj: VisualObject3D, meta: Meta) {
|
||||
//clear old root
|
||||
fun clear(){
|
||||
scene.children.find { it.name == "@root" }?.let {
|
||||
scene.remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(obj: VisualObject3D, meta: Meta) {
|
||||
//clear old root
|
||||
clear()
|
||||
|
||||
|
||||
val object3D = three.buildObject3D(obj)
|
||||
object3D.name = "@root"
|
||||
|
@ -12,7 +12,6 @@ import react.RProps
|
||||
import react.RState
|
||||
import react.dom.div
|
||||
import react.dom.findDOMNode
|
||||
import kotlin.dom.clear
|
||||
|
||||
interface ThreeCanvasProps : RProps {
|
||||
var context: Context
|
||||
@ -33,18 +32,21 @@ class ThreeCanvasComponent : RComponent<ThreeCanvasProps, ThreeCanvasState>() {
|
||||
var canvas: ThreeCanvas? = null
|
||||
|
||||
override fun componentDidMount() {
|
||||
val element = state.element as? HTMLElement ?: error("Canvas element not found")
|
||||
val three: ThreePlugin = props.context.plugins.load(ThreePlugin)
|
||||
canvas = three.output(element, props.options ?: Canvas.empty())
|
||||
props.canvasCallback?.invoke(canvas)
|
||||
if(canvas == null) {
|
||||
val element = state.element as? HTMLElement ?: error("Canvas element not found")
|
||||
val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin)
|
||||
canvas = three.output(element, props.options ?: Canvas.empty()).apply {
|
||||
onClick = props.clickCallback
|
||||
}
|
||||
props.canvasCallback?.invoke(canvas)
|
||||
}
|
||||
canvas?.render(props.obj)
|
||||
canvas?.onClick = props.clickCallback
|
||||
}
|
||||
|
||||
override fun componentWillUnmount() {
|
||||
state.element?.clear()
|
||||
props.canvasCallback?.invoke(null)
|
||||
}
|
||||
// override fun componentWillUnmount() {
|
||||
// state.element?.clear()
|
||||
// props.canvasCallback?.invoke(null)
|
||||
// }
|
||||
|
||||
override fun componentDidUpdate(prevProps: ThreeCanvasProps, prevState: ThreeCanvasState, snapshot: Any) {
|
||||
if (prevProps.obj != props.obj) {
|
||||
|
@ -159,6 +159,7 @@ private class GDMLDemoApp : Application {
|
||||
}
|
||||
canvas.select(name)
|
||||
editorElement.visualPropertyEditor(name, child)
|
||||
|
||||
}
|
||||
|
||||
// canvas.clickListener = ::selectElement
|
||||
|
@ -20,6 +20,9 @@ kotlin {
|
||||
keep("ktor-ktor-io.\$\$importsForInline\$\$.ktor-ktor-io.io.ktor.utils.io")
|
||||
}
|
||||
}
|
||||
webpackTask {
|
||||
mode = org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode.PRODUCTION
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,11 @@ package ru.mipt.npm.muon.monitor
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.js.card
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.isEmpty
|
||||
import hep.dataforge.vis.VisualObject
|
||||
import hep.dataforge.vis.editor.configEditor
|
||||
import hep.dataforge.vis.editor.objectTree
|
||||
import hep.dataforge.vis.editor.visualPropertyEditor
|
||||
import hep.dataforge.vis.spatial.Visual3D
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
import hep.dataforge.vis.spatial.specifications.Camera
|
||||
import hep.dataforge.vis.spatial.specifications.Canvas
|
||||
@ -14,43 +15,27 @@ import hep.dataforge.vis.spatial.three.ThreeCanvas
|
||||
import hep.dataforge.vis.spatial.three.ThreeCanvasComponent
|
||||
import hep.dataforge.vis.spatial.three.canvasControls
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.features.json.JsonFeature
|
||||
import io.ktor.client.features.json.serializer.KotlinxSerializer
|
||||
import io.ktor.client.request.get
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import kotlinx.serialization.json.Json
|
||||
import react.*
|
||||
import react.dom.button
|
||||
import react.dom.div
|
||||
import react.dom.*
|
||||
import kotlin.math.PI
|
||||
|
||||
interface MMAppProps : RProps {
|
||||
var model: Model
|
||||
var context: Context
|
||||
var connection: HttpClient
|
||||
}
|
||||
|
||||
interface MMAppState : RState {
|
||||
var model: Model
|
||||
var selected: Name?
|
||||
var canvas: ThreeCanvas?
|
||||
}
|
||||
|
||||
class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
|
||||
|
||||
private val model = Model()
|
||||
|
||||
private val connection = HttpClient {
|
||||
install(JsonFeature) {
|
||||
serializer = KotlinxSerializer(Json(context = Visual3D.serialModule))
|
||||
}
|
||||
}
|
||||
|
||||
override fun MMAppState.init(props: MMAppProps) {
|
||||
this.model = props.model
|
||||
}
|
||||
|
||||
private val onSelect: (Name?) -> Unit = {
|
||||
setState {
|
||||
selected = it
|
||||
@ -66,7 +51,7 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
|
||||
}
|
||||
|
||||
override fun RBuilder.render() {
|
||||
val visual = model.root
|
||||
val visual = props.model.root
|
||||
val selected = state.selected
|
||||
|
||||
div("row") {
|
||||
@ -86,9 +71,9 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
|
||||
this.selected = selected
|
||||
this.clickCallback = onSelect
|
||||
this.canvasCallback = {
|
||||
setState{
|
||||
canvas = it
|
||||
}
|
||||
setState {
|
||||
canvas = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,15 +81,19 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
|
||||
div("col-lg-3") {
|
||||
div("row") {
|
||||
//settings
|
||||
state.canvas?.let { canvasControls(it) }
|
||||
state.canvas?.let {
|
||||
card("Canvas configuration") {
|
||||
canvasControls(it)
|
||||
}
|
||||
}
|
||||
card("Events") {
|
||||
button {
|
||||
+"Next"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
GlobalScope.launch {
|
||||
val event = connection.get<Event>("http://localhost:8080/event")
|
||||
model.displayEvent(event)
|
||||
val event = props.connection.get<Event>("http://localhost:8080/event")
|
||||
props.model.displayEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,7 +102,50 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
|
||||
+"Clear"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
model.reset()
|
||||
props.model.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div("row") {
|
||||
div("container-fluid p-0") {
|
||||
nav {
|
||||
attrs {
|
||||
attributes["aria-label"] = "breadcrumb"
|
||||
}
|
||||
ol("breadcrumb") {
|
||||
li("breadcrumb-item") {
|
||||
button(classes = "btn btn-link p-0") {
|
||||
+"World"
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
setState {
|
||||
this.selected = Name.EMPTY
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selected != null) {
|
||||
val tokens = ArrayList<NameToken>(selected.length)
|
||||
selected.tokens.forEach { token ->
|
||||
tokens.add(token)
|
||||
val fullName = Name(tokens.toList())
|
||||
li("breadcrumb-item") {
|
||||
button(classes = "btn btn-link p-0") {
|
||||
+token.toString()
|
||||
attrs {
|
||||
onClickFunction = {
|
||||
setState {
|
||||
console.log("Selected = $fullName")
|
||||
this.selected = fullName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,13 +154,14 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
|
||||
div("row") {
|
||||
//properties
|
||||
if (selected != null) {
|
||||
val selectedObject = when {
|
||||
val selectedObject: VisualObject? = when {
|
||||
selected.isEmpty() -> visual
|
||||
else -> visual[selected]
|
||||
}
|
||||
if (selectedObject != null) {
|
||||
//TODO replace by explicit breadcrumbs with callback
|
||||
visualPropertyEditor(selected, selectedObject, descriptor = VisualObject3D.descriptor)
|
||||
card("Properties") {
|
||||
configEditor(selectedObject, descriptor = VisualObject3D.descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,83 +21,22 @@ private class MMDemoApp : Application {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO introduce react application
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
val context = Global.context("demo") {}
|
||||
// val three = context.plugins.load(ThreePlugin)
|
||||
|
||||
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
|
||||
|
||||
render(element) {
|
||||
child(MMAppComponent::class) {
|
||||
attrs {
|
||||
this.model = model
|
||||
model = this@MMDemoApp.model
|
||||
connection = this@MMDemoApp.connection
|
||||
this.context = context
|
||||
}
|
||||
}
|
||||
}
|
||||
// //val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
|
||||
//
|
||||
// val canvasElement = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page")
|
||||
// val settingsElement = 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()
|
||||
// val visual = model.root
|
||||
//
|
||||
// //output.camera.layers.enable(1)
|
||||
// val canvas = three.output(canvasElement as HTMLElement)
|
||||
//
|
||||
// canvas.camera.layers.set(0)
|
||||
// canvas.camera.position.z = -2000.0
|
||||
// canvas.camera.position.y = 500.0
|
||||
// canvas.camera.lookAt(Vector3(0, 0, 0))
|
||||
//
|
||||
// settingsElement.displayCanvasControls(canvas) {
|
||||
// card("Events") {
|
||||
// button {
|
||||
// +"Next"
|
||||
// onClickFunction = {
|
||||
// GlobalScope.launch {
|
||||
// val event = connection.get<Event>("http://localhost:8080/event")
|
||||
// model.displayEvent(event)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// button {
|
||||
// +"Clear"
|
||||
// onClickFunction = {
|
||||
// model.reset()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var objectTreeContainer: ObjectTreeContainer? = null
|
||||
//
|
||||
// fun selectElement(name: Name?) {
|
||||
// if (name != null) {
|
||||
// canvas.select(name)
|
||||
// val child: VisualObject = when {
|
||||
// name.isEmpty() -> visual
|
||||
// visual is VisualGroup -> visual[name] ?: return
|
||||
// else -> return
|
||||
// }
|
||||
// editorElement.visualPropertyEditor(name, child, descriptor = VisualObject3D.descriptor)
|
||||
// objectTreeContainer?.select(name)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// val selectElementFunction: (Name?) -> Unit = { name ->
|
||||
// selectElement(name?.selectable())
|
||||
// }
|
||||
//
|
||||
// canvas.onClick = selectElementFunction
|
||||
//
|
||||
// objectTreeContainer = treeElement.renderObjectTree(visual, selectElementFunction)
|
||||
// canvas.render(visual)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user