Draggable GUI Elements Module

About

This module allows you to make a GUI element draggable. There is already a similar module for this, but this one has more features. Everything will be explained later on. This module only works with ScreenGui. I do plan on making it possible for SurfaceGui.

How to use

We must first require the module. Let’s also make a variable for the GUI element that will be draggable as well.

local Draggable_UI = require(...)
local Frame = script.Parent.Frame

Then we create our draggable element with the Draggable_UI.new function and pass in the element that we want to be draggable, in this case, Frame. We are also able to pass in a second argument with true/false. This just converts position to offset if the argument is false, and scale if true, which is the default.

local Draggable_Frame = Draggable_UI.new(Frame)

And now your GUI element should be draggable. To disable the dragging you can simply use:

Draggable_Frame:Toggle()

We are also able to stop it from dragging from an overlapping GUI element.

Draggable_Frame:IgnoreDescendants()

Lastly if we want to add tween for our dragging we simply use SetTweenInfo function.

Draggable_Frame:SetTweenInfo(TweenInfo.new(0.2))

Example

local DraggableUI = require(script.Parent.DraggableUI)

local Frame = script.Parent.WithTween
local DraggableFrame = DraggableUI.new(Frame)
DraggableFrame:SetTweenInfo(TweenInfo.new(0.5, Enum.EasingStyle.Quad))
DraggableFrame:Ignore({Frame.UnDraggable})

local Frame = script.Parent.WithoutTween
DraggableUI.new(Frame):Ignore({Frame.UnDraggable})

Events

We are able to listen 3 event handlers which uses BindableEvent, which might cause a memory leak if not handled properly, I do plan on using something similar to Signal module. Or I could also just use if statement if Started has a value and a type of function. However, if ever there is a time where you have to use Started twice, the function will only work for one of the functions.

-- This fires the event when left click or touched started/began.
Draggable_Frame.Started:Connect(function()
	print("Dragging started at: " .. os.date("%X"))
end)

-- Upon left click or touched released/ended this event get's fired and
-- passes in the mouse position which is a type of Vector2.
Draggable_Frame.Released:Connect(function(Position: Vector2)
	print(Position)
end)

-- Upon mouse or touched movement this event get's fired and passes in
-- the mouse position which is a type of Vector2.
Draggable_Frame.Moved:Connect(function(Position: Vector2)
	print(Position)
end)

The event that would be of use often would probably be the Released. I can see that this would be of use for when it comes to drag and drop.

Features

As stated before the second parameter just uses Scale if true and Offset if false.

Draggable_UI.new(UI: Instance, byScale: true?)

If Value is empty, it toggles from Enabled to Disabled. If the Value’s value is false, then we force disable the dragging and true to force enable.

Draggable_UI:Toggle(Value: boolean?)

This function just allow you to add animation when dragging.

Draggable_UI:SetTweenInfo(tweenInfo: TweenInfo)

There are three functions that stop it from dragging from an overlapping GUI element. The second and third just use the first function and passes in second argument a true value. The first function’s argument is the list of overlapping UI, while the second argument just place the loop inside a protected function.

Draggable_UI:Ignore(list: { Instance }, instanceCheck: false?)
Draggable_UI:IgnoreChildren()
Draggable_UI:IgnoreDescendants()

And finally, when we are done using the draggable element, we simply use the Destroy function.

Draggable_UI:Destroy()
31 Likes

Could you possibly add screenshots and videos to the post?

1 Like

Alright, I just added the video and the code under Example.

I might review the code to see if it’s applicable to the BaseAdmin mobile window, since it uses similar functionality to this.

Great module, this is definitely needed to replace the old Draggable property.

1 Like

I love the module, and the fact to how it replaces the deprecated draggable property! Neat module, and very customisable.

One neat feature I’d love to request is perhaps: a function which enables whether the gui can go off screen or not. Meaning, automatically detect a player’s screen size, and prevent the UI being able to be dragged past those bounds to prevent UIs disappearing / being half hidden, etc.

2 Likes

Hey, I loved the idea and just implemented it in the code. It does still need more testing, because I didn’t really test it that much.

Draggable_UI:LimitScreenBoundingBox:(byParent: false?, value: boolean?)

This method restricts the dragging capability to the screen’s bounding box, which is the default. Passing in the first argument as true limits it to the parent’s bounding box. The second argument just allows you to force enable or disable the method.

Example

local DraggableUI = require(script.Parent.DraggableUI)

local InsideDraggableFrame = DraggableUI.new(script.Parent.Inside.Frame)
InsideDraggableFrame:LimitScreenBoundingBox(true)

local DraggableFrame = DraggableUI.new(script.Parent.Frame)
DraggableFrame:LimitScreenBoundingBox()

Update

  • The event handler now uses the signal module.
  • Other methods are not visible when using the module.
  • Other methods and indexes are not visible when creating a new draggable.
  • New method added LimitScreenBoundingBox
6 Likes

I love this! Thank you so much, and I’m glad the idea interested you!

This will help me, so much.

1 Like

Sorry to be a pest! But currently I’m getting some unexpected behaviour, it works to an extent:

My screen size is: 2560 x 1440.

  • The bounding box isn’t set to a parent frame (I’ve also attempted this to try fix it)
  • I’ve tried going in a live server, same results
  • I’ve also tried to fullscreen in studio as shown in the video, etc

Sorry to be a pain!

1 Like

Hmm, try checking your camera’s viewport frame to see if it has the same screen size.

Odd results (studio):
image

Live game server:
image

1 Like

Oh, I found the issue; the issue was that the code was not considering the anchor point, which offsets the pivot of the GUI element based on the value of the anchor point. I just updated the module; let me know if there is still a bug, and I will try to fix it as soon as possible.

1 Like

No worries! Appreciate it. I’ll keep you updated, gonna try now.

EDIT: Seems to work flawlessly now.

2 Likes

This is intended. That is the server’s viewport size, not the clients.

Issue was resolved, and I know.

Thanks! This seems extremely useful and I will use it.

1 Like

I’m using this module but it’s not working because whenever I move the mouse it fires Released instead of Moved, and for some reason it can’t find an object even though it’s clearly there

for i, v in pairs(slots) do
		local dragObject = DraggableUIModule.new(v.TextButton)
		v.TextButton.MouseButton1Click:Connect(function()
			inventorySlotSelected(i, v, dragObject)
		end)
		v.TextButton.TouchTap:Connect(function()
			inventorySlotSelected(i, v, dragObject)
		end)
		
		dragObject.Released:Connect(function(Position)
			if isPositionInFrame(Position, INVENTORY_UI:FindFirstChild("Slots")) then
				print("DROP!")
			else
				print("Dont Drop")
			end
			
			lastClickTick = tick()
			v.TextButton.Position = UDim2.new(0,0,0,0)
			print("Move Back")
		end)
	end

So when it starts dragging the module sets the parent of the object to nil, so it thinks that TextButton doesn’t exist.

Most likely because it isn’t supported? Aren’t those mobile controls? Be more descriptive if you can, the OP is very good at adding & listening to suggestive features.

Mistake on my part, I messed something up but it’s fixed

That is quite odd behaviour, I must admit. I’m glad you resolved your issue.