OnMouseMove is not executed while clicking

I was trying to manage EventReply branching within OnMouseMove, but I realized that the event stops automatically while mouse input is active. I didn’t know about that, I don’t want it, and I don’t understand the reason.

How should I go about fixing this? If something is going to be done automatically, there should at least be an option to disable it.

That’s Slate pointer capture, and it’s why the event vanishes while a button is held. When you press down on a control, that control captures the mouse pointer; while captured, OnMouseMove events are routed only to the capturing widget — everything else (including your widget under the cursor) stops receiving them. It exists so sliders, drags and scrollbars don’t ‘leak’ moves to widgets the cursor passes over mid-gesture. There’s no built-in disable, but you can work with it:

  • If a child control (button/slider) is what captures, handle OnMouseButtonDown in your widget and return Unhandled less often — if your widget is the one that should track the gesture, return Handled from OnMouseButtonDown (after arming yourself) so the child never captures, and OnMouseMove will keep firing on your widget until release.
    • To get moves regardless of who captured: call SetCapture() on your widget in OnMouseButtonDown (and ReleaseCapture on Up). A captured widget gets all move events including during another control’s press.
      • If what you actually want is gesture data, consider OnDragDetected/OnDragOver instead — drag detection is the intended path for ‘moves while button held’.
    • So the event isn’t broken or auto-disabled; the capture owner changed. Returning Handled from your own Down event (or taking capture yourself) keeps OnMouseMove alive through the whole gesture.

I want the event to execute for the widget underneath as well, so using “Handled” is not an option. A function is required that returns an EventReply in this state, either continuously or at any given time.

But that probably doesn’t exist. The people who developed UMG have absolutely no concept of choices or options.

you found the real wall: there is no blueprint function that forwards events to widgets under the capture. that routing logic lives in Slate’s C++ event router (the capture path is resolved first, everything else is skipped) and UMG simply does not expose a “keep routing below” switch, so no EventReply value can produce it. the frustration is valid, it is a genuine gap.

what works instead, cleanest first:

  1. do not let the top widget capture in the first place. a widget only captures the mouse when it returns Handled from OnMouseButtonDown. if the overlay/top widget detects clicks itself (down + up pair on it, or a short-press timer) while returning Unhandled, capture never happens, and the widget underneath keeps receiving moves and everything else. only return Handled when you actually need exclusive gesture ownership.
  2. put the move handling on a shared parent. events that a child does not handle bubble up to ancestors, and moves are delivered to the parent regardless of which child sits under the cursor. if both widgets live under one container, put OnMouseMove on the container and hit-test which child is relevant from there. no capture fight at all.
  3. manual delivery, if you truly need “the widget under the cursor gets the move while another one captures”: the capturing widget is the only one receiving OnMouseMove, so use it — GetMousePosition inside it, then for each candidate widget GetCachedGeometry and test whether the cursor is inside its transformed rect (compare against its local size / use AbsoluteToLocal). call the target widget’s handler function directly. it is the supported-by-hand version of what the router refuses to do.
    for most setups 1 or 2 removes the need for 3 entirely — the capture only exists because some widget returned Handled on the down.

Unfortunately, the parent widget I am using is a list view. Placing an item with “Handled” processing inside this will interfere with its functionality. Also, since child widgets are used in places other than list views, designs that rely on list views for functionality cannot be used.

What I ultimately came up with was use the resumption of OnMouseMove as OnMouseButtonUp. After the button is pressed, an event is executed to save the branch as a Boolean value. When the button is released, OnMouseMove resumes, and the value is used to determine the branching.

This is by no means a clean method, but it is the last option left to me. Let’s hope that decent options will be provided in the next update…

that boolean latch is the right call — it is basically the manual version of pointer tracking, and it survives list views because it never fights the capture. two small upgrades make it robust: store the press position and press time alongside the bool, so on release you can classify click vs drag (distance threshold + hold time) instead of just branching on a flag — that gives you the press-and-hold behaviors for free. second: keep using the last move position you got before capture as your gesture origin, since you only get one move event before the list view swallows the rest.

the list view is exactly why handled was a dead end, by the way — it captures on down for row selection and its own drag detection, so any tile returning handled breaks selection. and if you ever need live cursor position while the button is held (a drag preview following the mouse), the only clean hook left is the list view’s own drag detection flow (on drag detected / on drag over / on drop) since it is already capturing anyway — piggyback on it rather than fight it.

but for press → release evaluation, your latch is the pattern people actually ship. and yeah, a keep-routing-below EventReply would be a change inside slate’s C++ event router, not something a bp update can bolt on — I would not hold my breath for that one.