diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index f42f5296..7593ac20 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -271,9 +271,22 @@ if (e.key.toLowerCase() === "escape") { nodeListLocation = undefined; document.removeEventListener("keydown", keydown); + linkInProgressFromConnector = undefined; } } + function loadNodeList(e: PointerEvent, graphBounds: DOMRect) { + nodeListLocation = { + x: Math.round(((e.clientX - graphBounds.x) / transform.scale - transform.x) / GRID_SIZE), + y: Math.round(((e.clientY - graphBounds.y) / transform.scale - transform.y) / GRID_SIZE), + }; + + // Find actual relevant child and focus it (setTimeout is required to actually focus the input element) + setTimeout(() => nodeSearchInput?.focus(), 0); + + document.addEventListener("keydown", keydown); + } + // TODO: Move the event listener from the graph to the window so dragging outside the graph area (or even the whole browser window) works function pointerDown(e: PointerEvent) { const [lmb, rmb] = [e.button === 0, e.button === 2]; @@ -287,15 +300,7 @@ if (rmb) { const graphBounds = graph?.getBoundingClientRect(); if (!graphBounds) return; - nodeListLocation = { - x: Math.round(((e.clientX - graphBounds.x) / transform.scale - transform.x) / GRID_SIZE), - y: Math.round(((e.clientY - graphBounds.y) / transform.scale - transform.y) / GRID_SIZE), - }; - - // Find actual relevant child and focus it (setTimeout is required to actually focus the input element) - setTimeout(() => nodeSearchInput?.focus(), 0); - - document.addEventListener("keydown", keydown); + loadNodeList(e, graphBounds); return; } @@ -303,7 +308,10 @@ if (lmb && nodeList) return; // Since the user is clicking elsewhere in the graph, ensure the add nodes list is closed - if (lmb) nodeListLocation = undefined; + if (lmb) { + nodeListLocation = undefined; + linkInProgressFromConnector = undefined; + } // Alt-click sets the clicked node as previewed if (lmb && e.altKey && nodeId) { @@ -389,7 +397,7 @@ if (panning) { transform.x += e.movementX / transform.scale; transform.y += e.movementY / transform.scale; - } else if (linkInProgressFromConnector) { + } else if (linkInProgressFromConnector && !nodeListLocation) { const target = e.target as Element | undefined; const dot = (target?.closest(`[data-port="input"]`) || undefined) as SVGSVGElement | undefined; if (dot) { @@ -436,9 +444,54 @@ else return undefined; } + // Check if this node should be inserted between two other nodes + function checkInsertBetween() { + if (selected.length !== 1) return; + const selectedNodeId = selected[0]; + const selectedNode = nodesContainer?.querySelector(`[data-node="${String(selectedNodeId)}"]`) || undefined; + + // Check that neither the input or output of the selected node are already connected. + const notConnected = $nodeGraph.links.findIndex((link) => link.linkStart === selectedNodeId || (link.linkEnd === selectedNodeId && link.linkEndInputIndex === BigInt(0))) === -1; + const input = selectedNode?.querySelector(`[data-port="input"]`) || undefined; + const output = selectedNode?.querySelector(`[data-port="output"]`) || undefined; + + // TODO: Make sure inputs are correctly typed + if (!selectedNode || !notConnected || !input || !output || !nodesContainer) return; + + // Fixes typing for some reason? + const theNodesContainer = nodesContainer; + + // Find the link that the node has been dragged on top of + const link = $nodeGraph.links.find((link): boolean => { + const { nodeInput, nodeOutput } = resolveLink(link, theNodesContainer); + if (!nodeInput || !nodeOutput) return false; + + const wireCurveLocations = buildWirePathLocations(nodeOutput.getBoundingClientRect(), nodeInput.getBoundingClientRect(), false, false); + + const selectedNodeBounds = selectedNode.getBoundingClientRect(); + const containerBoundsBounds = theNodesContainer.getBoundingClientRect(); + + return editor.instance.rectangleIntersects( + new Float64Array(wireCurveLocations.map((loc) => loc.x)), + new Float64Array(wireCurveLocations.map((loc) => loc.y)), + selectedNodeBounds.top - containerBoundsBounds.y, + selectedNodeBounds.left - containerBoundsBounds.x, + selectedNodeBounds.bottom - containerBoundsBounds.y, + selectedNodeBounds.right - containerBoundsBounds.x + ); + }); + + // If the node has been dragged on top of the link then connect it into the middle. + if (link) { + editor.instance.connectNodesByLink(link.linkStart, 0, selectedNodeId, 0); + editor.instance.connectNodesByLink(selectedNodeId, 0, link.linkEnd, Number(link.linkEndInputIndex)); + editor.instance.shiftNode(selectedNodeId); + } + } function pointerUp(e: PointerEvent) { panning = false; + const initialDisconnecting = disconnecting; if (disconnecting) { editor.instance.disconnectNodes(BigInt(disconnecting.nodeId), disconnecting.inputIndex); } @@ -453,6 +506,24 @@ const { nodeId: inputConnectedNodeID, index: inputNodeConnectionIndex } = to; editor.instance.connectNodesByLink(outputConnectedNodeID, outputNodeConnectionIndex, inputConnectedNodeID, inputNodeConnectionIndex); } + } else if (linkInProgressFromConnector && !initialDisconnecting) { + // If the add node menu is already open, we don't want to open it again + if (nodeListLocation) return; + + const graphBounds = graph?.getBoundingClientRect(); + if (!graphBounds) return; + + // Create the node list, which should set nodeListLocation to a valid value + loadNodeList(e, graphBounds); + if (!nodeListLocation) return; + let nodeListLocation2: { x: number; y: number } = nodeListLocation; + + linkInProgressToConnector = new DOMRect( + (nodeListLocation2.x * GRID_SIZE + transform.x) * transform.scale + graphBounds.x, + (nodeListLocation2.y * GRID_SIZE + transform.y) * transform.scale + graphBounds.y + ); + + return; } else if (draggingNodes) { if (draggingNodes.startX === e.x || draggingNodes.startY === e.y) { if (selectIfNotDragged !== undefined && (selected.length !== 1 || selected[0] !== selectIfNotDragged)) { @@ -463,48 +534,7 @@ if (selected.length > 0 && (draggingNodes.roundX !== 0 || draggingNodes.roundY !== 0)) editor.instance.moveSelectedNodes(draggingNodes.roundX, draggingNodes.roundY); - // Check if this node should be inserted between two other nodes - if (selected.length === 1) { - const selectedNodeId = selected[0]; - const selectedNode = nodesContainer?.querySelector(`[data-node="${String(selectedNodeId)}"]`) || undefined; - - // Check that neither the input or output of the selected node are already connected. - const notConnected = $nodeGraph.links.findIndex((link) => link.linkStart === selectedNodeId || (link.linkEnd === selectedNodeId && link.linkEndInputIndex === BigInt(0))) === -1; - const input = selectedNode?.querySelector(`[data-port="input"]`) || undefined; - const output = selectedNode?.querySelector(`[data-port="output"]`) || undefined; - - // TODO: Make sure inputs are correctly typed - if (selectedNode && notConnected && input && output && nodesContainer) { - const theNodesContainer = nodesContainer; - - // Find the link that the node has been dragged on top of - const link = $nodeGraph.links.find((link): boolean => { - const { nodeInput, nodeOutput } = resolveLink(link, theNodesContainer); - if (!nodeInput || !nodeOutput) return false; - - const wireCurveLocations = buildWirePathLocations(nodeOutput.getBoundingClientRect(), nodeInput.getBoundingClientRect(), false, false); - - const selectedNodeBounds = selectedNode.getBoundingClientRect(); - const containerBoundsBounds = theNodesContainer.getBoundingClientRect(); - - return editor.instance.rectangleIntersects( - new Float64Array(wireCurveLocations.map((loc) => loc.x)), - new Float64Array(wireCurveLocations.map((loc) => loc.y)), - selectedNodeBounds.top - containerBoundsBounds.y, - selectedNodeBounds.left - containerBoundsBounds.x, - selectedNodeBounds.bottom - containerBoundsBounds.y, - selectedNodeBounds.right - containerBoundsBounds.x - ); - }); - - // If the node has been dragged on top of the link then connect it into the middle. - if (link) { - editor.instance.connectNodesByLink(link.linkStart, 0, selectedNodeId, 0); - editor.instance.connectNodesByLink(selectedNodeId, 0, link.linkEnd, Number(link.linkEndInputIndex)); - editor.instance.shiftNode(selectedNodeId); - } - } - } + checkInsertBetween(); draggingNodes = undefined; selectIfNotDragged = undefined; @@ -517,8 +547,19 @@ function createNode(nodeType: string): void { if (!nodeListLocation) return; - editor.instance.createNode(nodeType, nodeListLocation.x, nodeListLocation.y); + const inputNodeConnectionIndex = 0; + const inputConnectedNodeID = editor.instance.createNode(nodeType, nodeListLocation.x, nodeListLocation.y - 1); nodeListLocation = undefined; + + if (!linkInProgressFromConnector) return; + const from = connectorToNodeIndex(linkInProgressFromConnector); + + if (from !== undefined) { + const { nodeId: outputConnectedNodeID, index: outputNodeConnectionIndex } = from; + editor.instance.connectNodesByLink(outputConnectedNodeID, outputNodeConnectionIndex, inputConnectedNodeID, inputNodeConnectionIndex); + } + + linkInProgressFromConnector = undefined; } function nodeBorderMask(nodeWidth: number, primaryInputExists: boolean, parameters: number, primaryOutputExists: boolean, exposedOutputs: number): string { @@ -654,7 +695,7 @@