Skip to content
This repository has been archived by the owner on Oct 31, 2023. It is now read-only.

Making children of node drag along with parent #200

Closed
TimOgden opened this issue Nov 2, 2021 · 5 comments
Closed

Making children of node drag along with parent #200

TimOgden opened this issue Nov 2, 2021 · 5 comments

Comments

@TimOgden
Copy link

TimOgden commented Nov 2, 2021

Hey, I'm trying to modify the code so that when I move one node, any children of that node are dragged by the same amount.

I created an extension class to NodeEditorCallbackReceiver and overrided the OnMoveNode(Node node) method. However, my code isn't working, and the Debug print statement I put in that function never does anything, so I know the method is not being called. I also noticed that the NodeEditorCallbacks class has a static Action<Node> for each of the node callbacks. I tried setting this value in OnEnable() in my class, but still, no effect.

What am I missing here?

using System;
using UnityEngine;
using NodeEditorFramework;
using NodeEditorFramework.Standard;

namespace NodeEditorFramework
{
    public class DragChildrenCallbackReceiver : NodeEditorCallbackReceiver
    {

        void OnEnable()
        {
            NodeEditorCallbacks.OnMoveNode = DragNode;
        }

        public override void OnMoveNode(Node node)
        {
            DragNode(node);
        }

        public void DragNode(Node node)
        {
            Debug.Log("Running OnMoveNode callback");
            var n = node as BaseBTNode;
            if (n != null)
            {
                for (int i = 0; i < n.children.Length; i++)
                {
                    n.children[i].position = n.position + n.children[i].offset;
                }
                n.parent.offset = n.parent.position - n.position;
            }
        }
    }
}
@Seneral
Copy link
Owner

Seneral commented Nov 2, 2021

Its called here:
https://github.com/Seneral/Node_Editor_Framework/blob/develop/Node_Editor_Framework/Runtime/Framework/Interface/NodeEditorInputControls.cs#L173
so it should call it once after the drag ended. It does not currently call it every frame, nor does it call it when the keyboard is used (that is a bug).
Note that the Receiver is only working as an instantiated MonoBehaviour due to implementation, I assume you don't have ExecuteInEditMode or are in play mode so the code will not execute. You will have to get a static script to work in edit mode and use the Actions, not sure how the details were for that in Unity.
Alternatively, you can take a look at what the built-in NodeGroup does. It implements custom controls that override the default drag in certain scenarios and also moves multiple nodes, that might be something to refer to. The static functions tagged with the proper attributes will be called automatically if you set the priorities right, but you might have to copy and modify the existing drag function which is more complex

@TimOgden
Copy link
Author

TimOgden commented Nov 3, 2021

@Seneral Thanks so much for such a quick and informative reply. I realized early on that I didn't have much of a use for the NodeGroups, so I didn't do much research into them, and turns out the NodeGroup class was a perfect example to show me what to do. I spent today writing my own methods, structured similar to those in NodeGroup and it's working perfectly now.

dragNodes

I used recursion since I don't just want a node's children to be dragged along, I want the whole subtree. Here's the code I wrote inside of my BaseBTNode class:

public void SetNodeOffset(Node node)
{
	this.offset = this.position - node.position;
	for (int i = 0; i < children.Length; i++)
	{
		children[i].SetNodeOffset(node);
	}
}

public void UpdatePosition(Node node)
{
	//Debug.Log(offset);
	this.position = node.position + offset;
	for (int i = 0; i < children.Length; i++)
	{
		if (children[i] != null)
			children[i].UpdatePosition(node);
	}
}

[EventHandlerAttribute(EventType.MouseDown, 109)]
private static void HandleNodeStartDrag(NodeEditorInputInfo inputInfo)
{
	//Debug.Log("Running OnMoveNode callback");
	if (GUIUtility.hotControl > 0)
		return; // GUI has control

	NodeEditorState state = inputInfo.editorState;
	if(inputInfo.inputEvent.button == 0 && !state.dragNode)
    {
		var n = NodeEditor.NodeAtPosition(NodeEditor.ScreenToCanvasSpace(inputInfo.inputPos)) as BaseBTNode;
		if(n!=null)
        {
			for(int i = 0; i<n.children.Length; i++)
            {
				n.children[i].SetNodeOffset(n);
            }
			state.StartDrag("node", inputInfo.inputPos, n.position);
			state.dragNode = true;
        }
    }
}

[EventHandlerAttribute(EventType.MouseDrag)]
private static void HandleNodeDragging(NodeEditorInputInfo inputInfo)
{
	NodeEditorState state = inputInfo.editorState;
	var n = state.selectedNode as BaseBTNode;
			
	if(n!=null)
    {
		if(state.dragUserID != "node")
        {
			state.selectedNode = null;
			state.dragNode = false;
			return;
        }
		//Update drag operation
		Vector2 dragChange = state.UpdateDrag("node", inputInfo.inputPos);
		Vector2 newPos = state.dragObjectPos;

		// Update positions
		n.position = newPos;
		for(int i = 0; i<n.children.Length; i++)
        {
			n.children[i].UpdatePosition(n);
        }

		inputInfo.inputEvent.Use();
		NodeEditor.RepaintClients();
    }
}

[EventHandlerAttribute(EventType.MouseUp)]
private static void HandleDraggingEnd(NodeEditorInputInfo inputInfo)
{
	if(inputInfo.editorState.dragUserID == "node")
    {
		inputInfo.editorState.EndDrag("node");
		NodeEditor.RepaintClients();
    }

	inputInfo.editorState.selectedNode = null;
	inputInfo.editorState.dragNode = false;
}

@TimOgden TimOgden closed this as completed Nov 3, 2021
@Seneral
Copy link
Owner

Seneral commented Nov 3, 2021

Looking good, just one thing, I'd expect ypu need to change at least the drag id from "node" to something custom so the default mouseDrag events won't interfere. Right now it seems it will come down to chance which one will get called first, at least from a quick glance. Having a different drag id means the builtin function will not consider it as a drag it needs to handle.

Also make sure all your relevant canvas types are disallowing loops (via property) if you are relying on recursion like this, especially if you intend to have other users use the system.

@TimOgden
Copy link
Author

TimOgden commented Nov 3, 2021

Yeah, good point, I was wondering if that would cause an issue. In testing so far, it never seemed like the default mouseDrag events ever activated, I think perhaps because I set the priority of the EventHandlerAttribute to a lesser value, but it definitely would be good to change it to something unique.

And yeah, I never disallowed recursive loops, I just knew I wasn't going to be using them. How should I do that? Maybe in ValidateSelf() in my custom Canvas class, if I detect any recursive loops I could delete one of the nodes?

@TimOgden
Copy link
Author

TimOgden commented Nov 3, 2021

Nevermind, Canvas has an allowRecursion property, that's what you meant, haha. Thanks again

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants