Optimize UI

This commit is contained in:
Alexander Nozik 2023-12-31 18:09:36 +03:00
parent 7871987df1
commit da0f4c0ff0
14 changed files with 101 additions and 44 deletions

View File

@ -1,27 +1,36 @@
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
alias(spclibs.plugins.compose)
} }
group = "demo" group = "demo"
kscience { kscience {
jvm() // jvm()
js { js {
browser { browser {
binaries.executable() binaries.executable()
commonWebpackConfig{
cssSupport{
enabled = true
}
scssSupport{
enabled = true
}
sourceMaps = true
}
} }
} }
dependencies { dependencies {
implementation(projects.visionforgeSolid) implementation(projects.visionforgeSolid)
implementation(projects.visionforgeGdml) implementation(projects.visionforgeGdml)
} }
jvmMain { // jvmMain {
// implementation(project(":visionforge-fx")) //// implementation(project(":visionforge-fx"))
implementation(spclibs.logback.classic) // implementation(spclibs.logback.classic)
} // }
jsMain { jsMain {
implementation(projects.visionforgeThreejs) implementation(projects.visionforgeThreejs)
implementation(npm("react-file-drop", "3.0.6"))
} }
} }

View File

@ -3,12 +3,12 @@
package space.kscience.visionforge.gdml.demo package space.kscience.visionforge.gdml.demo
import androidx.compose.runtime.* import androidx.compose.runtime.*
import app.softwork.bootstrapcompose.Container
import app.softwork.bootstrapcompose.Icon import app.softwork.bootstrapcompose.Icon
import org.jetbrains.compose.web.ExperimentalComposeWebApi import org.jetbrains.compose.web.ExperimentalComposeWebApi
import org.jetbrains.compose.web.attributes.InputType import org.jetbrains.compose.web.attributes.InputType
import org.jetbrains.compose.web.attributes.name import org.jetbrains.compose.web.attributes.name
import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.Input import org.jetbrains.compose.web.dom.Input
import org.jetbrains.compose.web.dom.Text import org.jetbrains.compose.web.dom.Text
import org.w3c.files.FileList import org.w3c.files.FileList
@ -22,7 +22,7 @@ fun FileDrop(
) { ) {
var dragOver by remember { mutableStateOf(false) } var dragOver by remember { mutableStateOf(false) }
Div({ Container(attrs = {
id("dropzone") id("dropzone")
style { style {
border( border(
@ -69,7 +69,6 @@ fun FileDrop(
} }
} }
}) { }) {
Icon("cloud-upload"){ classes("dropzone-icon") } Icon("cloud-upload"){ classes("dropzone-icon") }
Text(title) Text(title)
Input(type = InputType.File, attrs = { Input(type = InputType.File, attrs = {

View File

@ -4,7 +4,7 @@ import androidx.compose.runtime.*
import kotlinx.browser.window import kotlinx.browser.window
import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Div import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.H2 import org.jetbrains.compose.web.dom.P
import org.jetbrains.compose.web.dom.Text import org.jetbrains.compose.web.dom.Text
import org.w3c.files.File import org.w3c.files.File
import org.w3c.files.FileReader import org.w3c.files.FileReader
@ -66,7 +66,7 @@ fun GDMLApp(solids: Solids, initialVision: Solid?, selected: Name? = null) {
}) { }) {
ThreeView(solids, vision, selected) { ThreeView(solids, vision, selected) {
Tab("Load") { Tab("Load") {
H2 { P {
Text("Drag and drop .gdml or .json VisionForge files here") Text("Drag and drop .gdml or .json VisionForge files here")
} }
FileDrop("(drag file here)") { files -> FileDrop("(drag file here)") { files ->

View File

@ -47,8 +47,7 @@ private class JsPlaygroundApp : Application {
width(100.vw) width(100.vw)
} }
}) { }) {
Tabs { Tabs("gravity") {
active = "gravity"
Tab("gravity") { Tab("gravity") {
GravityDemo(solids, client) GravityDemo(solids, client)
} }

View File

@ -14,10 +14,12 @@ import space.kscience.dataforge.meta.Null
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.html.VisionPage import space.kscience.visionforge.html.VisionPage
import space.kscience.visionforge.html.meta
import space.kscience.visionforge.server.close import space.kscience.visionforge.server.close
import space.kscience.visionforge.server.openInBrowser import space.kscience.visionforge.server.openInBrowser
import space.kscience.visionforge.server.visionPage import space.kscience.visionforge.server.visionPage
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.three.threeJsHeader import space.kscience.visionforge.three.threeJsHeader
import kotlin.random.Random import kotlin.random.Random
@ -47,7 +49,12 @@ fun main() {
) { ) {
div("flex-column") { div("flex-column") {
h1 { +"Satellite detector demo" } h1 { +"Satellite detector demo" }
vision { sat } vision {
meta(Canvas3DOptions {
controls.enabled = false
})
sat
}
} }
} }

View File

@ -46,7 +46,7 @@ internal class VariableBox(val xSize: Number, val ySize: Number) : ThreeJsVision
mesh.scale.z = properties.getValue(VALUE)?.number?.toDouble() ?: 1.0 mesh.scale.z = properties.getValue(VALUE)?.number?.toDouble() ?: 1.0
//add listener to object properties //add listener to object properties
onPropertyChange { name -> onPropertyChange(three.context) { name ->
when { when {
name == VALUE -> { name == VALUE -> {
val value = properties.getValue(VALUE)?.int ?: 0 val value = properties.getValue(VALUE)?.int ?: 0

View File

@ -0,0 +1,28 @@
package space.kscience.visionforge.compose
import androidx.compose.runtime.Composable
import org.jetbrains.compose.web.css.Style
import org.jetbrains.compose.web.dom.DOMScope
import org.jetbrains.compose.web.renderComposable
import org.w3c.dom.Element
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.ElementVisionRenderer
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionClient
/**
* An [ElementVisionRenderer] that could be used directly in Compose-html as well as a stand-alone renderer
*/
public interface ComposeVisionRenderer: ElementVisionRenderer {
@Composable
public fun DOMScope<Element>.render(client: VisionClient, name: Name, vision: Vision, meta: Meta)
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) {
renderComposable(element) {
Style(VisionForgeStyles)
render(client, name, vision, meta)
}
}
}

View File

@ -28,9 +28,9 @@ import space.kscience.visionforge.hidden
* The display state of a property * The display state of a property
*/ */
public sealed class EditorPropertyState { public sealed class EditorPropertyState {
public object Defined : EditorPropertyState() public data object Defined : EditorPropertyState()
public class Default(public val source: String = "unknown") : EditorPropertyState() public data class Default(public val source: String = "unknown") : EditorPropertyState()
public object Undefined : EditorPropertyState() public data object Undefined : EditorPropertyState()
} }

View File

@ -79,8 +79,8 @@ public var Vision.visible: Boolean?
* Subscribe on property updates. The subscription is bound to the given scope and canceled when the scope is canceled * Subscribe on property updates. The subscription is bound to the given scope and canceled when the scope is canceled
*/ */
public fun Vision.onPropertyChange( public fun Vision.onPropertyChange(
scope: CoroutineScope? = manager?.context, scope: CoroutineScope,
callback: suspend (Name) -> Unit, callback: suspend (Name) -> Unit,
): Job = properties.changes.onEach { ): Job = properties.changes.onEach {
callback(it) callback(it)
}.launchIn(scope ?: error("Orphan Vision can't observe properties")) }.launchIn(scope)

View File

@ -4,10 +4,7 @@ import kotlinx.html.*
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.context.ContextAware
import space.kscience.dataforge.context.PluginFactory import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.*
import space.kscience.dataforge.meta.MetaSerializer
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.isEmpty
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
@ -46,9 +43,14 @@ public class VisionOutput @PublishedApi internal constructor(override val contex
newContext.visionManager newContext.visionManager
} }
public inline fun meta(block: MutableMeta.() -> Unit) { }
public inline fun VisionOutput.meta(block: MutableMeta.() -> Unit) {
this.meta = Meta(block) this.meta = Meta(block)
} }
public fun VisionOutput.meta(metaRepr: MetaRepr) {
this.meta = metaRepr.toMeta()
} }
/** /**

View File

@ -19,7 +19,7 @@ public fun Vision.useProperty(
propertyName: Name, propertyName: Name,
inherit: Boolean? = null, inherit: Boolean? = null,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
scope: CoroutineScope? = manager?.context, scope: CoroutineScope = manager?.context ?: error("Orphan Vision can't observe properties"),
callback: (Meta) -> Unit, callback: (Meta) -> Unit,
): Job { ): Job {
//Pass initial value. //Pass initial value.
@ -28,14 +28,14 @@ public fun Vision.useProperty(
if (name.startsWith(propertyName)) { if (name.startsWith(propertyName)) {
callback(properties.get(propertyName, inherit, includeStyles)) callback(properties.get(propertyName, inherit, includeStyles))
} }
}.launchIn(scope ?: error("Orphan Vision can't observe properties")) }.launchIn(scope)
} }
public fun Vision.useProperty( public fun Vision.useProperty(
propertyName: String, propertyName: String,
inherit: Boolean? = null, inherit: Boolean? = null,
includeStyles: Boolean? = null, includeStyles: Boolean? = null,
scope: CoroutineScope? = manager?.context, scope: CoroutineScope = manager?.context ?: error("Orphan Vision can't observe properties"),
callback: (Meta) -> Unit, callback: (Meta) -> Unit,
): Job = useProperty(propertyName.parseAsName(), inherit, includeStyles, scope, callback) ): Job = useProperty(propertyName.parseAsName(), inherit, includeStyles, scope, callback)

View File

@ -52,7 +52,7 @@ public class ThreeCompositeFactory(public val three: ThreePlugin) : ThreeFactory
applyProperties(vision) applyProperties(vision)
if (observe) { if (observe) {
vision.onPropertyChange { name -> vision.onPropertyChange(three.context) { name ->
when { when {
//name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj) //name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj)
name.startsWith(EDGES_KEY) -> applyEdges(vision) name.startsWith(EDGES_KEY) -> applyEdges(vision)

View File

@ -1,14 +1,16 @@
package space.kscience.visionforge.solid.three package space.kscience.visionforge.solid.three
import androidx.compose.runtime.Composable
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import org.jetbrains.compose.web.renderComposable import org.jetbrains.compose.web.dom.DOMScope
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import space.kscience.dataforge.context.* import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.compose.ComposeVisionRenderer
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.solid.three.compose.ThreeView import space.kscience.visionforge.solid.three.compose.ThreeView
@ -21,7 +23,7 @@ import three.objects.Group as ThreeGroup
/** /**
* A plugin that handles Three Object3D representation of Visions. * A plugin that handles Three Object3D representation of Visions.
*/ */
public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer { public class ThreePlugin : AbstractPlugin(), ComposeVisionRenderer {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
public val solids: Solids by require(Solids) public val solids: Solids by require(Solids)
@ -75,7 +77,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
// disable tracking changes for statics // disable tracking changes for statics
group[token] = object3D group[token] = object3D
} catch (ex: Throwable) { } catch (ex: Throwable) {
logger.error(ex) { "Failed to render $child" } logger.error(ex) { "Failed to render vision with token $token and type ${child::class}" }
} }
} }
} }
@ -115,7 +117,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
val object3D = buildObject3D(child) val object3D = buildObject3D(child)
set(childName, object3D) set(childName, object3D)
} catch (ex: Throwable) { } catch (ex: Throwable) {
logger.error(ex) { "Failed to render $child" } logger.error(ex) { "Failed to render vision with name $childName" }
} }
} }
}.launchIn(context) }.launchIn(context)
@ -185,12 +187,11 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
render(vision) render(vision)
} }
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) { @Composable
override fun DOMScope<Element>.render(client: VisionClient, name: Name, vision: Vision, meta: Meta) {
require(vision is Solid) { "Expected Solid but found ${vision::class}" } require(vision is Solid) { "Expected Solid but found ${vision::class}" }
renderComposable(element) {
ThreeView(solids, vision, null, Canvas3DOptions.read(meta)) ThreeView(solids, vision, null, Canvas3DOptions.read(meta))
} }
}
public companion object : PluginFactory<ThreePlugin> { public companion object : PluginFactory<ThreePlugin> {
override val tag: PluginTag = PluginTag("vision.threejs", PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag("vision.threejs", PluginTag.DATAFORGE_GROUP)

View File

@ -6,21 +6,33 @@ plugins {
val ktorVersion: String by rootProject.extra val ktorVersion: String by rootProject.extra
kscience { kscience {
fullStack("js/visionforge-three.js") fullStack(
bundleName = "js/visionforge-three.js",
browserConfig = {
webpackTask {
cssSupport {
enabled = true
}
scssSupport {
enabled = true
}
}
}
)
commonMain { commonMain {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)
api(projects.visionforgeComposeHtml) api(projects.visionforgeComposeHtml)
} }
jvmMain{ jvmMain {
api(projects.visionforgeServer) api(projects.visionforgeServer)
} }
jsMain{ jsMain {
api(projects.visionforgeThreejs) api(projects.visionforgeThreejs)
implementation(npm("file-saver","2.0.5")) implementation(npm("file-saver", "2.0.5"))
implementation(npm("@types/file-saver", "2.0.7")) implementation(npm("@types/file-saver", "2.0.7"))
compileOnly(npm("webpack-bundle-analyzer","4.5.0")) compileOnly(npm("webpack-bundle-analyzer", "4.5.0"))
} }
} }