Search Unity

Question VisualElement Drag and Drop during runtime.

Discussion in 'UI Toolkit' started by Chris-Trueman, Jul 12, 2020.

  1. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    I am trying to get drag and drop to work at runtime. I have the drag working, but the 5 different drop events do not trigger.

    I found this page here for making it work. The problem lies with DragAndDrop being an editor class.

    How do you setup drag and drop to work at runtime?
     
  2. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Okay, I have found a some what working solution. It is very cumbersome and not easy to do.

    I followed the texture dragging example from the unite video/posted on github to get dragging working.

    I made an extension method to get the root VisualElement.
    I then get all the VisualElements from that root that has picking mode set to position.

    At that point I use the MouseMoveEvent to iterate over all the VisualElements and check the world bounds for overlapping. I then simulate the drag events according to what phase it is. I made a custom element that accepts drops which is mostly for visual effect, but also allows for drop to be disabled. Got that setup to work some what in UIBuilder.

    I need to figure out how to get the inspector to list a few values without it adding it to the element itself. The example @uDamian posted here works fine for the editor, but also added the elements to the scene at runtime.

    One other problem I have is, I am moving the dragged element to the root and putting it in front of everything. When I do this it puts the element in the top left corner if the mouse doesn't move(the left and top are 0 from the last parent). Once I move it brings it to the right location with the mouse. How do I get it to stay in the same position when I change the parent?
     
  3. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    What kind of values do you want to display in the (I assume) UI Builder attributes inspector, but not add them as real attributes on the element?

    UI Toolkit does not have an automatic "re-parent without changing visible position" function. Absolute positioning is actually actively discouraged and only useful in special cases (like drag and drop :) ). That said, my suggestion is to create a "drag layer" element at the root, like this:

    rootVisualElement
    contentContainer
    dragDropLayer

    Then, when starting a drag, reparent the element to the dragDropLayer so it's always on top and use Absolute + top/left to move it around. Once done dragging, reparent it as needed, removing Absolute + top/left styles.
     
    Findeh likes this.
  4. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Some basic values like bool's, Vector2's and enums. I have a custom element that sets the position of it's children based on an offset. I would like to be able to be set them in the builder so I don't need to have monolithic code for each element.

    I am already somewhat doing that, minus the dragDropLayer. I put the item(s) being dragged on the root and make sure to bring them to the front. I have it working most of the time if I use a drag state system that doesn't re-parent the element until you physically start dragging it. This has almost eliminated the elements popping up to the top left corner, though it still does it every now and then. I need to test further to see what is causing it to happen when it shouldn't, theoretically.

    About the absolute positioning being discouraged I completely understand. After setting up some custom inspectors and editor windows it makes short work of a lot of what I was doing in the past with IMGUI. The UIBuilder is really a god send, even with the bugs.
     
  5. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Right, that's what UXML attributes are for. I'm just curious why you specifically said this:
    Like, how can you add them to the Builder but not also on the element? Remember that the Builder can only "edit" values can be saved in the UXML. If you move some child of a custom C# element to the right, and that child is not in the UXML because it's being created by the custom C# element, this won't be saved in UXML and your change will be lost when you reload your UXML. Use the UXML Preview at the bottom of the Viewport to see what is actually being saved.

    Please do send us any bugs you come across in the UI Builder. Either post on the forum if unsure or submit a bug report via the Unity Bug Reporter. We want to iron those out.
     
  6. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Sorry I worded that wrong, yes I only want it in the inspector of the UIBuilder and not on the element itself.

    This makes sense. I need the attributes to be saved in the UXML so when I query the element I don't need to worry about setting those values.

    I've added the dragDropLayer to avoid some bugs that kept happening with it getting stuck in the root element. That and it was going over top of other HUD items that it shouldn't.

    Is there a way to keep the world/root position of the element when changing parents? Any drag/drop inventory system is going to need this.
     
  7. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    I wanted to update to let you know I figured out how to get the attributes to save in the Uxml. I found the Unity document page here, noticed it was a simple change to your example and it works great.
     
  8. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Found a bug with attributes, it is doing the same thing that it was doing for read only. You can change the value in the inspector, save it, and the value resets. It seems like it just not displaying the values from the uxml.
     
  9. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Out of curiosity, what was the change you need to make?

    The above two statements are currently mutually exclusive. If you don't add a public C# property, named the same as the uxml attribute (but without '-'), to your element, the UI Builder Inspector won't be able to read it back after it sets it via UXML and that's why the UI Builder inspector looks broken.

    That's why my example has C# properties for all UXML attributes it exposes.
     
  10. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Instead of adding visual elements in the Init method, I got the values from the bag and applied them to the objects fields instead, like it showed in the docs.

    Code (CSharp):
    1. public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
    2. {
    3.     base.Init(ve, bag, cc);
    4.     CardPosition cp = ve as CardPosition;
    5.  
    6.     cp.DropAllowed = m_Bool.GetValueFromBag(bag, cc);
    7.     cp.nextCardOffset = new Vector2(m_Left.GetValueFromBag(bag, cc), m_Top.GetValueFromBag(bag, cc));
    8.     cp.CardAddType = m_CardAddType.GetValueFromBag(bag, cc);
    9. }
    I couldn't find a Uxml attribute for Vector2 so I used two floats instead. Is there a better way?

    The two floats won't work in this scenario then, as I have it taking the two floats and converting them to a Vector2, so UIBuilder won't be able to read the properties for them because they don't exist.

    Once I had the rest of properties the same name it saved them and displayed them properly. All I need now is a way to handle Vector2's.
     
  11. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Ah yes, makes sense. No need to create new elements of course. That's just my example.

    There's no built-in way but you can inherit from
    TypedUxmlAttributeDescription<T>
    and define your own custom attribute with a string conversion function that works with Vector2.
     
    Chris-Trueman likes this.
  12. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    I'm trying to figure out a bit of a visual bug when it displays the Vector2 back to me after reloading the uxml. It's rounding the values so that 0.25 becomes 0.3. Its not a big problem because in the uxml it shows 0.25, but I don't want the values rounded at all in the inspector.

    This is my custom uxml attribute description
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UIElements;
    3. using System.Collections;
    4.  
    5. public class UxmlVector2AttributeDescription : TypedUxmlAttributeDescription<Vector2>
    6. {
    7.     public override string defaultValueAsString => Vector2.zero.ToString();
    8.  
    9.     public override Vector2 GetValueFromBag(IUxmlAttributes bag, CreationContext cc)
    10.     {
    11.         Vector2 v = defaultValue;
    12.  
    13.         if (bag.TryGetAttributeValue(name, out string value))
    14.         {
    15.             v = Utils.Vector2FromString(value);
    16.         }
    17.  
    18.         return v;
    19.     }
    20. }
    And the Utils method.
    Code (CSharp):
    1. public static Vector2 Vector2FromString(string value)
    2. {
    3.     Vector2 v = Vector2.zero;
    4.     string[] values = value.Substring(1, value.Length - 2).Split(',');//Need to remove the parentheses.
    5.  
    6.     if (float.TryParse(values[0], out float x))
    7.     {
    8.         v.x = x;
    9.     }
    10.  
    11.     if (float.TryParse(values[1], out float y))
    12.     {
    13.         v.y = y;
    14.     }
    15.  
    16.     return v;
    17. }
    Also is there a way to define how UIBuilder writes the values to the uxml? I am saying this because I am assuming that its is using the ToString() method, or is it how I define the defaultValueAsString?
     
  13. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    There's no way to customize the UI Builder's Attributes Inspector in any official way. It's not currently a priority but it's been asked a few times before, like:
    https://forum.unity.com/threads/enumfield-source.945491/

    so I'll see what I can do about bumping the priority on this.
     
    Chris-Trueman likes this.
  14. muthu0914_unity

    muthu0914_unity

    Joined:
    Oct 1, 2020
    Posts:
    5
    Could you help me out?? i need to drag and drop the visual elements at runtime too..
    the DragandDrop examples are too confusing for me
    https://github.com/Unity-Technologi...ter/Assets/Examples/Editor/E20_DragAndDrop.cs
     
  15. muthu0914_unity

    muthu0914_unity

    Joined:
    Oct 1, 2020
    Posts:
    5
    Could you please say how to reparent the visual element and do the same logic you have done, shed some light on code.
     
  16. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    I am also having issues with this. How do I capture a 'mouse up' event if the user cancels the drag outside the game window?
     
  17. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    The problem with that example is, it's for the editor. The drag and drop events do not seem to work at runtime at all. I had to hack a very inefficient way of getting all the elements that can be dropped on and check for bounds overlaps on the dragged element. The events would make it much simpler. If I get some time I can post the code to github or something like that for a basic example, it's not trivial and most of the code is specific to the project.

    Its pretty basic.

    Code (CSharp):
    1. //Remove from current parent
    2. element.RemoveFromHierarchy();
    3.  
    4. //Add to new parent
    5. newParent.Add(element);
     
    Last edited: Oct 2, 2020
    muthu0914_unity likes this.
  18. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Still working on that myself :(.
     
    Maverick likes this.
  19. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    I think it can be done by ditching the event system and using a schedule update and use Input.Mouse functions, but well... that's a strange way to go about it.
     
  20. muthu0914_unity

    muthu0914_unity

    Joined:
    Oct 1, 2020
    Posts:
    5
    That would be great..!! Please do that..
     
  21. muthu0914_unity

    muthu0914_unity

    Joined:
    Oct 1, 2020
    Posts:
    5
    could you provide a link to that..?
     
  22. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    muthu0914_unity likes this.
  23. Yecats

    Yecats

    Joined:
    Jul 13, 2014
    Posts:
    69
    toomasio, dlorre and StonePaleAle like this.
  24. shanecelis

    shanecelis

    Joined:
    Mar 26, 2014
    Posts:
    22
    I made a little runtime drag-and-drop system for UIToolkit. MIT licensed.



    In doing that I wrote a
    ChangeParent()
    method that does what you asked. It was conceptually simple but tricky to get right because of when changes take effect. Basically it required a well placed
    schedule.Execute()
    .

    Code (CSharp):
    1.   public static IVisualElementScheduledItem ChangeParent(VisualElement target,
    2.                                                          VisualElement newParent) {
    3.     var position_parent = target.ChangeCoordinatesTo(newParent, Vector2.zero);
    4.     target.RemoveFromHierarchy();
    5.     target.transform.position = Vector3.zero;
    6.     newParent.Add(target);
    7.     // ChangeCoordinatesTo will not be correct unless you wait a tick. #hardwon
    8.     return target.schedule.Execute(() => {
    9.       var newPosition = position_parent - target.ChangeCoordinatesTo(newParent,
    10.                                                                      Vector2.zero);
    11.       target.RemoveFromHierarchy();
    12.       target.transform.position = newPosition;
    13.  
    14.       newParent.Add(target);
    15.     });
    16.   }
    17.  
     
  25. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    I love the design of your game!
     
    shanecelis likes this.