Example code: Creating a drag-scrollable view in an editor window with UIElement

This lets you drag a view of visual elements in an editor window.

Idea is:

  • Create a visual element on the root that fills the view and interprets mouse events to drag the view.
  • Add a dummy element to the root, and add your elements on to that.
  • The first element adds the mouse delta of the drag event to the dummy element.

Problems:

  • Haven’t worked on how to capture the event yet so if the mouse goes over one of the visual elements the drag stops

have put the code all in one script for now as i’m just jamming stuff out

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.Experimental.UIElements;
using UnityEditor.Experimental.UIElements;
using UnityEngine.Experimental.UIElements.StyleEnums;
public class DraggerWindow : EditorWindow {

    [MenuItem("UIElement examples/Draggable view")]
    public static void OpenWindow()
    {
        var window = GetWindow<DraggerWindow>();
        window.minSize = new Vector2(700, 700);
        window.titleContent = new GUIContent("Draggable view example");
    }

    float zoom = 1;

    void OnEnable()
    {
        // UIElement set up.
        var root = this.GetRootVisualContainer();
        VisualElement dummy = new VisualElement()
        {
            style ={
                width=0,
                height=0
            }
        };
        dummy.clippingOptions = VisualElement.ClippingOptions.NoClipping;
        VisualElement panel = new VisualElement()
        {
            style ={
                // stretch to fit whole display
                flex = 1,
                alignSelf = Align.Stretch,
                flexDirection = FlexDirection.Row,
                backgroundColor = Color.black
            }
        };
        panel.style.backgroundColor = Color.grey;
        root.Add(panel);
        DragManipulator dm = new DragManipulator(dummy);
        panel.AddManipulator(dm);
        root.Add(dummy);
        root.style.backgroundColor = Color.black;

        for (int i = 0; i < 10; i++)
        {
            VisualElement box = new VisualElement()
            {
                style ={
                    width = 50,
                    height=50,
                    backgroundColor = Random.ColorHSV()
                }
            };
            dummy.Add(box);
            box.transform.position = new Vector3(Random.Range(100, 600),-Random.Range(100, 600), 0);

        }
    }

}


class DragManipulator : Manipulator
{
    VisualElement passTo;
    bool dragging = false;
    public DragManipulator(VisualElement passTo)
    {
        this.passTo = passTo;
    }
    protected override void RegisterCallbacksOnTarget()
    {
        target.RegisterCallback<MouseEnterEvent>(OnMouseEnterEvent);
        target.RegisterCallback<MouseLeaveEvent>(OnMouseLeaveEvent);
        target.RegisterCallback<MouseDownEvent>(OnMouseDownEvent);
        target.RegisterCallback<MouseUpEvent>(OnMouseUpEvent);
        target.RegisterCallback<MouseMoveEvent>(OnMouseMoveEvent);
    }
    protected override void UnregisterCallbacksFromTarget()
    {     
        target.UnregisterCallback<MouseEnterEvent>(OnMouseEnterEvent);
        target.UnregisterCallback<MouseLeaveEvent>(OnMouseLeaveEvent);
        target.UnregisterCallback<MouseDownEvent>(OnMouseDownEvent);
        target.UnregisterCallback<MouseUpEvent>(OnMouseUpEvent);
        target.UnregisterCallback<MouseMoveEvent>(OnMouseMoveEvent);
    }


    protected virtual void OnMouseEnterEvent(MouseEventBase<MouseEnterEvent> evt)
    {
    }
    protected virtual void OnMouseLeaveEvent(MouseEventBase<MouseLeaveEvent> evt)
    {
        // this hack gets around the fact I don't know how to capture events at the moment.
        // if removed and the mouse leaves the element while dragging, the element gets stuck in drag mode.
        dragging = false;
    }
    protected virtual void OnMouseDownEvent(MouseEventBase<MouseDownEvent> evt)
    {
        //target.TakeMouseCapture()
        evt.StopPropagation();
        // start the drag
        dragging = true;
    }
    protected virtual void OnMouseUpEvent(MouseEventBase<MouseUpEvent> evt)
    {
        evt.StopPropagation();
        // stop the drag
        dragging = false;
    }

    protected virtual void OnMouseMoveEvent(MouseEventBase<MouseMoveEvent> evt)
    {
        if (dragging)
        {
            evt.StopPropagation();
            OnDrag(evt);
        }
    }

    protected virtual void OnDrag(MouseEventBase<MouseMoveEvent> evt)
    {
        passTo.transform.position += new Vector3(evt.mouseDelta.x, evt.mouseDelta.y, 0);
    }
}

Hi,

Just tried your snippet for something I need, I made 2 changes : made an interface to receive the drag event (and make DragManipulator reusable) and maybe a fix for your issue ?

Here is the code :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.UIElements;

public interface IDragEventHandler
{
    void DidDrag(Vector3 drag);
}

public class DragManipulator : Manipulator
{
    IDragEventHandler passTo;

    bool dragging = false;
    public DragManipulator(IDragEventHandler passTo)
    {
        this.passTo = passTo;
    }
    protected override void RegisterCallbacksOnTarget()
    {
        target.RegisterCallback<MouseEnterEvent>(OnMouseEnterEvent);
        target.RegisterCallback<MouseLeaveEvent>(OnMouseLeaveEvent);
        target.RegisterCallback<MouseDownEvent>(OnMouseDownEvent);
        target.RegisterCallback<MouseUpEvent>(OnMouseUpEvent);
        target.RegisterCallback<MouseMoveEvent>(OnMouseMoveEvent);
    }
    protected override void UnregisterCallbacksFromTarget()
    {
        target.UnregisterCallback<MouseEnterEvent>(OnMouseEnterEvent);
        target.UnregisterCallback<MouseLeaveEvent>(OnMouseLeaveEvent);
        target.UnregisterCallback<MouseDownEvent>(OnMouseDownEvent);
        target.UnregisterCallback<MouseUpEvent>(OnMouseUpEvent);
        target.UnregisterCallback<MouseMoveEvent>(OnMouseMoveEvent);
    }


    protected virtual void OnMouseEnterEvent(MouseEventBase<MouseEnterEvent> evt)
    {
    }
    protected virtual void OnMouseLeaveEvent(MouseEventBase<MouseLeaveEvent> evt)
    {
        // this hack gets around the fact I don't know how to capture events at the moment.
        // if removed and the mouse leaves the element while dragging, the element gets stuck in drag mode.
        //dragging = false;
    }
    protected virtual void OnMouseDownEvent(MouseEventBase<MouseDownEvent> evt)
    {
        evt.target.TakeMouseCapture();
        //target.TakeMouseCapture()
        evt.StopPropagation();
        // start the drag
        dragging = true;
    }
    protected virtual void OnMouseUpEvent(MouseEventBase<MouseUpEvent> evt)
    {
        evt.StopPropagation();
        // stop the drag
        dragging = false;
        evt.target.ReleaseMouseCapture();
    }

    protected virtual void OnMouseMoveEvent(MouseEventBase<MouseMoveEvent> evt)
    {
        if (dragging)
        {
            evt.StopPropagation();
            OnDrag(evt);
        }
    }

    protected virtual void OnDrag(MouseEventBase<MouseMoveEvent> evt)
    {
        passTo.DidDrag(new Vector3(evt.mouseDelta.x, evt.mouseDelta.y, 0));
    }
}