Last active
June 29, 2024 16:31
-
-
Save DemmyDemon/b9158b3bbf52aadc36386f0ae5bfd566 to your computer and use it in GitHub Desktop.
Simple, ugly menu where you just "pick one" using the mouse.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
---@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