Can't use MouseMoveEvent without Blocking all Clicks

I’m trying to make a tooltip
I need the mouse position to position the tooltip

I tried to add a full screen element so I could RegisterCallback<PointerMoveEvent>(PointerMove); on it.
In order for it to update when I move the mouse anywhere on the screen the element needs to fill the screen, but if it does that it also blocks all the clicking.

I can’t seem to figure out any way to just let it pass through. I only want to fire the event during the bubbling phase but not actually be a target, this is fairly trivial on the web, but I cannot for the life of me figure out how Unity even manages this…
I don’t see any examples in the code of how to do this either. I’ve played around with ExecuteDefaultActionAtTarget and ExecuteDefaultAction but there doesn’t seem to be anything I can do with a Visual element that lets me selectively observe events, only completely enable/disable.

In fact, as shown in the figure below, all propagable events are sent from the root of the node tree, follow the node tree to the event target, and then return to the root of the node tree.

You can get the root of the node tree like this.

var uiDoc = GetComponent<UnityEngine.UIElements.UIDocument>();
var root = uiDoc.rootVisualElement;

That’s it. Can consider using inherited Manipulator, which can organize the event operation logic better.

public class NewBehaviourScript : MonoBehaviour
{
    public class MouseReportManipulator : Manipulator
    {
        protected override void RegisterCallbacksOnTarget()
        {
            //target will be root node
            target.RegisterCallback<PointerMoveEvent>(OnTrickleDownPointerMoveEvent, TrickleDown.TrickleDown);
            target.RegisterCallback<PointerMoveEvent>(OnPointerMoveEvent);
        }

        protected override void UnregisterCallbacksFromTarget()
        {
            target.UnregisterCallback<PointerMoveEvent>(OnTrickleDownPointerMoveEvent, TrickleDown.TrickleDown);
            target.UnregisterCallback<PointerMoveEvent>(OnPointerMoveEvent);
        }

        //during the trickle down phase
        private void OnTrickleDownPointerMoveEvent(PointerMoveEvent evt)
        {
            //TODO
        }

        //during the bubble-up phase
        private void OnPointerMoveEvent(PointerMoveEvent evt)
        {
            //TODO
        }
    }

    private MouseReportManipulator mouseReportManipulator;

    // Start is called before the first frame update
    void Start()
    {
        var uiDoc = GetComponent<UnityEngine.UIElements.UIDocument>();
        var root = uiDoc.rootVisualElement;

        root.RegisterCallback<AttachToPanelEvent>((evt) =>
        {
            mouseReportManipulator = new MouseReportManipulator();
            root.AddManipulator(mouseReportManipulator);
        });

        root.RegisterCallback<DetachFromPanelEvent>((evt) =>
        {
            root.RemoveManipulator(mouseReportManipulator);
        });
    }

    // Update is called once per frame
    void Update()
    {
    }
}

But this implementation is also a bit problematic. If PointerMoveEvent is blocked by a target using StopImmediatePropagation, StopPropagation, or captured using PointerCaptureHelper CapturePointer, you can no longer receive events in the bubbling phase, so it is safer to handle events in the capture phase.

Here you can see the sequence of event processing and how to block the event. ExecuteDefaultActionAtTarget and ExecuteDefaultAction will only be executed when the target of the event is the VisualElement itself.

Here you can see an introduction to the capture event.

Here you can see an introduction to the capture event.

Hello, sorry to jump in the thread but I have an issue with that event propagation logic.

I’m doing a UI for an ability system. I have a list of abilities that I’m able to drag onto action slot.
That is done with a mouse manipulator modifier that capture the mouse when mouse down event is triggered, mouve the ability and once I’m above and acition slot, it duplicate the ability visual element and add’s it as a child of the action slot.
That works well and I can assigne my abilities to an action slot.

I want to be able to swap my abiities on my action bar, I grab the one in slot 1 and put it in slot 3 and this works.

Now comes the issue. when I I try to grap the one in slot 3 to put in in slot 1 it does not.

Adding some debug it seems that the MouseOverEvent is not triggered on the action slot 1 because it’s “above” in the UXML hierarchy.

I don’t get How I can make it work.

Edit : details, I’m on latest unity 2020 (DOTS/ECS Constraint) and it’s for runitime, not custom editor so I can’t use the built in drag and drop events.

EDit2: Never mind, made it work thanks to gamedev-resources.com - gamedev resources Resources and Information.

The problem is that the Bubble Up and Trickle Down is only relevant if they’re using the same root.
My issue is that I have one UI Document that was in front of a second, and its blocking the interaction with the occluded document (even though it’s a completely invisible element I only need to get mouse position.)

One document is only concerned with mouse position (for displaying tooltips, always on top of the rest of the UI), the other is concerned with mouse position (UI logic, drag drop, inventory, etc).
There doesn’t appear to be a way that I can find for both UI documents to get the mouse events, if any elements in the front most document are active.
There isn’t a Trickle Down through multiple documents, nor a Bubble Up, at least not that I’m able to see in my code.

I thought about this problem, I think it can be handled like this.

If you use two uidocuments to separate the ui logic.
You can consider the following uidocument relationship.

Father-son relationship.
A
-B (pickMode to ignore)

At this time, the MonoBehaviour is bound to the node where UIDocument B is located.

using UnityEngine;
using UnityEngine.UIElements;

public class NewBehaviourScript : MonoBehaviour
{
    public class MouseReportManipulator : Manipulator
    {
        protected override void RegisterCallbacksOnTarget()
        {
            //target will be root node
            target.RegisterCallback<PointerMoveEvent>(OnTrickleDownPointerMoveEvent, TrickleDown.TrickleDown);
            target.RegisterCallback<PointerMoveEvent>(OnPointerMoveEvent);
        }

        protected override void UnregisterCallbacksFromTarget()
        {
            target.UnregisterCallback<PointerMoveEvent>(OnTrickleDownPointerMoveEvent, TrickleDown.TrickleDown);
            target.UnregisterCallback<PointerMoveEvent>(OnPointerMoveEvent);
        }

        //during the trickle down phase
        private void OnTrickleDownPointerMoveEvent(PointerMoveEvent evt)
        {
            //TODO
        }

        //during the bubble-up phase
        private void OnPointerMoveEvent(PointerMoveEvent evt)
        {
            //TODO
        }
    }

    private MouseReportManipulator mouseReportManipulator;
    private UnityEngine.UIElements.VisualElement rootA;

    // Start is called before the first frame update
    void Start()
    {
        var uiDocB = GetComponent<UIDocument>();
        //Use the root of UIDocument A
        rootA = uiDocB.parentUI.rootVisualElement;
        rootA.pickingMode = PickingMode.Position;

        mouseReportManipulator = new MouseReportManipulator();
        rootA.AddManipulator(mouseReportManipulator);
    }

    // Update is called once per frame
    void Update()
    {
    }
}

Sibling.
A
B(pickMode to ignore)

Or sibling like this.
P
-A
-B(pickMode to ignore)

You can consider getting the node where A is, and get the uidocument in it, and then use the root of uidocument A as the root.

Hope these methods can help you.

Some useful information.
7181620--861178--upload_2021-5-27_21-9-56.jpg7181620--861184--upload_2021-5-27_21-9-56.jpg7181620--861181--upload_2021-5-27_21-9-56.jpg

I tested this situation and it works

I thought about your question, and I think what you mean is because there are other content covering slot1. You can consider using BringToFront and SendToBack in VisualElement to adjust the level of VisualElement, or use Sort (Comparison ) to regularly arrange the levels of all the content in the current slot in a certain order.

I still have some unclear questions. If you can display the level of Visualelement in uidebugger, it is convenient for me to further understand your problem.:stuck_out_tongue:

Hi, thanks for your time.
Like I said I found a warkaround involving a singleton and overlaping bounds comparison but I’d be trhilled to have it all work with proper events.

Here is what it look like :

7184257--861601--upload_2021-5-28_7-57-49.png

#book-root is my list of abilities
#A1 #A2 #A3 are my action slots.
When i grab an ability from the book I can put it in any slot.
One all slot are populated, I can swap abilities between slots but only if the slot is lower in the hierarchy :
#A2 to #A3 or #A1 works
#A3 to #A1 works but not to #A2
and I can’t move #A1.

Note that the same thing happen if I change the order of my action slot and the book. If hte action slots are “above” the book, then I can’t assigne anything from the book to the action slots.

I hope it’s a bit simpler to understand explained like that ;).

Sorry for being late, I know what the problem is.

When the lower-level content moves to the upper-level target, the upper-level target will receive the mouseover event, but when the upper-level content moves to the lower-level, the upper-level content will be overlaid on the lower-level target, so the higher-level content receives the Mouseover event.

@[WAYN_Games]( Can't use MouseMoveEvent without Blocking all Clicks members/wayn_games.2835889/)
As shown in the figure below, events generally only propagate along the path from the root to the target on the node tree, so when the target covers other nodes of the same depth, the other nodes of the same depth will not receive the event.

When dealing with dragging, I am generally used to handle these things on the common ancestor node of the node that needs to be dragged, or use the method provided by the common ancestor to handle the drag in the node that needs to be dragged.

I wrote a demo here for your reference.

The hierarchical relationship of nodes is shown in the figure below. The demonstration in area1 uses the common ancestor to handle dragging, and the demonstration in area2 uses the method provided by the common ancestor to handle dragging.

I hope these can help you.:wink:

7187602–862225–DragDemo.unitypackage (205 KB)

OK, thanks for your demo project.
From what I can see, you are using a similar approach to the one I found in the mentionend blog. difference beeing you rely on a the uxml parent to hold the ‘drop’ areas whereas the blog (and I) use singleton.

I think I’ll stick with the singleton has it offers IMO better flexibility in the uxml structure and should also work across multiple UIDocuments.