Hello there, I have been trying to create a simple menu interface. I do not really know what I could improve code wise, if anything. I am trying to see what I could make in terms of improvements within the code base or if I am using any modules (like Maid) the wrong way.
Could you take a look and tell me if anything looks odd to you?
Overview of the codebase for this simple menu user interface:
This is the module script for the code:
local Menu = {
Events = {
OnClosed = nil,
Submitted = nil,
OnItemSelected = nil
},
Close = nil,
SubmitCoolDown = 3
}
local uiColors = {
red = Color3.new(0.666667, 0, 0),
black = Color3.new(0, 0, 0)
}
local private = {
userInterface = nil,
templateItem = nil,
items = {},
currentItemSelectedIdx = 1,
colorOnSelect = uiColors.red,
colorOnNotSelected = uiColors.black,
functions = {
submit = nil, clickedOn = nil, moveUp = nil, moveDown = nil, getCurrentItemSelected = nil
},
maid = nil
}
local UserInputService = game:GetService("UserInputService")
local Signal = require(game.ReplicatedStorage.Signal)
local Maid = require(game.ReplicatedStorage.Maid)
local bin = game.StarterGui.Bin
local localPlayer = game.Players.LocalPlayer
private.maid = Maid.new()
Menu.Events.OnClosed = Signal.new()
Menu.Events.Submitted = Signal.new()
Menu.Events.OnItemSelected = Signal.new()
private.maid:GiveTask(Menu.Events.OnClosed)
private.maid:GiveTask(Menu.Events.Submitted)
private.maid:GiveTask(Menu.Events.OnItemSelected)
Menu.Close = function()
private.maid:Destroy()
end
local submitting = false
private.functions.submit = function(item)
if not (submitting) then
submitting = true
Menu.Events.Submitted:Fire(item.Name)
wait(Menu.SubmitCoolDown)
submitting = false
end
end
private.functions.getCurrentItemSelected = function()
local getItem = private.items[private.currentItemSelectedIdx]
return getItem
end
local doubleClickDetectionIsOn = false
private.functions.clickedOn = function(item)
local getItemClickedIdx = item:GetAttribute("ItemIdx")
local itemIsNotAlreadySelected = (private.currentItemSelectedIdx ~= getItemClickedIdx)
if (itemIsNotAlreadySelected) then
item.BackgroundColor3 = private.colorOnSelect
private.items[private.currentItemSelectedIdx].BackgroundColor3 = private.colorOnNotSelected
private.currentItemSelectedIdx = getItemClickedIdx
Menu.Events.OnItemSelected:Fire(item.name.." has been selected.")
end
if not (doubleClickDetectionIsOn) then
doubleClickDetectionIsOn = true
wait(2)
doubleClickDetectionIsOn = false
else
private.functions.submit(item)
end
end
private.functions.moveDown = function()
local lastMenuItemIdx = #private.items
if (private.currentItemSelectedIdx ~= lastMenuItemIdx) then
local moveSelectedIdxDown = private.currentItemSelectedIdx + 1 -- This goes down in the gui.
local nextItem = private.items[moveSelectedIdxDown]
nextItem.BackgroundColor3 = private.colorOnSelect
local currentItemBeforeMovingDown = private.items[private.currentItemSelectedIdx]
currentItemBeforeMovingDown.BackgroundColor3 = private.colorOnNotSelected
private.currentItemSelectedIdx = moveSelectedIdxDown
local getItem = private.functions.getCurrentItemSelected()
Menu.Events.OnItemSelected:Fire(getItem.Name)
end
end
private.functions.moveUp = function()
local atBeginning = 1
if (private.currentItemSelectedIdx ~= atBeginning) then
local moveSelectedIdxUp = private.currentItemSelectedIdx - 1
local prevItem = private.items[moveSelectedIdxUp]
prevItem.BackgroundColor3 = private.colorOnSelect
local currentItemBeforeMovingUp = private.items[private.currentItemSelectedIdx]
currentItemBeforeMovingUp.BackgroundColor3 = private.colorOnNotSelected
private.currentItemSelectedIdx = moveSelectedIdxUp
local getItem = private.functions.getCurrentItemSelected()
Menu.Events.OnItemSelected:Fire(getItem.Name)
end
end
function Menu.insert(options)
if type(options) == "table" then
for i = 1, #options do
local newItem = private.templateItem:Clone()
newItem.Name = options[i]
newItem.Text = options[i]
newItem.Parent = private.userInterface.Frame.BodyFrame.ScrollingFrame
local function attachEventsToItem()
newItem.MouseButton1Click:Connect(function()
private.functions.clickedOn(newItem)
end)
end
attachEventsToItem()
local appendItemToEndOfItems = #private.items + 1
private.items[appendItemToEndOfItems] = newItem
newItem:SetAttribute("ItemIdx", #private.items)
private.templateItem.BackgroundColor3 = private.colorOnNotSelected -- Makes sure that the templateItem's background color when not selected is the
-- color that is designated for not selected every new insertion!
end
else
warn("[Menu] Wrong input format when using insert. Must be a table.")
end
end
function Menu.create()
-- Create default state/environment:
private.userInterface = bin.Menu:Clone()
private.userInterface.Parent = localPlayer.PlayerGui
-- Make the place holder item a template item for cloning purposes:
local placeHolderItem = private.userInterface.Frame.BodyFrame.ScrollingFrame.PlaceholderItem
private.templateItem = placeHolderItem:Clone()
placeHolderItem:Destroy()
private.maid:GiveTask(private.userInterface)
private.maid:GiveTask(private.templateItem)
-- Event listeners for the menu itself:
private.maid:GiveTask(UserInputService.InputBegan:Connect(function(input, _gameProcessed)
if (input.KeyCode == Enum.KeyCode.W) then
private.functions.moveUp()
elseif (input.KeyCode == Enum.KeyCode.S) then
private.functions.moveDown()
elseif (input.KeyCode == Enum.KeyCode.Backspace) then
Menu.Events.OnClosed:Fire()
elseif (input.KeyCode == Enum.KeyCode.Return) then
local getItem = private.functions.getCurrentItemSelected()
private.functions.submit(getItem)
end
end))
private.userInterface.Frame.ButtonFrame.FirstButton.MouseButton1Click:Connect(function()
Menu.Events.OnClosed:Fire()
Menu.Close()
end)
private.userInterface.Frame.ButtonFrame.SecondButton.MouseButton1Click:Connect(function()
local getItem = private.functions.getCurrentItemSelected()
private.functions.submit(getItem)
end)
-- Enable the menu:
private.userInterface.Enabled = true
end
return Menu
And here is the code being used in a local script simply:
repeat wait() until game.ContentProvider.RequestQueueSize == 0 -- Wait until the game has completely loaded in for the player.
local Maid = require(game.ReplicatedStorage.Maid)
local Menu = require(game.ReplicatedStorage.Menu)
Menu.create()
Menu.insert({"Menu Item Button One", "Panda Express"})
local maid = Maid.new()
maid:GiveTask(Menu.Events.OnItemSelected:Connect(function(itemName)
print(itemName.." was selected.")
end))
maid:GiveTask(Menu.Events.Submitted:Connect(function(itemName)
print(itemName.." was submitted.")
end))
maid:GiveTask(Menu.Events.OnClosed:Connect(function(itemName)
maid:Destroy()
end))