|
|
|
@ -2,6 +2,10 @@ package net.shadowfacts.asmr.ui
|
|
|
|
|
|
|
|
|
|
import net.shadowfacts.asmr.program.*
|
|
|
|
|
import net.shadowfacts.asmr.program.execution.ExecutableBlock
|
|
|
|
|
import net.shadowfacts.asmr.program.execution.IncomingExecutionFlow
|
|
|
|
|
import net.shadowfacts.asmr.program.execution.OutgoingExecutionFlow
|
|
|
|
|
import net.shadowfacts.asmr.ui.block.ProgramBlockConnectionIndicatorView
|
|
|
|
|
import net.shadowfacts.asmr.ui.block.ProgramBlockExecutionView
|
|
|
|
|
import net.shadowfacts.asmr.ui.block.ProgramBlockParamView
|
|
|
|
|
import net.shadowfacts.asmr.ui.block.ProgramBlockView
|
|
|
|
|
import net.shadowfacts.cacao.geometry.Point
|
|
|
|
@ -40,12 +44,16 @@ class ProgramCanvasView(val program: Program): View() {
|
|
|
|
|
for (outgoing in startBlock.outgoing) {
|
|
|
|
|
val outgoingView = startView.outgoingViews[outgoing] ?: continue
|
|
|
|
|
val start = outgoingView.convert(outgoingView.bounds.center, to = this)
|
|
|
|
|
val incoming = outgoing.destination ?: continue
|
|
|
|
|
val incoming = outgoing.destination
|
|
|
|
|
if (incoming != null) {
|
|
|
|
|
val incomingBlockView = blocks[incoming.block] ?: continue
|
|
|
|
|
val incomingView = incomingBlockView.incomingView!!
|
|
|
|
|
val end = incomingView.convert(incomingView.bounds.center, to = this)
|
|
|
|
|
|
|
|
|
|
executionFlowStartLinks[startBlock] = start to end
|
|
|
|
|
} else {
|
|
|
|
|
executionFlowStartLinks.remove(startBlock)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -83,25 +91,40 @@ class ProgramCanvasView(val program: Program): View() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val currentPartialLink = currentPartialLink
|
|
|
|
|
when (currentPartialLink) {
|
|
|
|
|
is ParamInputPartialLink<*>, is ParamOutputPartialLink<*> -> {
|
|
|
|
|
RenderHelper.drawLine(currentPartialLink.startPoint, mouse, zIndex, 5f, Color.RED)
|
|
|
|
|
}
|
|
|
|
|
if (currentPartialLink != null) {
|
|
|
|
|
val color = if (currentPartialLink is ParamInputPartialLink<*> || currentPartialLink is ParamOutputPartialLink<*>) Color.RED else Color.GREEN
|
|
|
|
|
RenderHelper.drawLine(currentPartialLink.startPoint, mouse, zIndex, 5f, color)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
|
|
|
|
|
if (mouseButton == MouseButton.RIGHT && currentPartialLink != null) {
|
|
|
|
|
this.currentPartialLink = null
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
return inputViewClicked(block, input, inputView, mouseButton)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for ((output, outputView) in blockView.outputViews) {
|
|
|
|
|
if (convert(point, to = outputView) in outputView.bounds) {
|
|
|
|
|
return outputViewClicked(block, output, outputView)
|
|
|
|
|
return outputViewClicked(block, output, outputView, mouseButton)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (block is ExecutableBlock) {
|
|
|
|
|
if (convert(point, to = blockView.incomingView!!) in blockView.incomingView!!.bounds) {
|
|
|
|
|
return incomingExecutionViewClicked(block, block.incoming, blockView.incomingView!!, mouseButton)
|
|
|
|
|
}
|
|
|
|
|
for ((outgoing, outgoingView) in blockView.outgoingViews) {
|
|
|
|
|
if (convert(point, to = outgoingView) in outgoingView.bounds) {
|
|
|
|
|
return outgoingExecutionViewClicked(block, outgoing, outgoingView, mouseButton)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -109,12 +132,28 @@ class ProgramCanvasView(val program: Program): View() {
|
|
|
|
|
return super.mouseClicked(point, mouseButton)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun <Type: Any> inputViewClicked(block: ProgramBlock, input: ProgramBlockInput<Type>, inputView: ProgramBlockParamView): Boolean {
|
|
|
|
|
private fun <Type: Any> inputViewClicked(block: ProgramBlock, input: ProgramBlockInput<Type>, inputView: ProgramBlockParamView, mouseButton: MouseButton): Boolean {
|
|
|
|
|
val currentPartialLink = currentPartialLink
|
|
|
|
|
val result = if (currentPartialLink == null) {
|
|
|
|
|
// no partial link in progress, begin a new one
|
|
|
|
|
return if (currentPartialLink == null) {
|
|
|
|
|
// no partial link in progress
|
|
|
|
|
when (mouseButton) {
|
|
|
|
|
MouseButton.LEFT -> {
|
|
|
|
|
// begin a new one
|
|
|
|
|
this.currentPartialLink = ParamInputPartialLink(input, inputView, inputView.convert(inputView.bounds.center, to = this))
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
MouseButton.RIGHT -> {
|
|
|
|
|
// clear clicked connection
|
|
|
|
|
val source = input.source ?: return false
|
|
|
|
|
val outputView = blocks[source.block]?.outputViews?.get(source) ?: return false
|
|
|
|
|
input.unlink()
|
|
|
|
|
inputView.updateTexture()
|
|
|
|
|
outputView.updateTexture()
|
|
|
|
|
updateBlockLinks()
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
} 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
|
|
|
|
@ -123,24 +162,36 @@ class ProgramCanvasView(val program: Program): View() {
|
|
|
|
|
currentPartialLink.output.link(to = input)
|
|
|
|
|
currentPartialLink.view.updateTexture()
|
|
|
|
|
inputView.updateTexture()
|
|
|
|
|
updateBlockLinks()
|
|
|
|
|
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 {
|
|
|
|
|
private fun <Type: Any> outputViewClicked(block: ProgramBlock, output: ProgramBlockOutput<Type>, outputView: ProgramBlockParamView, mouseButton: MouseButton): Boolean {
|
|
|
|
|
val currentPartialLink = currentPartialLink
|
|
|
|
|
val result = if (currentPartialLink == null) {
|
|
|
|
|
return if (currentPartialLink == null) {
|
|
|
|
|
// no partial link in progress
|
|
|
|
|
when (mouseButton) {
|
|
|
|
|
MouseButton.LEFT -> {
|
|
|
|
|
// begin a new one
|
|
|
|
|
this.currentPartialLink = ParamOutputPartialLink(output, outputView, outputView.convert(outputView.bounds.center, to = this))
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
MouseButton.RIGHT -> {
|
|
|
|
|
// clear clicked connection
|
|
|
|
|
val inputViews = output.destinations.mapNotNull { blocks[it.block]?.inputViews?.get(it) }
|
|
|
|
|
output.unlinkAll()
|
|
|
|
|
outputView.updateTexture()
|
|
|
|
|
inputViews.forEach(ProgramBlockParamView::updateTexture)
|
|
|
|
|
updateBlockLinks()
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
} 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
|
|
|
|
@ -149,23 +200,91 @@ class ProgramCanvasView(val program: Program): View() {
|
|
|
|
|
currentPartialLink.input.link(from = output)
|
|
|
|
|
currentPartialLink.view.updateTexture()
|
|
|
|
|
outputView.updateTexture()
|
|
|
|
|
updateBlockLinks()
|
|
|
|
|
this.currentPartialLink = null
|
|
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
// otherwise, click always fails
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
|
private fun incomingExecutionViewClicked(block: ProgramBlock, incoming: IncomingExecutionFlow, incomingView: ProgramBlockExecutionView, mouseButton: MouseButton): Boolean {
|
|
|
|
|
val currentPartialLink = currentPartialLink
|
|
|
|
|
return if (currentPartialLink == null) {
|
|
|
|
|
// no partial link in progress
|
|
|
|
|
when (mouseButton) {
|
|
|
|
|
MouseButton.LEFT -> {
|
|
|
|
|
// being a new one
|
|
|
|
|
this.currentPartialLink = IncomingExecutionPartialLink(incoming, incomingView, incomingView.convert(incomingView.bounds.center, to = this))
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
MouseButton.RIGHT -> {
|
|
|
|
|
// clear clicked connection
|
|
|
|
|
val outgoing = incoming.source ?: return false
|
|
|
|
|
val outgoingView = blocks[outgoing.block]?.outgoingViews?.get(outgoing) ?: return false
|
|
|
|
|
incoming.unlink()
|
|
|
|
|
incomingView.updateTexture()
|
|
|
|
|
outgoingView.updateTexture()
|
|
|
|
|
updateBlockLinks()
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
} else if (currentPartialLink is OutgoingExecutionPartialLink) {
|
|
|
|
|
// current link in progress originated with an outgoing execution connection, so complete it
|
|
|
|
|
currentPartialLink.outgoing.link(incoming)
|
|
|
|
|
currentPartialLink.view.updateTexture()
|
|
|
|
|
incomingView.updateTexture()
|
|
|
|
|
updateBlockLinks()
|
|
|
|
|
this.currentPartialLink = null
|
|
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
// otherwise, click always fails
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
open class PartialLink(val view: ProgramBlockParamView, val startPoint: Point)
|
|
|
|
|
private fun outgoingExecutionViewClicked(block: ProgramBlock, outgoing: OutgoingExecutionFlow, outgoingView: ProgramBlockExecutionView, mouseButton: MouseButton): Boolean {
|
|
|
|
|
val currentPartialLink = currentPartialLink
|
|
|
|
|
return if (currentPartialLink == null) {
|
|
|
|
|
// no partial link in progress
|
|
|
|
|
when (mouseButton) {
|
|
|
|
|
MouseButton.LEFT -> {
|
|
|
|
|
// begin a new one
|
|
|
|
|
this.currentPartialLink = OutgoingExecutionPartialLink(outgoing, outgoingView, outgoingView.convert(outgoingView.bounds.center, to = this))
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
MouseButton.RIGHT -> {
|
|
|
|
|
// clear clicked connection
|
|
|
|
|
val incoming = outgoing.destination ?: return false
|
|
|
|
|
val incomingView = blocks[incoming.block]?.incomingView ?: return false
|
|
|
|
|
outgoing.unlink()
|
|
|
|
|
outgoingView.updateTexture()
|
|
|
|
|
incomingView.updateTexture()
|
|
|
|
|
updateBlockLinks()
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
} else if (currentPartialLink is IncomingExecutionPartialLink) {
|
|
|
|
|
// current link in progress originated with an incoming execution connection, so complete it
|
|
|
|
|
currentPartialLink.incoming.link(outgoing)
|
|
|
|
|
currentPartialLink.view.updateTexture()
|
|
|
|
|
outgoingView.updateTexture()
|
|
|
|
|
updateBlockLinks()
|
|
|
|
|
this.currentPartialLink = null
|
|
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
// otherwise, click always fails
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
open class PartialLink(val view: ProgramBlockConnectionIndicatorView, 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
|
|
|
|
|
class IncomingExecutionPartialLink(val incoming: IncomingExecutionFlow, view: ProgramBlockExecutionView, startPoint: Point): PartialLink(view, startPoint)
|
|
|
|
|
class OutgoingExecutionPartialLink(val outgoing: OutgoingExecutionFlow, view: ProgramBlockExecutionView, startPoint: Point): PartialLink(view, startPoint)
|
|
|
|
|
|
|
|
|
|
}
|