|
6 | 6 | LitegraphLinkAdapter
|
7 | 7 | } from '@/renderer/core/canvas/litegraph/litegraphLinkAdapter'
|
8 | 8 | import { getSlotPosition } from '@/renderer/core/canvas/litegraph/slotCalculations'
|
| 9 | +import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations' |
9 | 10 | import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
| 11 | +import { LayoutSource } from '@/renderer/core/layout/types' |
10 | 12 |
|
11 | 13 | import { CanvasPointer } from './CanvasPointer'
|
12 | 14 | import type { ContextMenu } from './ContextMenu'
|
@@ -3431,8 +3433,13 @@ export class LGraphCanvas
|
3431 | 3433 |
|
3432 | 3434 | const deltaX = delta[0] / this.ds.scale
|
3433 | 3435 | const deltaY = delta[1] / this.ds.scale
|
3434 |
| - for (const item of allItems) { |
3435 |
| - item.move(deltaX, deltaY, true) |
| 3436 | + |
| 3437 | + if (LiteGraph.vueNodesMode) { |
| 3438 | + this.moveChildNodesInGroupVueMode(allItems, deltaX, deltaY) |
| 3439 | + } else { |
| 3440 | + for (const item of allItems) { |
| 3441 | + item.move(deltaX, deltaY, true) |
| 3442 | + } |
3436 | 3443 | }
|
3437 | 3444 |
|
3438 | 3445 | this.#dirty()
|
@@ -8452,4 +8459,120 @@ export class LGraphCanvas
|
8452 | 8459 | const setDirty = () => this.setDirty(true, true)
|
8453 | 8460 | this.ds.animateToBounds(bounds, setDirty, options)
|
8454 | 8461 | }
|
| 8462 | + |
| 8463 | + /** |
| 8464 | + * Calculate new position with delta |
| 8465 | + */ |
| 8466 | + private calculateNewPosition( |
| 8467 | + node: LGraphNode, |
| 8468 | + deltaX: number, |
| 8469 | + deltaY: number |
| 8470 | + ): { x: number; y: number } { |
| 8471 | + return { |
| 8472 | + x: node.pos[0] + deltaX, |
| 8473 | + y: node.pos[1] + deltaY |
| 8474 | + } |
| 8475 | + } |
| 8476 | + |
| 8477 | + /** |
| 8478 | + * Apply batched node position updates |
| 8479 | + */ |
| 8480 | + private applyNodePositionUpdates( |
| 8481 | + nodesToMove: Array<{ node: LGraphNode; newPos: { x: number; y: number } }>, |
| 8482 | + mutations: ReturnType<typeof useLayoutMutations> |
| 8483 | + ): void { |
| 8484 | + for (const { node, newPos } of nodesToMove) { |
| 8485 | + // Update LiteGraph position first so next drag uses correct base position |
| 8486 | + node.pos[0] = newPos.x |
| 8487 | + node.pos[1] = newPos.y |
| 8488 | + // Then update layout store which will update Vue nodes |
| 8489 | + mutations.moveNode(node.id, newPos) |
| 8490 | + } |
| 8491 | + } |
| 8492 | + |
| 8493 | + /** |
| 8494 | + * Initialize layout mutations with Canvas source |
| 8495 | + */ |
| 8496 | + private initLayoutMutations(): ReturnType<typeof useLayoutMutations> { |
| 8497 | + const mutations = useLayoutMutations() |
| 8498 | + mutations.setSource(LayoutSource.Canvas) |
| 8499 | + return mutations |
| 8500 | + } |
| 8501 | + |
| 8502 | + /** |
| 8503 | + * Collect all nodes that are children of groups in the selection |
| 8504 | + */ |
| 8505 | + private collectNodesInGroups(items: Set<Positionable>): Set<LGraphNode> { |
| 8506 | + const nodesInGroups = new Set<LGraphNode>() |
| 8507 | + for (const item of items) { |
| 8508 | + if (item instanceof LGraphGroup) { |
| 8509 | + for (const child of item._children) { |
| 8510 | + if (child instanceof LGraphNode) { |
| 8511 | + nodesInGroups.add(child) |
| 8512 | + } |
| 8513 | + } |
| 8514 | + } |
| 8515 | + } |
| 8516 | + return nodesInGroups |
| 8517 | + } |
| 8518 | + |
| 8519 | + /** |
| 8520 | + * Move group children (both nodes and non-nodes) |
| 8521 | + */ |
| 8522 | + private moveGroupChildren( |
| 8523 | + group: LGraphGroup, |
| 8524 | + deltaX: number, |
| 8525 | + deltaY: number, |
| 8526 | + nodesToMove: Array<{ node: LGraphNode; newPos: { x: number; y: number } }> |
| 8527 | + ): void { |
| 8528 | + for (const child of group._children) { |
| 8529 | + if (child instanceof LGraphNode) { |
| 8530 | + const node = child as LGraphNode |
| 8531 | + nodesToMove.push({ |
| 8532 | + node, |
| 8533 | + newPos: this.calculateNewPosition(node, deltaX, deltaY) |
| 8534 | + }) |
| 8535 | + } else { |
| 8536 | + // Non-node children (nested groups, reroutes) |
| 8537 | + child.move(deltaX, deltaY) |
| 8538 | + } |
| 8539 | + } |
| 8540 | + } |
| 8541 | + |
| 8542 | + moveChildNodesInGroupVueMode( |
| 8543 | + allItems: Set<Positionable>, |
| 8544 | + deltaX: number, |
| 8545 | + deltaY: number |
| 8546 | + ) { |
| 8547 | + const mutations = this.initLayoutMutations() |
| 8548 | + const nodesInMovingGroups = this.collectNodesInGroups(allItems) |
| 8549 | + const nodesToMove: Array<{ |
| 8550 | + node: LGraphNode |
| 8551 | + newPos: { x: number; y: number } |
| 8552 | + }> = [] |
| 8553 | + |
| 8554 | + // First, collect all the moves we need to make |
| 8555 | + for (const item of allItems) { |
| 8556 | + const isNode = item instanceof LGraphNode |
| 8557 | + if (isNode) { |
| 8558 | + const node = item as LGraphNode |
| 8559 | + if (nodesInMovingGroups.has(node)) { |
| 8560 | + continue |
| 8561 | + } |
| 8562 | + nodesToMove.push({ |
| 8563 | + node, |
| 8564 | + newPos: this.calculateNewPosition(node, deltaX, deltaY) |
| 8565 | + }) |
| 8566 | + } else if (item instanceof LGraphGroup) { |
| 8567 | + item.move(deltaX, deltaY, true) |
| 8568 | + this.moveGroupChildren(item, deltaX, deltaY, nodesToMove) |
| 8569 | + } else { |
| 8570 | + // Other items (reroutes, etc.) |
| 8571 | + item.move(deltaX, deltaY, true) |
| 8572 | + } |
| 8573 | + } |
| 8574 | + |
| 8575 | + // Now apply all the node moves at once |
| 8576 | + this.applyNodePositionUpdates(nodesToMove, mutations) |
| 8577 | + } |
8455 | 8578 | }
|
0 commit comments