UI Drag & Drop
Any element can be picked up, and any element can catch what is dropped on it. That is enough to build inventories, card hands, equipment slots, crafting benches and movable windows, with the interface script deciding what each drop actually means.
Overview
Drag and drop has two halves that are configured completely separately:
- The source is opted in with a property. Tick Is Draggable on an element and the player can pick it up.
- The target is opted in by wiring. Wire an element's On Dropped event in the interface script and it becomes a drop target. There is no property for it.
The runtime itself does nothing on a drop. It fires the target's On Dropped event, handing it the source's drag tags and the dragged element, and your graph decides what changes. Nothing moves, nothing is consumed and no state is written unless you write it.
The runtime carries, the graph decides
Making an Element Draggable
Select the element and open the Drag and Drop section of Element Settings. Ticking Is Draggable reveals the source settings under it. Is Drag Handle sits in the same section but is always shown, since it does not depend on Is Draggable.
| Setting | What it does |
|---|---|
| Is Draggable | Lets the player pick up and drag this element in Play and in exports. Scripts can flip it at runtime with a Set Is Draggable node, which takes effect on the next press. |
| Drag Behavior | Payload (the default) drags a copy to a drop target. Move slides the element itself and keeps it where you let go. Move is only selectable on a canvas-positioned element. |
| Drag Tags | Short names for what this item is, such as sword_01 or weapon, separated by commas. Drop targets receive them and can filter by them. |
| Drag Visual | A User Interface file shown as the ghost under the cursor. Leave it empty to show a copy of the element itself. |
| Ghost Anchor | Grab Point (the default) glues the ghost to where you picked the element up, so it lifts in place and moves like the element itself. At Cursor makes it trail the pointer instead, which suits icon-style visuals. |
| Ghost Opacity | How see-through the ghost looks while dragging, from 0 to 1. |
| Is Drag Handle | Makes this element the grab area for the nearest draggable parent. Independent of Is Draggable, so it sits on any element. |
Turning Is Draggable back off clears the drag properties with it, so an element you experimented with does not keep stale settings.
The Drag Ghost
A Payload drag shows a ghost in a layer above everything else. It is sized to the element you grabbed in both modes, so it reads at the same scale as the thing you picked up.
The ghost is a static picture. It has no id, no bindings and no listeners, it is never hit-tested and its script graph does not run. That is deliberate: a live copy of an element would be found by the same lookups as the original and would fight it. If you set a Drag Visual, the referenced file is rendered the same inert way and ships inside Play and export bundles just like an instance reference does, following renames and deletions with it.
Pick the anchor that matches the ghost
Grab Point suits a ghost that looks like the element, because the item appears to lift off the board and travel under your hand. At Cursor suits a small icon-style Drag Visual, because a compact ghost pinned to an arbitrary grab offset reads as detached. Since the ghost is always sized to the element you grabbed, a Drag Visual meant to be small is best used on a small element.
Drag Handles
By default a draggable element can be grabbed anywhere on it. Tick Is Drag Handle on a child, typically a title bar, and the behavior changes: the draggable can then only be armed through that handle. A press anywhere else on it falls through, so the buttons and sliders inside a movable window stay fully interactive.
The handle is only the press surface. The element that actually moves is always the nearest draggable ancestor.
Making an Element a Drop Target
Wire On Dropped on the element in the interface script. That single act is what makes it a target. An element with no On Dropped node is not a target at all, and drags pass straight through it.
On Dropped hands the graph two things: the source's Drag Tags exactly as they were typed, on a string pin, and the dragged element itself on an element pin, so you can read or set properties on it. Note that the tag pin carries the raw comma string. Only the matching rules below split it into individual tags.
Filtering by Drag Tags
Tick Filter Drop Tags on a target and two more rows appear:
- Filter Mode: Allow listed accepts only the listed tags, Block listed accepts everything except them.
- Allowed Drag Tags or Disallowed Drag Tags, depending on the mode. Comma separated and not case sensitive.
Matching is an intersection: the drag is accepted when the source shares at least one tag with the target's list. The edge cases are literal and worth knowing:
| Situation | Result |
|---|---|
| No filter at all | Accepts every drag |
| Allow list with entries | Accepts a source sharing one of them |
| Empty Allow list | Accepts nothing |
| Empty Block list | Accepts everything |
| Source with no Drag Tags | Rejected by every Allow list, accepted by every Block list |
The filter is inert until On Dropped is wired, since a non-target has nothing to filter.
Fall-Through & Stacking
A target that rejects a drag is invisible to it. It gets no highlight, fires no On Drag Enter or On Drag Leave, and the drop falls straight through to whatever accepting target sits beneath it.
Targets are found by geometry rather than by pointer events: the runtime tests the cursor against every target's rectangle, and when several overlap the last one in paint order wins. Because the hit test never touches pointer events, a drop container laid over your interface can never block clicks meant for what is behind it.
Silent rejection is a feature
weapon sits on top of a general inventory grid: drop a sword and the slot takes it, drop a potion and the slot is not even there as far as the drag is concerned, so the grid underneath receives it.
The Six Drag Events
Every element type offers all six, so the Events list in Element Settings is never empty. Each row carries the description below as its tooltip.
| Event | Fires on | When |
|---|---|---|
| On Drag Detected | The source | When the player starts dragging it. Needs Is Draggable on, set here or via Set Is Draggable. |
| On Drag Enter | The target | When a dragged item moves onto it and its drag tags are accepted. Carries the tags as typed plus the dragged element. |
| On Drag Leave | The target | When a dragged item moves off it. |
| On Dropped | The target | When a dragged item is released on it. Carries the tags as typed plus the dragged element. Wiring this is what makes the element a drop target. |
| On Moved | The source | When the player drops it after dragging it with Move behavior. Read the new spot with Get Position X and Get Position Y. |
| On Drag Cancelled | The source | When the drag ends nowhere: released over empty space, or cancelled with Escape. |
Enter and Leave need a target
On Drag Enter and On Drag Leave only fire on elements that are genuinely drop targets, which means On Dropped is wired on them. An element with an Enter node but no Dropped node never fires. That rule exists so a hover reaction cannot quietly turn an element into a drop catcher that swallows drops meant for something underneath.
Ordering is predictable. A Leave always fires on the previously hovered target before the new one gets its Enter, and every On Dropped is preceded by an On Drag Enter on the same element, even when a fast flick means the cursor never reported hovering it.
The Move Behavior
Set Drag Behavior to Move and the drag stops being about payloads. There is no ghost, no drag tags and no targets. The real element follows the pointer and stays where you let go, which is how you build a movable window, a repositionable HUD panel or a draggable map marker.
Move differs from Payload in a few concrete ways:
- It needs a canvas-positioned element. A child laid out by a container has no free coordinate to write, so the option is greyed out with the reason Move needs a canvas-positioned element.
- Keep on Screen replaces the payload rows. It is on by default and keeps the whole element inside its container while dragging. Turn it off to let the element slide off screen. For a top-level element the container is the screen. For one nested in a smaller panel the clamp is that panel, so it cannot slide under a clipping edge and vanish.
- On Moved fires when it is dropped, and Get Position X and Get Position Y read where it landed.
- Escape puts it back. Cancelling a Move drag restores the element to exactly the pose it had when you picked it up.
A moved position is not saved
Mouse, Touch & Cancelling
- Mouse: pressing a draggable arms a drag without preventing anything, so a plain click still works normally. The drag begins once the pointer crosses a few pixels.
- Touch and pen: a drag begins after a short press and hold in place. Moving before the hold completes is treated as a scroll and disarms the pending drag, so normal scrolling is unaffected. Once a touch drag is live, scrolling and the native long-press menu are both suppressed.
- Cancelling: press Escape to abort, which fires On Drag Cancelled on the source. Releasing a Payload drag over nothing counts as a cancel too. A Move drag has no targets, so it always commits where you let go and fires On Moved instead. If the system takes the pointer away, for example on a tab switch, the drag aborts the same way.
- Cursors: a draggable with no handle shows the grab cursor, as does a drag handle. A draggable that owns handles keeps its own body cursor, so only the handle advertises the grab. While a drag is live everything on the page shows the grabbing cursor.
- Cursor override: the Cursor row in the Behavior section always wins. Set anything other than Auto and the automatic pointer and grab cursors step aside.
- Tooltips are hidden for the duration of a drag and cannot open while one is running.
A drag never leaves a stray click behind. The click the browser would normally generate after the release is swallowed, so releasing a dragged element over a button does not also fire that button's On Click.
Is Enabled Gates Everything
Is Enabled in the Behavior section is universal, on every element type, and it is the single switch that turns input off. A disabled element ignores clicks, drags and drops.
- A disabled element cannot start a drag, and one disabled mid-gesture will not lift.
- A disabled element is skipped by the drop hit test, so drops fall through it exactly like a rejecting filter does.
- The gate is inherited. An element inside a disabled container is disabled too.
- An element whose Visibility is Hidden keeps its layout space but is skipped by the hit test as well.
- Scripts flip it with a Set Is Enabled node, and the interactive controls also repaint into their Disabled colors.
Disabling a slot is the cleanest way to close it
Rather than removing a drop target's wiring or emptying its allow list, untick Is Enabled on it. The slot keeps its layout and its styling and drops route to whatever sits behind it. If the slot is an interactive control it also repaints into its Disabled colors, though a Box or an Image has no Disabled palette to show.
Testing a Drag
The Designer canvas does not simulate drags, in the same way it does not simulate tooltips. It stays a design surface. To try a drag for real, use Play in Editor or an export.
A checklist when nothing happens
If a drop does nothing, work down this list. Is Is Draggable ticked on the source. Is On Dropped wired on the target. Is Is Enabled on for the target and every one of its ancestors. Does the tag filter accept the source, remembering that an empty Allow list accepts nothing and an untagged source fails every Allow list. Finally, is another target stacked over the one you meant, since the last one in paint order wins.