Skip to content

Commit

Permalink
Add copy pasting feature for nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
serivesmejia committed Nov 29, 2024
1 parent dfa3e96 commit 2271614
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import org.lwjgl.glfw.GLFW.*

object GlfwKeys : PlatformKeys {

val isMac = System.getProperty("os.name").contains("Mac")

override val ArrowUp = glfwGetKeyScancode(GLFW_KEY_UP) //111
override val ArrowDown = glfwGetKeyScancode(GLFW_KEY_DOWN)// 116
override val ArrowLeft = glfwGetKeyScancode(GLFW_KEY_LEFT) // 113
Expand All @@ -41,4 +43,11 @@ object GlfwKeys : PlatformKeys {
override val LeftSuper = glfwGetKeyScancode(GLFW_KEY_LEFT_SUPER) //133
override val RightSuper = glfwGetKeyScancode(GLFW_KEY_RIGHT_SUPER) //134

override val NativeLeftSuper by lazy {
if(isMac) glfwGetKeyScancode(GLFW_KEY_LEFT_SUPER) else glfwGetKeyScancode(GLFW_KEY_LEFT_CONTROL)
}
override val NativeRightSuper by lazy {
if(isMac) glfwGetKeyScancode(GLFW_KEY_RIGHT_SUPER) else glfwGetKeyScancode(GLFW_KEY_RIGHT_CONTROL)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -274,16 +274,6 @@ class PaperVision(
IdElementContainerStack.threadStack.push(actions)
IdElementContainerStack.threadStack.push(popups)

if(keyManager.pressing(keyManager.keys.LeftControl)) {
if(ImGui.isKeyPressed(ImGuiKey.Z)) {
undo()
} else if(ImGui.isKeyPressed(ImGuiKey.Y)) {
redo()
} else if(ImGui.isKeyPressed(ImGuiKey.S)) {
logger.info(PaperVisionSerializer.serialize(nodes.inmutable, links.inmutable))
}
}

onUpdate.run()

engineClient.process()
Expand Down Expand Up @@ -318,18 +308,6 @@ class PaperVision(
IdElementContainerStack.threadStack.pop<Popup>()
}

fun undo() {
logger.info("undo | stack; size: ${actions.size}, pointer: ${actions.stackPointer}, peek: ${actions.peek()}")
actions.peekAndPushback()?.undo()
}

fun redo() {
logger.info("redo | stack; size: ${actions.size}, pointer: ${actions.stackPointer}, peek: ${actions.peek()}")

actions.pushforwardIfNonNull()
actions.peek()?.execute()
}

fun destroy() {
nodeEditor.destroy()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class CreateLinkAction(
}

override fun execute() {
link.associatedAction = this
if(link.isEnabled) return

if(link.hasEnabled) {
Expand All @@ -45,8 +46,6 @@ class DeleteLinksAction(
override fun undo() {
links.forEach {
if(it.isEnabled) return


it.enable()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ class EmptyInputAttribute(
abstract class Attribute : DrawableIdElementBase<Attribute>(), DataSerializable<AttributeSerializationData> {

override val idElementContainer get() = IdElementContainerStack.threadStack.peekNonNull<Attribute>()
override val requestedId get() = serializedId

override val requestedId get() = if(forgetSerializedId || (hasParentNode && parentNode.forgetSerializedId))
null
else serializedId

@Transient private var getThisSupplier: (() -> Any)? = null

Expand All @@ -75,6 +78,8 @@ abstract class Attribute : DrawableIdElementBase<Attribute>(), DataSerializable<
lateinit var parentNode: Node<*>
internal set

val hasParentNode get() = ::parentNode.isInitialized

val links = mutableListOf<Link>()
val enabledLinks get() = links.filter { it.isEnabled }

Expand All @@ -88,6 +93,9 @@ abstract class Attribute : DrawableIdElementBase<Attribute>(), DataSerializable<

var showAttributesCircles = true

var forgetSerializedId = false
private set

private var isFirstDraw = true
private var cancelNextDraw = false

Expand Down Expand Up @@ -280,6 +288,10 @@ abstract class Attribute : DrawableIdElementBase<Attribute>(), DataSerializable<
return data
}

fun forgetSerializedId() {
forgetSerializedId = true
}

override fun pollChange() = changeQueue.poll() ?: false

override fun toString() = "Attribute(type=${this::class.java.typeName}, id=$id)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ open class ListAttribute(
it.enabledLinkedAttribute() == linkedAttribute
}
if(alreadyLinkedAttribute == null) {
createElement(linkTo = linkedAttribute)
createElement(linkTo = linkedAttribute, relatedLink = linkedAttribute.links.last())
}

// delete the original link
Expand Down Expand Up @@ -302,7 +302,7 @@ open class ListAttribute(
return list.toTypedArray()
}

private fun createElement(enable: Boolean = true, linkTo: Attribute? = null): TypedAttribute {
private fun createElement(enable: Boolean = true, linkTo: Attribute? = null, relatedLink: Link? = null): TypedAttribute {
val count = listAttributes.size.toString()
val elementName = count + if (count.length == 1) " " else ""

Expand All @@ -316,8 +316,23 @@ open class ListAttribute(
listAttributes.add(element)

if (linkTo != null) {
// if the element is being created because of a link, create the link action
// to link the new element to the linked attribute
parentNode.editor.onDraw.doOnce {
CreateLinkAction(Link(linkTo.id, element.id)).enable()
val action = CreateLinkAction(Link(linkTo.id, element.id))

if(relatedLink != null) {
val associatedAction = relatedLink.associatedAction
if(associatedAction != null) {
// sneakily insert ourselves into the action stack by killing and impersonating the original action
// that created the original link (hopefully he got the chance to say goodbye to his family)
// this avoids glitches when the user tries to undo/redo the creation of this new link
// otherwise, the stack would try to address the original link, which doesn't exist anymore
associatedAction.idElementContainer[associatedAction.id] = action
}
}

action.enable()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@

package io.github.deltacv.papervision.gui

import com.google.gson.JsonElement
import imgui.ImGui
import imgui.ImVec2
import imgui.extension.imnodes.ImNodes
import imgui.extension.imnodes.flag.ImNodesMiniMapLocation
import imgui.extension.texteditor.TextEditorLanguageDefinition
import imgui.flag.ImGuiCol
import imgui.flag.ImGuiKey
import imgui.flag.ImGuiMouseButton
import imgui.flag.ImGuiWindowFlags
import imgui.type.ImInt
Expand All @@ -40,22 +42,23 @@ import io.github.deltacv.papervision.codegen.language.interpreted.CPythonLanguag
import io.github.deltacv.papervision.codegen.language.jvm.JavaLanguage
import io.github.deltacv.papervision.engine.client.message.AskProjectGenClassNameMessage
import io.github.deltacv.papervision.engine.client.response.StringResponse
import io.github.deltacv.papervision.gui.NodeEditor.SourceCodeExportSelectLanguageWindow.Companion.separationMultiplier
import io.github.deltacv.papervision.gui.NodeEditor.SourceCodeExportSelectLanguageWindow.Companion.SEPARATION_MULTIPLIER
import io.github.deltacv.papervision.gui.eocvsim.ImageDisplay
import io.github.deltacv.papervision.gui.eocvsim.ImageDisplayNode
import io.github.deltacv.papervision.gui.eocvsim.ImageDisplayWindow
import io.github.deltacv.papervision.gui.util.FrameWidthWindow
import io.github.deltacv.papervision.gui.util.Popup
import io.github.deltacv.papervision.gui.util.TooltipPopup
import io.github.deltacv.papervision.gui.util.Window
import io.github.deltacv.papervision.id.DrawableIdElement
import io.github.deltacv.papervision.io.KeyManager
import io.github.deltacv.papervision.node.DrawNode
import io.github.deltacv.papervision.node.FlagsNode
import io.github.deltacv.papervision.node.InvisibleNode
import io.github.deltacv.papervision.node.Link
import io.github.deltacv.papervision.node.Node
import io.github.deltacv.papervision.node.vision.InputMatNode
import io.github.deltacv.papervision.node.vision.OutputMatNode
import io.github.deltacv.papervision.serialization.PaperVisionSerializer
import io.github.deltacv.papervision.util.ElapsedTime
import io.github.deltacv.papervision.util.event.PaperVisionEventHandler
import io.github.deltacv.papervision.util.flags
Expand Down Expand Up @@ -123,6 +126,8 @@ class NodeEditor(val paperVision: PaperVision, private val keyManager: KeyManage

private val scrollTimer = ElapsedTime()

var clipboard: String? = null

val onEditorChange = PaperVisionEventHandler("NodeEditor-OnChange")
val onEditorPan = PaperVisionEventHandler("NodeEditor-OnPan")

Expand All @@ -139,7 +144,7 @@ class NodeEditor(val paperVision: PaperVision, private val keyManager: KeyManage

private var currentRightClickMenuPopup: RightClickMenuPopup? = null
val rightClickMenuPopup: RightClickMenuPopup get() {
val popup = RightClickMenuPopup(paperVision.nodeList, paperVision::undo, paperVision::redo, popupSelection)
val popup = RightClickMenuPopup(paperVision.nodeList, ::undo, ::redo, popupSelection)
currentRightClickMenuPopup = popup
return popup
}
Expand Down Expand Up @@ -350,6 +355,123 @@ class NodeEditor(val paperVision: PaperVision, private val keyManager: KeyManage
handleDeleteLink()
handleCreateLink()
handleDeleteSelection()

if (keyManager.pressing(keyManager.keys.NativeLeftSuper) || keyManager.pressing(keyManager.keys.NativeRightSuper)) {
val pressingShift = keyManager.pressing(keyManager.keys.LeftShift)

val pressedZ = ImGui.isKeyPressed(ImGuiKey.Z)
val pressedY = ImGui.isKeyPressed(ImGuiKey.Y)
val pressedS = ImGui.isKeyPressed(ImGuiKey.S)
val pressedC = ImGui.isKeyPressed(ImGuiKey.C)
val pressedV = ImGui.isKeyPressed(ImGuiKey.V)

if (pressingShift && pressedZ) {
redo() // Ctrl + Shift + Z
} else if (pressedZ) {
undo() // Ctrl + Z
} else if (pressedY) {
redo() // Ctrl + Y
} else if (pressedS) {
logger.info(PaperVisionSerializer.serialize(nodes.inmutable, links.inmutable))
} else if(pressedC) {
copy()
} else if(pressedV) {
paste()
}
}
}

fun undo() {
logger.info("undo | stack; size: ${paperVision.actions.size}, pointer: ${paperVision.actions.stackPointer}, peek: ${paperVision.actions.peek()}")
paperVision.actions.peekAndPushback()?.undo()
}

fun redo() {
logger.info("redo | stack; size: ${paperVision.actions.size}, pointer: ${paperVision.actions.stackPointer}, peek: ${paperVision.actions.peek()}")

paperVision.actions.pushforwardIfNonNull()
paperVision.actions.peek()?.execute()
}

fun copy() {
val selectedNodes = IntArray(ImNodes.numSelectedNodes())
ImNodes.getSelectedNodes(selectedNodes)

if(selectedNodes.isEmpty()) return

val selectedNodesList = selectedNodes.map { nodes[it]!! }.filter { it.allowDelete }

clipboard = PaperVisionSerializer.serialize(selectedNodesList, listOf())
logger.info("Clipboard content: $clipboard")
}

fun paste() {
if(clipboard == null) return

val (nodes, links) = PaperVisionSerializer.deserialize(clipboard!!)

var totalX = 0f
var totalY = 0f
var count = 0
for (node in nodes) {
// do not use the original ids of this stuff
node.forgetSerializedId()

// calculate the center point of the nodes
if (node is DrawNode<*>) {
node.nextNodePosition?.let {
totalX += it.x
totalY += it.y
count++
}
}
}

if(nodes.size == 1) {
// if we only have one node, no problemo, just pin it to the mouse
val node = nodes.first()

if(node is DrawNode<*>) {
node.nextNodePosition = ImGui.getMousePos()
}
} else if (count > 0) {
// if we have more than one, we better center them nicely based on the mouse position
// the nodes will maintain their relative positions between each other
val centerX = totalX / count
val centerY = totalY / count

val mousePos = ImGui.getMousePos()

for(node in nodes) {
if(node is DrawNode<*>) {
// create an offset based on the original position of the node
// and the avg center of all the nodes
val offsetX = node.nextNodePosition?.let {
centerX - it.x
} ?: 0f
val offsetY = node.nextNodePosition?.let {
centerY - it.y
} ?: 0f

// translate using the mouse position as an offset
node.nextNodePosition = ImVec2(
offsetX + mousePos.x,
offsetY + mousePos.y
)
}
}
}

// ok, we're done adjusting positions, we'll create the nodes now
for(node in nodes) {
val action = CreateNodeAction(node)

if (node.joinActionStack) {
action.enable()
} else {
action.execute()
}
}
}

fun addNode(nodeClazz: Class<out Node<*>>): Node<*> {
Expand Down Expand Up @@ -467,7 +589,7 @@ class NodeEditor(val paperVision: PaperVision, private val keyManager: KeyManage

if (Node.checkRecursion(inputAttrib.parentNode, outputAttrib.parentNode)) {
TooltipPopup.warning("err_couldntlink_recursion")
// remove the link if a recursion case was detected (e.g both nodes were attached to each other already)
// remove the link if a recursion case was detected (e.g. both nodes were attached to each other already)
link.delete()
} else {
paperVision.onUpdate.doOnce {
Expand Down Expand Up @@ -594,7 +716,7 @@ class NodeEditor(val paperVision: PaperVision, private val keyManager: KeyManage
) : Window() {

companion object {
val separationMultiplier = 1.5f
const val SEPARATION_MULTIPLIER = 1.5f
}

override var title = "$[win_selectlanguage]"
Expand All @@ -618,7 +740,7 @@ class NodeEditor(val paperVision: PaperVision, private val keyManager: KeyManage
}

ImGui.sameLine()
ImGui.indent(ImGui.getItemRectSizeX() * separationMultiplier)
ImGui.indent(ImGui.getItemRectSizeX() * SEPARATION_MULTIPLIER)

if (ImGui.button(FontAwesomeIcons.Brands.Python)) {
openSourceCodeWindow(CPythonLanguage)
Expand Down Expand Up @@ -838,7 +960,7 @@ class NodeEditor(val paperVision: PaperVision, private val keyManager: KeyManage
}

ImGui.sameLine()
ImGui.indent(ImGui.getItemRectSizeX() * separationMultiplier)
ImGui.indent(ImGui.getItemRectSizeX() * SEPARATION_MULTIPLIER)
}

ImGui.popStyleColor()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package io.github.deltacv.papervision.node

import imgui.extension.imnodes.ImNodes
import imgui.extension.imnodes.flag.ImNodesCol
import io.github.deltacv.papervision.action.Action
import io.github.deltacv.papervision.attribute.Attribute
import io.github.deltacv.papervision.attribute.TypedAttribute
import io.github.deltacv.papervision.id.DrawableIdElementBase
Expand All @@ -40,6 +41,8 @@ class Link(
val aAttrib get() = attribIdElementContainer[a]
val bAttrib get() = attribIdElementContainer[b]

var associatedAction: Action? = null

constructor(data: LinkSerializationData) : this(data.from, data.to)

fun getOtherAttribute(me: Attribute) = if(me == aAttrib) bAttrib else aAttrib
Expand Down
Loading

0 comments on commit 2271614

Please sign in to comment.