From 4789d86fe8beafdb75a135a27ce21486f17733b1 Mon Sep 17 00:00:00 2001 From: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com> Date: Thu, 18 Sep 2025 19:17:14 +0100 Subject: [PATCH] Line Selection toolbox up with Vue Nodes (#5601) This pull request improves the selection toolbox behavior during node dragging by ensuring that it correctly responds to both LiteGraph and Vue node drag events. The main changes introduce a reactive drag state for Vue nodes in the layout store and update the selection toolbox composable and Vue node component to use this state. **Selection toolbox behavior improvements:** * Added a helper function and separate watchers in `useSelectionToolboxPosition.ts` to hide the selection toolbox when either LiteGraph or Vue nodes are being dragged. This ensures consistent UI feedback regardless of node type. [[1]](diffhunk://#diff-57a51ac5e656e64ae7fd276d71b115058631621755de33b1eb8e8a4731d48713L171-R172) [[2]](diffhunk://#diff-57a51ac5e656e64ae7fd276d71b115058631621755de33b1eb8e8a4731d48713R212-R224) **Vue node drag state management:** * Added a reactive `isDraggingVueNodes` property to the `LayoutStoreImpl` class, along with getter and setter methods to manage Vue node drag state. This allows other components to reactively track when Vue nodes are being dragged. [[1]](diffhunk://#diff-80d32fe0fb72730c16cf7259adef8b20732ff214df240b1d39ae516737beaf3bR133-R135) [[2]](diffhunk://#diff-80d32fe0fb72730c16cf7259adef8b20732ff214df240b1d39ae516737beaf3bR354-R367) * Updated `LGraphNode.vue` to set and clear the Vue node dragging state in the layout store during pointer down and up events, ensuring the selection toolbox is hidden while dragging Vue nodes. [[1]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R357-R360) [[2]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R376-R378) **Dependency updates:** * Imported the `layoutStore` in `LGraphNode.vue` to access the new drag state management methods. * Added missing `ref` import in `layoutStore.ts` to support the new reactive property. https://github.com/user-attachments/assets/d6e9c15e-63b5-4de2-9688-ebbc6a3be545 --------- Co-authored-by: GitHub Action --- .../canvas/useSelectionToolboxPosition.ts | 113 +++++++++++------- src/renderer/core/layout/store/layoutStore.ts | 5 +- .../vueNodes/components/LGraphNode.vue | 8 ++ 3 files changed, 81 insertions(+), 45 deletions(-) diff --git a/src/composables/canvas/useSelectionToolboxPosition.ts b/src/composables/canvas/useSelectionToolboxPosition.ts index 963f9c7d2..e98d4d5ef 100644 --- a/src/composables/canvas/useSelectionToolboxPosition.ts +++ b/src/composables/canvas/useSelectionToolboxPosition.ts @@ -1,4 +1,4 @@ -import { onUnmounted, ref, watch } from 'vue' +import { computed, onUnmounted, ref, watch } from 'vue' import type { Ref } from 'vue' import { useCanvasTransformSync } from '@/composables/canvas/useCanvasTransformSync' @@ -170,50 +170,75 @@ export function useSelectionToolboxPosition( } ) - // Watch for dragging state - watch( - () => canvasStore.canvas?.state?.draggingItems, - (dragging) => { - if (dragging) { - visible.value = false - - if (moreOptionsOpen.value) { - const currentSig = buildSelectionSignature(canvasStore) - if (currentSig !== moreOptionsSelectionSignature) { - moreOptionsSelectionSignature = null - } - moreOptionsWasOpenBeforeDrag = true - moreOptionsOpen.value = false - moreOptionsRestorePending.value = !!moreOptionsSelectionSignature - if (moreOptionsRestorePending.value) { - forceCloseMoreOptionsSignal.value++ - } else { - moreOptionsWasOpenBeforeDrag = false - } - } else { - moreOptionsRestorePending.value = false - moreOptionsWasOpenBeforeDrag = false - } - } else { - requestAnimationFrame(() => { - updateSelectionBounds() - const selectionMatches = currentSelectionMatchesSignature(canvasStore) - const shouldRestore = - moreOptionsWasOpenBeforeDrag && - visible.value && - moreOptionsRestorePending.value && - selectionMatches - - if (shouldRestore) { - restoreMoreOptionsSignal.value++ - } else { - moreOptionsRestorePending.value = false - } - moreOptionsWasOpenBeforeDrag = false - }) - } + const handleDragStateChange = (dragging: boolean) => { + if (dragging) { + handleDragStart() + return } - ) + + handleDragEnd() + } + + const handleDragStart = () => { + visible.value = false + + // Early return if more options wasn't open + if (!moreOptionsOpen.value) { + moreOptionsRestorePending.value = false + moreOptionsWasOpenBeforeDrag = false + return + } + + // Handle more options cleanup + const currentSig = buildSelectionSignature(canvasStore) + const selectionChanged = currentSig !== moreOptionsSelectionSignature + + if (selectionChanged) { + moreOptionsSelectionSignature = null + } + moreOptionsOpen.value = false + moreOptionsWasOpenBeforeDrag = true + moreOptionsRestorePending.value = !!moreOptionsSelectionSignature + + if (moreOptionsRestorePending.value) { + forceCloseMoreOptionsSignal.value++ + return + } + + moreOptionsWasOpenBeforeDrag = false + } + + const handleDragEnd = () => { + requestAnimationFrame(() => { + updateSelectionBounds() + + const selectionMatches = currentSelectionMatchesSignature(canvasStore) + const shouldRestore = + moreOptionsWasOpenBeforeDrag && + visible.value && + moreOptionsRestorePending.value && + selectionMatches + + // Single point of assignment for each ref + moreOptionsRestorePending.value = + shouldRestore && moreOptionsRestorePending.value + moreOptionsWasOpenBeforeDrag = false + + if (shouldRestore) { + restoreMoreOptionsSignal.value++ + } + }) + } + + // Unified dragging state - combines both LiteGraph and Vue node dragging + const isDragging = computed((): boolean => { + const litegraphDragging = canvasStore.canvas?.state?.draggingItems ?? false + const vueNodeDragging = + shouldRenderVueNodes.value && layoutStore.isDraggingVueNodes.value + return litegraphDragging || vueNodeDragging + }) + + watch(isDragging, handleDragStateChange) onUnmounted(() => { resetMoreOptionsState() diff --git a/src/renderer/core/layout/store/layoutStore.ts b/src/renderer/core/layout/store/layoutStore.ts index 7daa4523c..254b27a2c 100644 --- a/src/renderer/core/layout/store/layoutStore.ts +++ b/src/renderer/core/layout/store/layoutStore.ts @@ -5,7 +5,7 @@ * CRDT ensures conflict-free operations for both single and multi-user scenarios. */ import log from 'loglevel' -import { type ComputedRef, type Ref, computed, customRef } from 'vue' +import { type ComputedRef, type Ref, computed, customRef, ref } from 'vue' import * as Y from 'yjs' import { ACTOR_CONFIG } from '@/renderer/core/layout/constants' @@ -134,6 +134,9 @@ class LayoutStoreImpl implements LayoutStore { private slotSpatialIndex: SpatialIndexManager // For slots private rerouteSpatialIndex: SpatialIndexManager // For reroutes + // Vue dragging state for selection toolbox (public ref for direct mutation) + public isDraggingVueNodes = ref(false) + constructor() { // Initialize Yjs data structures this.ynodes = this.ydoc.getMap('nodes') diff --git a/src/renderer/extensions/vueNodes/components/LGraphNode.vue b/src/renderer/extensions/vueNodes/components/LGraphNode.vue index 60e946bf0..808c21ca1 100644 --- a/src/renderer/extensions/vueNodes/components/LGraphNode.vue +++ b/src/renderer/extensions/vueNodes/components/LGraphNode.vue @@ -148,6 +148,7 @@ import { useErrorHandling } from '@/composables/useErrorHandling' import { LiteGraph } from '@/lib/litegraph/src/litegraph' import { SelectedNodeIdsKey } from '@/renderer/core/canvas/injectionKeys' import { TransformStateKey } from '@/renderer/core/layout/injectionKeys' +import { layoutStore } from '@/renderer/core/layout/store/layoutStore' import { useNodeExecutionState } from '@/renderer/extensions/vueNodes/execution/useNodeExecutionState' import { useNodeLayout } from '@/renderer/extensions/vueNodes/layout/useNodeLayout' import { LODLevel, useLOD } from '@/renderer/extensions/vueNodes/lod/useLOD' @@ -363,6 +364,10 @@ const handlePointerDown = (event: PointerEvent) => { // Start drag using layout system isDragging.value = true + + // Set Vue node dragging state for selection toolbox + layoutStore.isDraggingVueNodes.value = true + startDrag(event) lastY.value = event.clientY lastX.value = event.clientX @@ -378,6 +383,9 @@ const handlePointerUp = (event: PointerEvent) => { if (isDragging.value) { isDragging.value = false void endDrag(event) + + // Clear Vue node dragging state for selection toolbox + layoutStore.isDraggingVueNodes.value = false } // Emit node-click for selection handling in GraphCanvas const dx = event.clientX - lastX.value