Add program parameter linking UI
This commit is contained in:
parent
971c41ed2f
commit
233a83f368
|
@ -25,12 +25,12 @@ class ManagerBlockEntity: BlockEntity(ASMR.managerEntityType) {
|
||||||
it.position = Point(0.0, 120.0)
|
it.position = Point(0.0, 120.0)
|
||||||
}
|
}
|
||||||
val divide = program.addBlock(BinaryOperatorBlock(INT, DIVIDE)) {
|
val divide = program.addBlock(BinaryOperatorBlock(INT, DIVIDE)) {
|
||||||
it.left.link(from = left.output)
|
// it.left.link(from = left.output)
|
||||||
it.right.link(from = right.output)
|
// it.right.link(from = right.output)
|
||||||
it.position = Point(120.0, 0.0)
|
it.position = Point(120.0, 0.0)
|
||||||
}
|
}
|
||||||
val print = program.addBlock(PrintBlock(INT)) {
|
val print = program.addBlock(PrintBlock(INT)) {
|
||||||
it.input.link(from = divide.output)
|
// it.input.link(from = divide.output)
|
||||||
it.position = Point(240.0, 0.0)
|
it.position = Point(240.0, 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package net.shadowfacts.asmr.ui
|
||||||
|
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.shadowfacts.asmr.ASMR
|
||||||
|
import net.shadowfacts.asmr.program.ProgramBlockInput
|
||||||
|
import net.shadowfacts.asmr.program.ProgramBlockOutput
|
||||||
|
import net.shadowfacts.cacao.util.texture.Texture
|
||||||
|
import net.shadowfacts.cacao.view.TextureView
|
||||||
|
import net.shadowfacts.cacao.view.View
|
||||||
|
import net.shadowfacts.kiwidsl.dsl
|
||||||
|
import java.lang.RuntimeException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author shadowfacts
|
||||||
|
*/
|
||||||
|
class ProgramBlockParamView(val input: ProgramBlockInput<*>?, val output: ProgramBlockOutput<*>?): View() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val emptyConnection = Texture(Identifier(ASMR.modid, "textures/gui/programmer/program_connections.png"), u = 0, v = 0)
|
||||||
|
val parameterConnection = Texture(Identifier(ASMR.modid, "textures/gui/programmer/program_connections.png"), u = 14, v = 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
lateinit var textureView: TextureView
|
||||||
|
|
||||||
|
constructor(input: ProgramBlockInput<*>): this(input, null)
|
||||||
|
constructor(output: ProgramBlockOutput<*>): this(null, output)
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (input == null && output == null) {
|
||||||
|
throw RuntimeException("One of input or output must be non-null")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun wasAdded() {
|
||||||
|
super.wasAdded()
|
||||||
|
|
||||||
|
textureView = addSubview(TextureView(emptyConnection))
|
||||||
|
updateTexture()
|
||||||
|
|
||||||
|
solver.dsl {
|
||||||
|
textureView.leftAnchor equalTo leftAnchor
|
||||||
|
textureView.rightAnchor equalTo rightAnchor
|
||||||
|
textureView.topAnchor equalTo topAnchor
|
||||||
|
textureView.bottomAnchor equalTo bottomAnchor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateTexture() {
|
||||||
|
val active = if (input != null) input.source != null else output!!.destinations.isNotEmpty()
|
||||||
|
textureView.texture = if (active) parameterConnection else emptyConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -33,8 +33,8 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
|
||||||
var incomingExecution: View? = null
|
var incomingExecution: View? = null
|
||||||
var outgoingExeuction: View? = null
|
var outgoingExeuction: View? = null
|
||||||
|
|
||||||
val inputViews = mutableMapOf<ProgramBlockInput<*>, View>()
|
val inputViews = mutableMapOf<ProgramBlockInput<*>, ProgramBlockParamView>()
|
||||||
val outputViews = mutableMapOf<ProgramBlockOutput<*>, View>()
|
val outputViews = mutableMapOf<ProgramBlockOutput<*>, ProgramBlockParamView>()
|
||||||
|
|
||||||
var xConstraint: Constraint? = null
|
var xConstraint: Constraint? = null
|
||||||
var yConstraint: Constraint? = null
|
var yConstraint: Constraint? = null
|
||||||
|
@ -73,8 +73,9 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
|
||||||
val hStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER))
|
val hStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER))
|
||||||
|
|
||||||
block.inputs.getOrNull(i)?.let { input ->
|
block.inputs.getOrNull(i)?.let { input ->
|
||||||
val inputTexture = if (input.source == null) emptyConnection else parameterConnection
|
// val inputTexture = if (input.source == null) emptyConnection else parameterConnection
|
||||||
val inputView = hStack.addArrangedSubview(TextureView(inputTexture))
|
// val inputView = hStack.addArrangedSubview(TextureView(inputTexture))
|
||||||
|
val inputView = hStack.addArrangedSubview(ProgramBlockParamView(input))
|
||||||
inputViews[input] = inputView
|
inputViews[input] = inputView
|
||||||
val inputLabel = hStack.addArrangedSubview(Label(input.translateName()))
|
val inputLabel = hStack.addArrangedSubview(Label(input.translateName()))
|
||||||
solver.dsl {
|
solver.dsl {
|
||||||
|
@ -92,8 +93,9 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
|
||||||
|
|
||||||
block.outputs.getOrNull(i)?.let { output ->
|
block.outputs.getOrNull(i)?.let { output ->
|
||||||
val outputLabel = hStack.addArrangedSubview(Label(output.translateName(), textAlignment = Label.TextAlignment.RIGHT))
|
val outputLabel = hStack.addArrangedSubview(Label(output.translateName(), textAlignment = Label.TextAlignment.RIGHT))
|
||||||
val outputTexture = if (output.destinations.isEmpty()) emptyConnection else parameterConnection
|
// val outputTexture = if (output.destinations.isEmpty()) emptyConnection else parameterConnection
|
||||||
val outputView = hStack.addArrangedSubview(TextureView(outputTexture))
|
// val outputView = hStack.addArrangedSubview(TextureView(outputTexture))
|
||||||
|
val outputView = hStack.addArrangedSubview(ProgramBlockParamView(output))
|
||||||
outputViews[output] = outputView
|
outputViews[output] = outputView
|
||||||
solver.dsl {
|
solver.dsl {
|
||||||
hStack.heightAnchor equalTo outputLabel.heightAnchor
|
hStack.heightAnchor equalTo outputLabel.heightAnchor
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package net.shadowfacts.asmr.ui
|
package net.shadowfacts.asmr.ui
|
||||||
|
|
||||||
import net.shadowfacts.asmr.program.ExecutableBlock
|
import net.shadowfacts.asmr.program.*
|
||||||
import net.shadowfacts.asmr.program.Program
|
|
||||||
import net.shadowfacts.asmr.program.ProgramBlock
|
|
||||||
import net.shadowfacts.cacao.geometry.Point
|
import net.shadowfacts.cacao.geometry.Point
|
||||||
import net.shadowfacts.cacao.util.Color
|
import net.shadowfacts.cacao.util.Color
|
||||||
|
import net.shadowfacts.cacao.util.MouseButton
|
||||||
import net.shadowfacts.cacao.util.RenderHelper
|
import net.shadowfacts.cacao.util.RenderHelper
|
||||||
import net.shadowfacts.cacao.view.View
|
import net.shadowfacts.cacao.view.View
|
||||||
|
|
||||||
|
@ -19,6 +18,8 @@ class ProgramCanvasView(val program: Program): View() {
|
||||||
private val executionFlowStartLinks = mutableMapOf<ExecutableBlock, Pair<Point, Point>>()
|
private val executionFlowStartLinks = mutableMapOf<ExecutableBlock, Pair<Point, Point>>()
|
||||||
private val parameterLinks = mutableMapOf<ProgramBlock, Pair<Point, Point>>()
|
private val parameterLinks = mutableMapOf<ProgramBlock, Pair<Point, Point>>()
|
||||||
|
|
||||||
|
private var currentPartialLink: PartialLink? = null
|
||||||
|
|
||||||
override fun wasAdded() {
|
override fun wasAdded() {
|
||||||
super.wasAdded()
|
super.wasAdded()
|
||||||
|
|
||||||
|
@ -76,6 +77,91 @@ class ProgramCanvasView(val program: Program): View() {
|
||||||
for ((start, end) in parameterLinks.values) {
|
for ((start, end) in parameterLinks.values) {
|
||||||
RenderHelper.drawLine(start, end, zIndex, 5f, Color.RED)
|
RenderHelper.drawLine(start, end, zIndex, 5f, Color.RED)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val currentPartialLink = currentPartialLink
|
||||||
|
when (currentPartialLink) {
|
||||||
|
is ParamInputPartialLink<*>, is ParamOutputPartialLink<*> -> {
|
||||||
|
RenderHelper.drawLine(currentPartialLink.startPoint, mouse, zIndex, 5f, Color.RED)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
|
||||||
|
for ((block, blockView) in blocks) {
|
||||||
|
if (convert(point, to = blockView) !in blockView.bounds) continue
|
||||||
|
|
||||||
|
for ((input, inputView) in blockView.inputViews) {
|
||||||
|
if (convert(point, to = inputView) in inputView.bounds) {
|
||||||
|
return inputViewClicked(block, input, inputView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ((output, outputView) in blockView.outputViews) {
|
||||||
|
if (convert(point, to = outputView) in outputView.bounds) {
|
||||||
|
return outputViewClicked(block, output, outputView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.mouseClicked(point, mouseButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <Type: Any> inputViewClicked(block: ProgramBlock, input: ProgramBlockInput<Type>, inputView: ProgramBlockParamView): Boolean {
|
||||||
|
val currentPartialLink = currentPartialLink
|
||||||
|
val result = if (currentPartialLink == null) {
|
||||||
|
// no partial link in progress, begin a new one
|
||||||
|
this.currentPartialLink = ParamInputPartialLink(input, inputView, inputView.convert(inputView.bounds.center, to = this))
|
||||||
|
true
|
||||||
|
} else if (currentPartialLink is ParamOutputPartialLink<*> && currentPartialLink.output.type == input.type) {
|
||||||
|
// current link in progress originated with an output param of same type as clicked input, so complete it
|
||||||
|
// we can ignore the unchecked cast because we checked it ourselves above by comparing ProgramTypes, but the compiler doesn't know that
|
||||||
|
@Suppress("NAME_SHADOWING", "UNCHECKED_CAST")
|
||||||
|
val currentPartialLink = currentPartialLink as ParamOutputPartialLink<Type>
|
||||||
|
currentPartialLink.output.link(to = input)
|
||||||
|
currentPartialLink.view.updateTexture()
|
||||||
|
inputView.updateTexture()
|
||||||
|
this.currentPartialLink = null
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// otherwise, click always fails
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
updateBlockLinks()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <Type: Any> outputViewClicked(block: ProgramBlock, output: ProgramBlockOutput<Type>, outputView: ProgramBlockParamView): Boolean {
|
||||||
|
val currentPartialLink = currentPartialLink
|
||||||
|
val result = if (currentPartialLink == null) {
|
||||||
|
this.currentPartialLink = ParamOutputPartialLink(output, outputView, outputView.convert(outputView.bounds.center, to = this))
|
||||||
|
true
|
||||||
|
} else if (currentPartialLink is ParamInputPartialLink<*> && currentPartialLink.input.type == output.type) {
|
||||||
|
// current link in progress originated with an input param of same type as clicked output, so complete it
|
||||||
|
// we can ignore the unchecked cast because we checked it ourselves above by comparing ProgramTypes, but the compiler doesn't know that
|
||||||
|
@Suppress("NAME_SHADOWING", "UNCHECKED_CAST")
|
||||||
|
val currentPartialLink = currentPartialLink as ParamInputPartialLink<Type>
|
||||||
|
currentPartialLink.input.link(from = output)
|
||||||
|
currentPartialLink.view.updateTexture()
|
||||||
|
outputView.updateTexture()
|
||||||
|
this.currentPartialLink = null
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// otherwise, click always fails
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
updateBlockLinks()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
open class PartialLink(val view: ProgramBlockParamView, val startPoint: Point)
|
||||||
|
class ParamInputPartialLink<Type: Any>(val input: ProgramBlockInput<Type>, view: ProgramBlockParamView, startPoint: Point): PartialLink(view, startPoint)
|
||||||
|
class ParamOutputPartialLink<Type: Any>(val output: ProgramBlockOutput<Type>, view: ProgramBlockParamView, startPoint: Point): PartialLink(view, startPoint)
|
||||||
|
// class ExecutionStartPartialLink<Type: Any>(val start: ExecutableBlock): PartialLink
|
||||||
|
// class ExecutionEndPartialLink<Type: Any>(val end: ExecutableBlock): PartialLink
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue