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.

Extend DragAndDropManipulator and implement the abstract methods:
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;
}
}// 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);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");
}public DragAndDropManipulator(
LunaUIManager uiElementManager,
VisualElement dragArea,
int key,
List<VisualElement> dropSlots,
Func<List<VisualElement>> getDropSlots = null,
bool worldSpace = false
)| Parameter | Type | Description |
|---|---|---|
| uiElementManager | LunaUIManager | Reference to LunaUIManager for audio handling (can be null) |
| dragArea | VisualElement | The area where the drag clone will be added |
| key | int | Unique identifier for the dragged item |
| dropSlots | List<VisualElement> | Static list of valid drop targets |
| getDropSlots | Func<List<VisualElement>> | Optional function to get dynamic drop slots |
| worldSpace | bool | Set to true for World Space UI |
Called when drag starts. Must return the visual element to display during drag.
public abstract VisualElement CreateDragElement();Called when drag ends. Clean up any resources used by the drag element.
public abstract void DisposeDragElement();| Event | Parameters | Description |
|---|---|---|
| OnStart | Vector3 position | Fired when drag begins |
| OnMove | Vector3 position | Fired while dragging |
| OnDrop | int key, int slotIndex, VisualElement slot | Fired when dropped on valid slot |
| OnCancel | - | Fired when dropped outside valid slots |
Use SetDropSlotClasses to apply USS classes to drop slots during drag:
// Set classes to apply during drag
manipulator.SetDropSlotClasses(new List<string> { "drop-highlight", "glow" });/* USS styles */
.drop-highlight {
border-color: var(--color-primary);
border-width: 2px;
}
.glow {
background-color: rgba(255, 255, 255, 0.1);
}For drop slots that change at runtime, use the getDropSlots parameter:
var manipulator = new ItemDragManipulator(
uiManager,
dragArea,
key: 0,
dropSlots: null, // No static slots
getDropSlots: () => GetCurrentInventorySlots() // Dynamic function
);For World Space UI Toolkit panels, set worldSpace to true:
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.
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'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.
When using drag and drop with elements that have tooltips, you may want to disable tooltips during drag operations. Use AddTooltipReference to register tooltips that should be automatically disabled when dragging starts and re-enabled when dragging ends.
// Register tooltip to disable during drag
manipulator.AddTooltipReference(tooltipController.Tooltip);This prevents tooltips from interfering with drag operations and provides a cleaner user experience.
Settings
Theme
Light
Contrast
Material
Dark
Dim
Material Dark
System
Sidebar(Light & Contrast only)
Font Family
DM Sans
Wix
Inclusive Sans
AR One Sans
Direction