Dialogue Node dragging - when out of screen

Hi, I’ve got this behavior when I leave Dialogue Editor window with Dialoge Node dragging → Mouse Up → go to editor window → Mouse Down → it snaps last dialoge node to mouse position.

behav

I’ve tried to utilize MouseLeaveWindow event, but with no luck.
Any ideas how to fix it?

Unfortunately, there’s not a great solution for this. From the description of the OnMouseLeaveWindow documentation, the event is only called when the mouse leaves the window “without any buttons held down”.

Actually, I just tried to recreate this on my end and am NOT getting this. When the mouse leaves the window, the dialogue stops and is only re-aquired if I hold on to the mouse the whole time before re-entering the window.

Paste in your DialogueEditor.cs and we’ll take a look.

I checked out the “Dragging Nodes” commit and I’ve got same issue:
behav
I’m using 2020.1.17f1 version of Unity.

Code for DialogueEditor

using System;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

namespace RPG.Dialogue.Editor
{
    public class DialogueEditor : EditorWindow
    {
        Dialogue selectedDialogue = null;
        GUIStyle nodeStyle = null;
        DialogueNode draggingNode = null;
        Vector2 dragginOffset;

        [MenuItem("Window/Dialogue Editor")]
        public static void ShowEditorWindow()
        {
            GetWindow(typeof(DialogueEditor), false, "Dialogue  Editor");
        }

        [OnOpenAsset(1)]
        public static bool OnOpenAsset(int instanceID, int line)
        {
            Dialogue dialogue = EditorUtility.InstanceIDToObject(instanceID) as Dialogue;
            if (dialogue != null)
            {
                ShowEditorWindow();
                return true;
            }
            return false;
        }

        private void OnEnable()
        {
            Selection.selectionChanged += OnSelectionChanged;

            nodeStyle = new GUIStyle();
            nodeStyle.normal.background = EditorGUIUtility.Load("node0") as Texture2D;
            nodeStyle.padding = new RectOffset(20, 20, 20, 20);
            nodeStyle.border = new RectOffset(12, 12, 12, 12);
        }

        private void OnDisable()
        {
            Selection.selectionChanged -= OnSelectionChanged;
        }

        private void OnSelectionChanged()
        {
            Dialogue newDialogue = Selection.activeObject as Dialogue;
            if (newDialogue != null)
            {
                selectedDialogue = newDialogue;
                Repaint();
            }
        }

        private void OnGUI()
        {
            if (selectedDialogue == null)
            {
                EditorGUILayout.LabelField("No Dialogue selected");
            }
            else
            {
                ProcessEvents();
                foreach (DialogueNode node in selectedDialogue.GetAllNodes())
                {
                    OnGUINode(node);
                }
            }
        }

        private void ProcessEvents()
        {
            if (Event.current.type == EventType.MouseDown && draggingNode == null)
            {
                draggingNode = GetNodeAtPoint(Event.current.mousePosition);
                if (draggingNode != null)
                {
                    dragginOffset = draggingNode.rect.position - Event.current.mousePosition;
                }
            }
            else if (Event.current.type == EventType.MouseDrag && draggingNode != null)
            {
                Undo.RecordObject(selectedDialogue, "Move Dialogue Node");
                draggingNode.rect.position = Event.current.mousePosition + dragginOffset;
                GUI.changed = true;
                GUI.FocusControl(null);
            }
            else if (Event.current.type == EventType.MouseUp && draggingNode != null)
            {
                draggingNode = null;
            }
        }

        private void OnGUINode(DialogueNode node)
        {
            GUILayout.BeginArea(node.rect, nodeStyle);
            EditorGUI.BeginChangeCheck();
            EditorGUILayout.LabelField("Node:");
            string newText = EditorGUILayout.TextField(node.text);
            string newUniqueID = EditorGUILayout.TextField(node.uniqueID);
            if (EditorGUI.EndChangeCheck())
            {
                Undo.RecordObject(selectedDialogue, "Update Dialogue Text");
                node.text = newText;
                node.uniqueID = newUniqueID;
            }
            GUILayout.EndArea();
        }

        private DialogueNode GetNodeAtPoint(Vector2 point)
        {
            DialogueNode foundNode = null;
            foreach (DialogueNode node in selectedDialogue.GetAllNodes())
            {
                if (node.rect.Contains(point))
                {
                    foundNode = node;
                }
            }
            return foundNode;
        }
    }
}


The difference may be that I’m running on the completed code after we handle dragging the canvas itself.

        private void ProcessEvents()
        {
            if (Event.current.type == EventType.MouseDown && draggingNode == null)
            {
                draggingNode = GetNodeAtPoint(Event.current.mousePosition + scrollPosition);
                if (draggingNode != null)
                {
                    draggingOffset = draggingNode.GetRect().position - Event.current.mousePosition;
                    Selection.activeObject = draggingNode;
                }
                else
                {
                    draggingCanvas = true;
                    draggingCanvasOffset = Event.current.mousePosition + scrollPosition;
                    Selection.activeObject = selectedDialogue;
                }
            }
            else if (Event.current.type == EventType.MouseDrag && draggingNode != null)
            {
                draggingNode.SetPosition(Event.current.mousePosition + draggingOffset);

                GUI.changed = true;
            }
            else if (Event.current.type == EventType.MouseDrag && draggingCanvas)
            {
                scrollPosition = draggingCanvasOffset - Event.current.mousePosition;

                GUI.changed = true;
            }
            else if (Event.current.type == EventType.MouseUp && draggingNode != null)
            {
                draggingNode = null;
            }
            else if (Event.current.type == EventType.MouseUp && draggingCanvas)
            {
                draggingCanvas = false;
            }

        }

I think the code that would prevent this from being an issue, but it won’t be introduced for another few lectures.

Update: I plugged your code into the DialogueEditor, and was still unable to duplicate the results… This may be an issue with the Unity version itself. You might consider upgrading to the latest LTS version of Unity 2020.

Ok, even without being able to replicate this issue, I believe I have a solution…

        private void ProcessEvents()
        {
           if (!position.Contains(Event.current.mousePosition))
                {
                    draggingNode = null;
                    return;
                }
            if (Event.current.type == EventType.MouseDown && draggingNode == null)
            {
                draggingNode = GetNodeAtPoint(Event.current.mousePosition);
                if (draggingNode != null)
                {
                    dragginOffset = draggingNode.rect.position - Event.current.mousePosition;
                }
            }
            else if (Event.current.type == EventType.MouseDrag && draggingNode != null)
            {
                Undo.RecordObject(selectedDialogue, "Move Dialogue Node");
                draggingNode.rect.position = Event.current.mousePosition + dragginOffset;
                GUI.changed = true;
                GUI.FocusControl(null);
            }
            else if (Event.current.type == EventType.MouseUp && draggingNode != null)
            {
                draggingNode = null;
            }
        }

While OnMouseLeaveWindow only fires if no buttons are pressed (annoying that they did that, but I believe it’s to facilitate dragging from one window to another), you can always test the position of the mouse against the bounds of the window, which is stored in a field position.

So

                if (!position.Contains(Event.current.mousePosition))
                {
                    draggingNode = null;
                    return;
                }

should automatically reset draggingNode if the mouse leaves the boundary of the EditorWindow.

1 Like

Thank you. It almost works :).
Event.current.mousePosition gives you mouse position in relation to the window position so you need to add it to the eqaution.

            if (!position.Contains(Event.current.mousePosition + position.position))
            {
                draggingNode = null;
                return;
            }

Good catch. As I said, this one was impossible to truly test on my end. Well done!

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms