Simple Module For Creating Draggable GUI Elements

About
We used to have a property called Draggable which allowed us to enable dragging on GUI objects. Unfortunately this has been deprecated and is no longer available. That’s why I created an easy to use module to replace the old ‘Draggable’ property.

This module can be used on any GuiObject (Frames, ImageLabels, TextLabels, etc.) and supports both mouse and touch.

How To Use
First, you require the module and use DraggableObject.new(Object) function to create a new draggable object from an existing GuiObject.

local DraggableObject	= require(script.Parent.DraggableObject)
local FrameDrag 		= DraggableObject.new(script.Parent.Frame)

Then, to enable dragging all you have to do is just call the :Enable() method:

FrameDrag:Enable()

And now your frame should be draggable! To force disable dragging you can simply call:

FrameDrag:Disable()

Connecting To Events
This module also allows you to connect 3 different event handlers including DragStarted, Dragged, and DragEnded. Here’s an example:


FrameDrag.DragStarted = function()
	print("User has started dragging the frame")
end

FrameDrag.Dragged = function(ObjPosition)
	print("Frame is currently at", ObjPosition)
end

FrameDrag.DragEnded = function()
	print("User has stopped dragging the frame")
end

Hope this helps! If you guys have any comments or suggestions, please post them down below. :slight_smile:

~ Spynaz

276 Likes

This looks nice. However, is there any reason why you aren’t using signals in favor of callbacks? You can use BindableEvents and just return the .Event part of them so that it acts more like a traditional event.

15 Likes

You’re right, I could do that. There wasn’t really a particular reason why I did it this way, it was just the most simple way to do it. But it’d be better to use signals to keep it consistent with the ROBLOX APIs. I’ll update the script in a bit :slight_smile:

4 Likes

Oof, I’m actually not able to do that with BindableEvents unless I create 3 new BindableEvents every time a DraggableObject is created (which seems a bit inefficient). Otherwise it would simultaneously fire the same event for every single DraggableObject.

1 Like

That’s fair. It really depends upon how worried about efficiency you are. I just thought it might be easier for people who use this in the future.

4 Likes

Yea. I do have a custom signal/event module that I created years ago, but it could use some more improvements. I’ll look into improving it and possibly implementing it into this module.

1 Like

Thanks for this. I’ve used this twice now, very handy. I have one question: will running the :Enable() method twice in a row cause undefined behavior? Same question goes for :Disable() too.

3 Likes

Glad you found it useful! Running the :Enable() method more than once in a row isn’t a good idea, since it will not disconnect the connections that are already connected, therefore causing a memory leak. This is something I would have to fix.

As for the :Disabled() function, running it more than once in a row shouldn’t cause any issues.

1 Like

Thanks for the response, I really appreciate you coming back to answer my question after a whole year.

  1. If I were to accidentally run :Enable() twice, would running :Disable() once clear both of the connections?
  2. Would running :Disable() at the beginning of the :Enable() function fix the issue?

I don’t really know anything about metatables so please excuse me if the answer is right in front of me.

  1. No, it would not clear the connections.

  2. Yes you can fix it by putting self:Disable() in the very beginning of the :Enable() function. If you do that, then running :Enable() more than once in a row shouldn’t cause any problems.

2 Likes

I love your module as I am trying to do the same thing :slight_smile:

My suggestion is: Could we not instead use one function for Enabling and disabling? something like SetDraggable(false) or true instead of using two seprate functions? thanks for supporting this commuinty with your work

And if you were wondering about my code, here it is:

--[[
	Made by AREDEUS
	03/09/20 20:42:00
	
	Info: This is a library for making your UIs Draggable

	[Short wiki]
		BDMod.SetDraggable(GGuiObject, state)
		> GuiObject needs to be a Gui object.
		> State needs to be true/false or a string with "false" or "true"
		------
		Sets the Object as Draggable in the system if true.
		------
--]]

--// Refrences and Services
local BDMod = {}
local Player = game.Players.LocalPlayer
local UserInputService = game:GetService("UserInputService")
local Mouse = Player:GetMouse()

--Modular code begins here
local DraggableGuiObjects = {} -- a table of Objects that are allowed to be dragged
local Dragging = nil -- if an object is being dragged

--/ For setting which selection input it takes


--/ Adds elements that are Draggable to the DraggableGuiObjects
function BDMod.SetDraggable(GuiObject, State)
	if type(GuiObject) == "userdata" then 
		if GuiObject.IsA and GuiObject:IsA("GuiObject") then
			if State ~= nil then
				if State == true then
				-- for checking state if it is "true"/true or "false"/false
					DraggableGuiObjects[GuiObject] = true
				elseif State == false then
					DraggableGuiObjects[GuiObject] = false
				else
					error("SetDraggable for " .. GuiObject.Name .. " state needs to be a boolean")
				end
			else
				error("Draggable State for " .. GuiObject.Name .. " in SetDraggable was not passed")
			end
		else
			error("\"" .. tostring(GuiObject) .. "\" 's class is not supported or is not a GuiObject")
		end
	else
		error("GuiObject for SetDraggable can not be " .. type(GuiObject))
	end
end

--/ Updates Dragging when a selection update has been fired
UserInputService.InputBegan:Connect(function(Input)
	if Input.UserInputType == Enum.UserInputType.MouseButton1 or Enum.UserInputType.Touch then
		local FirstGuiObject = Player.PlayerGui:GetGuiObjectsAtPosition(Input.Position.X, Input.Position.Y)[1]
		if DraggableGuiObjects[FirstGuiObject] == true then
			Dragging = FirstGuiObject
		end
	end
end)

--/ sets the Dragging to nil when a Selection input has been fired
UserInputService.InputEnded:Connect(function(Input)
	if Input.UserInputType == Enum.UserInputType.MouseButton1 or Enum.UserInputType.Touch then
		Dragging = nil
	end
end)

Mouse.Move:Connect(function()
	if Dragging ~= nil then
		-- drag object
	end
end)

--Modular code ends here
return BDMod
4 Likes

Yea, that is possible. Thanks for the suggestion :slight_smile:

2 Likes

Nice i will use this in my Game with Save Positions etc so every Player can Positioning his UI like he want

2 Likes

What happens if a GuiObject gets dragged out of sight? (are there any features that can make it come back to a visible position, or does the module prevent the GuiObject from going out of sight).

2 Likes

Currently there’s nothing that prevents it from going out of sight. You could just use checks to make sure players don’t drag it out

1 Like

I am unable to use your module script. I have followed your instructions. I have tried with a client gui and server gui as well as different gui objects. I tried putting the module script in server storage and in the workspace as well. I put a system out print message under the enable function in module script and the enable function is working. While the enable function does run, it doesn’t appear that anything else is running. Either I am doing something wrong or a recent update made this module script out of date.

Edit: The reason that it was not working was because you have to put the module script inside of the gui object you wish to drag.

THANK U THANK U THANK U

u fixed my whole script ,i swear. I spend 2 days fixing and searching

1 Like

Made a quick snippet for a way to make custom events without BindableEvents and is purely vanilla lua based with some coroutine magic, though it is your choice to use it or not. Here.

I dont understand, why you aren’t just using

GuiObject.Draggable = true
GuiObject.Active = true

This option is still working fine…

Either way, that property is deprecated.

As stated on Roblox’s wiki page, “This event, as well as its counterparts, GuiObject.DragBegin and GuiObject/Stopped , have all been deprecated. None should be used for new work.” (source)

Because its deprecated, it could be removed from the API at any point with no warnings, which will cause issues in your game if you are still using it.

1 Like