Skip to content

Instantly share code, notes, and snippets.

@DemmyDemon
Last active June 29, 2024 16:31
Show Gist options
  • Save DemmyDemon/b9158b3bbf52aadc36386f0ae5bfd566 to your computer and use it in GitHub Desktop.
Save DemmyDemon/b9158b3bbf52aadc36386f0ae5bfd566 to your computer and use it in GitHub Desktop.
Simple, ugly menu where you just "pick one" using the mouse.
---@class PickOne
---@field header string The text above the buttons
---@field begin vector2 Top left of menu when it opens
---@field fontSize number Size of the menu font
---@field widest number The width of the widest element in the menu
---@field verticalSpace number The vertical spacing between menu items
---@field verticalOffset number The vertical offset for the cursor location
---@field itemHeight number The number of each item
---@field textOffset number The offset between the button location and where text is drawn, vertically.
---@field buttons table A table of the buttons to draw
---@field colors table A description of the idle and active button colors.
---@field disableControls table A list of controls to be disabled when the menu is open
PickOne = {}
---Create a brand new, empty PickOne menu
---@return PickOne
function PickOne:new(header)
header = header or "Pick One"
local instance = {
header = header,
begin = vector2(0.2, 0.3),
fontSize = 0.3,
widest = 0.0,
verticalSpace = 0.04,
verticalOffset = -0.015,
itemHeight = 0.029,
textOffset = 0.003,
buttons = {},
colors = {
header = {255, 255, 255, 200},
idle = {
button = {255, 255, 255, 100},
text = {0,0,0,255},
},
active = {
button = {255, 255, 255, 200},
text = {255, 255, 0, 255},
},
disabled = {
button = {255, 245, 245, 100},
text = {75, 0, 0, 255},
},
},
disableControls = {1, 2, 4, 6, 18, 24, 25, 68, 69, 70, 91, 92, 106, 114, 122, 135, 142, 144, 176, 177, 222, 223, 229, 237, 238, 257, 329, 330, 331, 346, 347 }
}
setmetatable(instance, self)
self.__index = self
BeginTextCommandGetWidth('STRING')
instance:_headerMeta()
local width = EndTextCommandGetWidth(false)
instance.widest = width
return instance
end
function PickOne:_disableMouseControls()
-- DisableAllControlActions(0)
for _, control in ipairs(self.disableControls) do
DisableControlAction(0, control, true)
end
end
function PickOne:_buttonTextMeta(text, highlighted, disabled)
SetTextCentre(false)
SetTextFont(0)
SetTextScale(self.fontSize, self.fontSize)
AddTextComponentSubstringPlayerName(text)
if highlighted then
SetTextOutline()
SetTextColour(table.unpack(self.colors.active.text))
elseif disabled then
SetTextColour(table.unpack(self.colors.disabled.text))
else
SetTextColour(table.unpack(self.colors.idle.text))
end
end
function PickOne:_drawButton(aspectRatio, cursor, button, mouseX, mouseY)
local left = cursor.x
local right = cursor.x + self.widest
local top = cursor.y + -self.itemHeight/2
local bottom = cursor.y + self.itemHeight/2
local hover = false
if not button.disabled and mouseX < right and mouseX > left then
if mouseY < bottom and mouseY > top then
hover = true
end
end
local r,g,b,a
if hover then
r,g,b,a = table.unpack(self.colors.active.button)
elseif button.disabled then
r,g,b,a = table.unpack(self.colors.disabled.button)
else
r,g,b,a = table.unpack(self.colors.idle.button)
end
DrawRect(
cursor.x + (self.widest/2), cursor.y + self.itemHeight/2,
self.widest, self.itemHeight,
r, g, b, a
)
BeginTextCommandDisplayText('STRING')
self:_buttonTextMeta(button.label, hover, button.disabled)
EndTextCommandDisplayText(cursor.x, cursor.y + self.textOffset)
return hover
end
function PickOne:_headerMeta()
SetTextCentre(false)
SetTextFont(7)
SetTextScale(self.fontSize * 2, self.fontSize * 2)
AddTextComponentSubstringPlayerName(self.header)
SetTextOutline()
SetTextColour(table.unpack(self.colors.header))
end
function PickOne:_drawHeader()
BeginTextCommandDisplayText('STRING')
self:_headerMeta()
EndTextCommandDisplayText(self.begin.x, self.begin.y-0.04)
end
function PickOne:_draw(mouseX, mouseY)
local cursor = vector2(self.begin.x, self.begin.y)
local hoveredButton = nil
local aspectRatio = GetAspectRatio(true)
self:_drawHeader()
for _, button in pairs(self.buttons) do
local hovered = self:_drawButton(aspectRatio, cursor, button, mouseX, mouseY)
if hovered then
hoveredButton = button
end
cursor = vector2(cursor.x, self.verticalSpace + cursor.y)
end
return hoveredButton
end
---Add a button to the menu
---@param label string The button label
---@param value? any The value to be returned when the button is clicked.
function PickOne:addButton(label, value, disabled)
local button = {
label = label or '(Unknown)',
value = value, -- nil and false are allowed!
disabled = disabled,
}
table.insert(self.buttons, button)
BeginTextCommandGetWidth('STRING')
self:_buttonTextMeta(button.label, true)
local width = EndTextCommandGetWidth(false)
if width > self.widest then
self.widest = width
end
return #self.buttons
end
---Add multiple buttons at once
---@param buttons table Buttons to add, like { {"Label", value}, {"Otherlabel", otherValue} }
function PickOne:addButtons(buttons)
for i, button in ipairs(buttons) do
self:addButton(button[1] or tostring(i), button[2] or i)
end
end
---Open the menu to pick one of the values
---@return any value Returns whatever value was given when the button was added.
function PickOne:pick(closeEvent)
closeEvent = closeEvent or 'closePickOne'
Citizen.Wait(0)
local active = true
local evt = AddEventHandler(GetCurrentResourceName().. ':' ..closeEvent, function()
active = false
end)
while active do
SetMouseCursorActiveThisFrame()
local cursorX = GetDisabledControlNormal(0, 239)
local cursorY = GetDisabledControlNormal(0, 240)
cursorY = cursorY + self.verticalOffset
self:_disableMouseControls()
local hoveredButton = self:_draw(cursorX,cursorY)
if IsDisabledControlJustReleased(0, 24) then
Citizen.Wait(0) -- So the "JustReleased" was in the *previous* frame when we exit the menu.
if hoveredButton then
return hoveredButton.value
else
return
end
elseif IsDisabledControlJustReleased(0, 25) then
active = false
end
Citizen.Wait(0)
end
RemoveEventHandler(evt)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment