Hello Unreal Devs,
I’m sharing here a small tutorial about how to make a Drag And Drop feature in C++.
I miss some information when I was doing it so I’m sharing my achievements now
I hope you like it.
Creating Drag And Drop UI using C++
In this example, we take a look at how a player can drag and reposition a window on screen with the Left Mouse Button. At the end of this guide, you will have a UI widget that you can drag around inside the viewport and drop at a new location.
N O T E
In this tutorial, we will be creating a simple window, and this is intended for demonstration purposes only.
1 - Project Setup
Before we get started, there are a couple assets that we need to create as part of our project setup. In this example, the element in our HUD that we want to drag and drop is the MyWindow Widget.
N O T E
For this how-to guide, we are using the Blueprint Third Person template project.
- In the Content Browser, create two C++ classes called: MyWindow and HUDLayout, deriving UserWidget class.
- Right-click in the Content Browser, create a new C++ Class, deriving from DragDropOperation class, and call it DragWidget.
This specialized C++ Class enables us to pass through information as part of our drag and drop action.
- Open the DragWidget Class, and create a User Widget variable called WidgetReference, this will be used to hold the UMG Widget that we want to drag around on screen.
Create also a Vector 2D variable called DragOffset.
-
Add Slate and SlateCore Modules in the project .build file.
-
Compile and Save the DragWidget Class and .build file.
With our project setup done, next we will start working on the MyWindow Widget Class which is the piece of UI that we want to be draggable.
2 - Setting Up the MyWindow Widget
In this step, we determine when the Left Mouse Button is pressed and store the location where it was pressed in screen space. We also use the DetectDragIfPressed function to determine if the player is dragging with a specified key.
-
Create a Blueprint Class called WBP_MyWindow, deriving from MyWindow Class.
-
Add a Size Box and add a Border to the box. In the Details Panel for the Size Box, set the Width and Height Override to 500x50.
-
You can also stylize the Border yourself by setting the preview to Desired on Screen.
On the final, the widget will look like the image below:
2.1 - Scripting the On Mouse Button Down Function
This step enables us to determine if the player is dragging the Left Mouse Button.
- In the MyWindow Class, add the NativeOnMouseButtonDown, NativeOnDragDetected, and NativeOnDragLeave overrides.
Create a FVector2D variable called DragOffset in MyWindow Class.
This creates tabs for each of the overrides in the Event Graph.
I will explain MyCustomDragDetect function later.
There are several functions that you can override to call your own custom script. Here we will perform some checking when the Mouse Button is pressed and what happens when a widget drag is detected.
- To Detect Drag if Pressed it can be done in two ways.
The first method to detect drag is by importing UWidgetBlueprintLibrary.h, use DetectDragIfPressed method, and return the output.
FEventReply ReplyResult = UWidgetBlueprintLibrary::DetectDragIfPressed(InMouseEvent, this, EKeys::LeftMouseButton);
return ReplyResult.NativeReply;
The second method to detect our drag is by implementing the previous method directly on code without the need to import any library. This is the CustomDetectDrag.
if ( InMouseEvent.GetEffectingButton() == DragKey /*|| PointerEvent.IsTouchEvent()*/ )
{
FEventReply Reply;
Reply.NativeReply = FReply::Handled();
if ( WidgetDetectingDrag )
{
TSharedPtr<SWidget> SlateWidgetDetectingDrag = WidgetDetectingDrag->GetCachedWidget();
if ( SlateWidgetDetectingDrag.IsValid() )
{
Reply.NativeReply = Reply.NativeReply.DetectDrag(SlateWidgetDetectingDrag.ToSharedRef(), DragKey);
return Reply.NativeReply;
}
}
}
return FReply::Unhandled();
Both ways DetectDragIfPressed, setting our Drag Key as Left Mouse Button. This determines if the player is dragging the Left Mouse Button, which will start the On Drag Detected script
(Personally, I will use the second method because I want to import as fewer libraries as I can.)
In the end, our “NativeOnMouseButton” will look like the image below:
- Compile and Save the MyWindow Class.
2.2 - Scripting the On Drag Detect Function
In this step, we determine what happens when the player is, in fact, dragging the window around on the screen.
- Create a DragDropOperation Object from our class DragWidget.
This can be done in two ways also.
The first method is by importing UWidgetBlueprintLibrary.h, to have access to CreateDrapDropOperation function, and set the variables.
UDragWidget* DragWidget = Cast<UDragWidget>(UWidgetBlueprintLibrary::CreateDragDropOperation(UDragWidget::StaticClass()));
The second method is by creating ourselves the Object from our DragDropOperation Class.
UDragWidget* DragDropOperation = NewObject<class UDragWidget>();
- After, we need to set the Widget Reference and the DragOffset variables from the DragWidget Class.
The DragOffset variable is calculated with
DragOffset = InGeometry.AbsoluteToLocal(InMouseEvent.GetScreenSpacePosition());
When we create the drag widget, we set a reference to the existing MyWindow widget.
The Pivot variable allows you to define where the Drag Widget Visual appears while being dragged relative to the pointer performing the drag operation.
Above we indicated what Widget Class is our reference and what the drag visual should be and provided an offset in which to start dragging. We are using our own calculation to determine the position in which to start dragging when Mouse Button Down is pressed.
-
SetVisibility to HitTestInvisible. Don’t forget to set DefaultDragVisual with the existent widget, and the Pivot to EDragPivot::MouseDown.
-
Set OutOperation with DragDropOperation.
-
Import “Blueprint/SlateBlueprintLibrary.h” library, if needed.
-
Compile and Save the MyWindow Class.
Next, we need to do is to determine what happens when the player releases the Left Mouse Button and performs the OnDrop function.
3 - Setting up the On Drag Leave Function
- Add RemoveFromParent
4 - Setting up the On Drop Function
Here we set up our main HUD Widget Class and override what happens when we perform the OnDrop function.
-
Create a widget called WBP_HUDLayout deriving from the HUDLayout Class.
In the Details panel, add a Canvas Panel and set the Visibility to Visible.
We scripted the drag detection inside the MyWindow widget; however, when we release the Left Mouse Button we want to drop the bar in our HUDLayout Class. In order for our HUD to receive the hit detection, we need to make the panel Visible. -
From the Palette window, add the WBP_MyWindow widget to the Canvas.
-
Set Size to Content from WBP_MyWindow to true.
-
In the HUD_Layout Class, add an NativeOnDrop function override.
- From the NativeOnDrop function, use InOperation and add a Cast to DragWidget.
Verify if DragWidget is not null.
UDragWidget* DragWidgetResult = Cast<UDragWidget>(InOperation);
It’s always good to check if our Cast returns null or not.
- We will be subtracting the final and the initial position, and subtract the DragOffset.
This step will tell us the position in 2D space where we released the Left Mouse Button to drop the widget. We will use this minus the DragOffset to determine where we should place the widget when it is dropped.
const FVector2D DragWindowOffset = InGeometry.AbsoluteToLocal(InDragDropEvent.GetScreenSpacePosition());
const FVector2D DragWindowOffsetResult = DragWindowOffset - DragWidgetResult->DragOffset;
- Then, we get the Widget Reference to AddToViewport, SetVisibility, and SetPositionInViewport.
In the end, the NativeOnDrop function, will look like the image below:
- Compile and Save the HUDLayout Class.
Our HUD is set up to handle dropping our dragged widget, and it displays our window.
5 - Adding the HUD Widget to the Viewport
Lastly, we need to add the WBP_HUDLayout Widget Blueprint to the viewpoint in the Character Blueprint and enable the Mouse Cursor so we can see where we are dragging.
-
In the Content Browser under Content/ThirdPersonBP/Blueprints, open the ThirdPersonCharacter Blueprint.
-
Off the Event Begin Play node, add a Create Widget node and set the Class to WBP_HUDLayout. Create an Add to Viewport node connect it to SET Show Mouse Cursor. Add a Get Player Controller node and connect the Return Value to the SET Target.
- Compile, Save, and click the Play button to play with the drag and drop widget.
You can find the project available here on Github.
And a small video on Youtube.