diff --git a/imnodes.cpp b/imnodes.cpp index 7689cb10..06f0ab4a 100644 --- a/imnodes.cpp +++ b/imnodes.cpp @@ -823,9 +823,9 @@ ImVec2 SnapOriginToGrid(ImVec2 origin) return origin; } -void TranslateSelectedNodes(ImNodesEditorContext& editor) +void TranslateSelectedNodes(ImNodesEditorContext& editor, bool force) { - if (GImNodes->LeftMouseDragging) + if (force || GImNodes->LeftMouseDragging) { // If we have grid snap enabled, don't start moving nodes until we've moved the mouse // slightly @@ -984,11 +984,19 @@ void ClickInteractionUpdate(ImNodesEditorContext& editor) break; case ImNodesClickInteractionType_Node: { - TranslateSelectedNodes(editor); + if (!editor.NodeDraggingEnableStateOnce) + { + IM_ASSERT(GImNodes->HoveredNodeIdx.HasValue()); + editor.NodeDraggingEnableStateOnce = true; + editor.NodeDraggingState = 1; + } + + TranslateSelectedNodes(editor, false); if (GImNodes->LeftMouseReleased) { editor.ClickInteraction.Type = ImNodesClickInteractionType_None; + editor.NodeDraggingEnableStateOnce = false; } } break; @@ -2244,6 +2252,11 @@ void BeginNodeEditor() GImNodes->ActiveAttribute = false; + if (editor.ClickInteraction.Type == ImNodesClickInteractionType_Node && GImNodes->LeftMouseReleased) + { + editor.NodeDraggingState = 2; + } + ImGui::BeginGroup(); { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1.f, 1.f)); @@ -2282,6 +2295,8 @@ void EndNodeEditor() ImNodesEditorContext& editor = EditorContextGet(); + editor.NodeDraggingState = 0; + bool no_grid_content = editor.GridContentBounds.IsInverted(); if (no_grid_content) { @@ -2955,6 +2970,32 @@ bool IsAnyAttributeActive(int* const attribute_id) return true; } +bool IsNodesDragStarted(int *num_selected_nodes) +{ + ImNodesEditorContext &editor = *GImNodes->EditorCtx; + + if (editor.NodeDraggingState == 1) + { + *num_selected_nodes = editor.SelectedNodeIndices.size(); + return true; + } + + return false; +} + +bool IsNodesDragStopped(int *num_selected_nodes) +{ + ImNodesEditorContext &editor = *GImNodes->EditorCtx; + + if (editor.NodeDraggingState == 2) + { + *num_selected_nodes = editor.SelectedNodeIndices.size(); + TranslateSelectedNodes(editor, true); + return true; + } + return false; +} + bool IsLinkStarted(int* const started_at_id) { // Call this function after EndNodeEditor()! diff --git a/imnodes.h b/imnodes.h index 15ad5ed9..85b23850 100644 --- a/imnodes.h +++ b/imnodes.h @@ -394,6 +394,11 @@ bool IsAnyAttributeActive(int* attribute_id = NULL); // Use the following functions to query a change of state for an existing link, or new link. Call // these after EndNodeEditor(). +// Returns true if the user just started dragging a node +bool IsNodesDragStarted(int *num_selected_nodes); +// Returns true if the user just stopped (released) the node after dragging it +bool IsNodesDragStopped(int *num_selected_nodes); + // Did the user start dragging a new link from a pin? bool IsLinkStarted(int* started_at_attribute_id); // Did the user drop the dragged link before attaching it to a pin? diff --git a/imnodes_internal.h b/imnodes_internal.h index 593ab499..80d68ee8 100644 --- a/imnodes_internal.h +++ b/imnodes_internal.h @@ -267,6 +267,8 @@ struct ImNodesEditorContext ImVec2 PrimaryNodeOffset; ImClickInteractionState ClickInteraction; + int NodeDraggingState; // 0 - None, 1 - Just started dragging, 2 - Just stopped dragging + bool NodeDraggingEnableStateOnce; // Used as a flag that ensures state value change for a single frame // Mini-map state set by MiniMap() @@ -284,7 +286,7 @@ struct ImNodesEditorContext ImNodesEditorContext() : Nodes(), Pins(), Links(), Panning(0.f, 0.f), SelectedNodeIndices(), SelectedLinkIndices(), - SelectedNodeOffsets(), PrimaryNodeOffset(0.f, 0.f), ClickInteraction(), + SelectedNodeOffsets(), PrimaryNodeOffset(0.f, 0.f), ClickInteraction(), NodeDraggingState(0), NodeDraggingEnableStateOnce(false), MiniMapEnabled(false), MiniMapSizeFraction(0.0f), MiniMapNodeHoveringCallback(NULL), MiniMapNodeHoveringCallbackUserData(NULL), MiniMapScaling(0.0f)