Luna UI provides a flexible drag and drop system through the DragAndDropManipulator class. This abstract manipulator handles pointer events, visual feedback, and drop slot detection.

Drag and Drop

Features

  • Abstract Base Class: Extend to create custom drag visuals
  • Drop Slot Detection: Automatic overlap detection with drop targets
  • Visual Feedback: Apply USS classes to drop slots during drag
  • World Space Support: Works with both Screen Space and World Space UI
  • Event System: Events for drag start, move, drop, and cancel

Basic Usage

1. Create a Custom Manipulator

Extend DragAndDropManipulator and implement the abstract methods:

csharp
using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; using CupkekGames.Luna; public class ItemDragManipulator : DragAndDropManipulator { private Sprite _itemSprite; private VisualElement _dragElement; public ItemDragManipulator( LunaUIManager uiManager, VisualElement dragArea, int itemKey, List<VisualElement> dropSlots, Sprite itemSprite ) : base(uiManager, dragArea, itemKey, dropSlots) { _itemSprite = itemSprite; } public override VisualElement CreateDragElement() { _dragElement = new VisualElement(); _dragElement.AddToClassList("drag-item"); _dragElement.style.width = 64; _dragElement.style.height = 64; _dragElement.style.backgroundImage = new StyleBackground(_itemSprite); return _dragElement; } public override void DisposeDragElement() { _dragElement = null; } }

2. Add the Manipulator to Elements

csharp
// Get references LunaUIManager uiManager = LunaUIManager.Instance; VisualElement dragArea = _uiDocument.rootVisualElement; List<VisualElement> dropSlots = dragArea.Query<VisualElement>(className: "inventory-slot").ToList(); // Create and add manipulator to draggable item var manipulator = new ItemDragManipulator( uiManager, dragArea, itemKey: 0, dropSlots, itemSprite ); // Subscribe to events manipulator.OnDrop += HandleDrop; manipulator.OnCancel += HandleCancel; // Add to the draggable element draggableItem.AddManipulator(manipulator);

3. Handle Drop Events

csharp
private void HandleDrop(int key, int dropSlotIndex, VisualElement dropSlot) { Debug.Log($"Item {key} dropped on slot {dropSlotIndex}"); // Move item to new slot } private void HandleCancel() { Debug.Log("Drag cancelled - no valid drop target"); }

Constructor

csharp
public DragAndDropManipulator( LunaUIManager uiElementManager, VisualElement dragArea, int key, List<VisualElement> dropSlots, Func<List<VisualElement>> getDropSlots = null, bool worldSpace = false )
ParameterTypeDescription
uiElementManagerLunaUIManagerReference to LunaUIManager for audio handling (can be null)
dragAreaVisualElementThe area where the drag clone will be added
keyintUnique identifier for the dragged item
dropSlotsList<VisualElement>Static list of valid drop targets
getDropSlotsFunc<List<VisualElement>>Optional function to get dynamic drop slots
worldSpaceboolSet to true for World Space UI

Abstract Methods

CreateDragElement

Called when drag starts. Must return the visual element to display during drag.

csharp
public abstract VisualElement CreateDragElement();

DisposeDragElement

Called when drag ends. Clean up any resources used by the drag element.

csharp
public abstract void DisposeDragElement();

Events

EventParametersDescription
OnStartVector3 positionFired when drag begins
OnMoveVector3 positionFired while dragging
OnDropint key, int slotIndex, VisualElement slotFired when dropped on valid slot
OnCancel-Fired when dropped outside valid slots

Drop Slot Highlighting

Use SetDropSlotClasses to apply USS classes to drop slots during drag:

csharp
// Set classes to apply during drag manipulator.SetDropSlotClasses(new List<string> { "drop-highlight", "glow" });
css
/* USS styles */ .drop-highlight { border-color: var(--color-primary); border-width: 2px; } .glow { background-color: rgba(255, 255, 255, 0.1); }

Dynamic Drop Slots

For drop slots that change at runtime, use the getDropSlots parameter:

csharp
var manipulator = new ItemDragManipulator( uiManager, dragArea, key: 0, dropSlots: null, // No static slots getDropSlots: () => GetCurrentInventorySlots() // Dynamic function );

World Space UI

For World Space UI Toolkit panels, set worldSpace to true:

csharp
var manipulator = new ItemDragManipulator( uiManager, dragArea, key: 0, dropSlots, itemSprite, worldSpace: true // Enable world space coordinate conversion );

The manipulator automatically handles coordinate conversion between screen space and panel local coordinates.

Complete Example

csharp
using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; using CupkekGames.Luna; public class InventoryDragDrop : MonoBehaviour { [SerializeField] private UIDocument _uiDocument; private List<ItemDragManipulator> _manipulators = new(); private void Start() { SetupDragAndDrop(); } private void SetupDragAndDrop() { var root = _uiDocument.rootVisualElement; var dropSlots = root.Query<VisualElement>(className: "inventory-slot").ToList(); foreach (var slot in dropSlots) { var itemElement = slot.Q<VisualElement>("Item"); if (itemElement == null) continue; int slotIndex = dropSlots.IndexOf(slot); var manipulator = new ItemDragManipulator( LunaUIManager.Instance, root, slotIndex, dropSlots, GetItemSprite(slotIndex) ); manipulator.SetDropSlotClasses(new List<string> { "drop-target-active" }); manipulator.OnDrop += OnItemDropped; manipulator.OnCancel += OnDragCancelled; itemElement.AddManipulator(manipulator); _manipulators.Add(manipulator); } } private void OnItemDropped(int sourceSlot, int targetSlot, VisualElement targetElement) { Debug.Log($"Moved item from slot {sourceSlot} to slot {targetSlot}"); SwapItems(sourceSlot, targetSlot); RefreshUI(); } private void OnDragCancelled() { Debug.Log("Drag cancelled"); } private void OnDestroy() { foreach (var manipulator in _manipulators) { manipulator.OnDrop -= OnItemDropped; manipulator.OnCancel -= OnDragCancelled; } } }

LunaUIManager Usage

LunaUIManager's purpose in DragAndDropManipulator is to prevent hover audio from playing when hovering over other buttons and interactable elements while dragging. When a drag operation starts, if LunaUIManager is not null, hover audio will be muted until the drag operation ends.

Note: For more advanced examples, refer to the Inventory implementation in the Game samples.

Tips

  • The drag element is positioned with absolute positioning within the drag area
  • Audio is automatically disabled during drag to prevent hover sounds (when LunaUIManager is provided)
  • The manipulator handles pointer capture automatically
  • Drop detection uses bounds overlap, not raycast

Settings

Theme

Light

Contrast

Material

Dark

Dim

Material Dark

System

Sidebar(Light & Contrast only)

Light
Dark

Font Family

DM Sans

Wix

Inclusive Sans

AR One Sans

Direction

LTR
RTL