2025-03-18 18:46:09 +11:00
|
|
|
--[[
|
|
|
|
|
https://github.com/overextended/ox_lib
|
|
|
|
|
|
|
|
|
|
This file is licensed under LGPL-3.0 or higher <https://www.gnu.org/licenses/lgpl-3.0.en.html>
|
|
|
|
|
|
2025-03-29 21:44:03 +11:00
|
|
|
Copyright © 2025 Linden <https://github.com/thelindat>
|
2025-03-18 18:46:09 +11:00
|
|
|
]]
|
|
|
|
|
|
2022-09-25 14:26:04 +02:00
|
|
|
---@type { [string]: MenuProps }
|
2022-07-31 12:54:40 +02:00
|
|
|
local registeredMenus = {}
|
2022-09-25 14:26:04 +02:00
|
|
|
---@type MenuProps | nil
|
2022-11-21 16:45:46 +11:00
|
|
|
local openMenu
|
2022-07-31 12:54:40 +02:00
|
|
|
|
2022-10-29 12:23:15 +02:00
|
|
|
---@alias MenuPosition 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
|
|
|
|
|
---@alias MenuChangeFunction fun(selected: number, scrollIndex?: number, args?: any, checked?: boolean)
|
2022-09-18 09:06:32 +10:00
|
|
|
|
|
|
|
|
---@class MenuOptions
|
|
|
|
|
---@field label string
|
2023-07-21 04:54:29 -07:00
|
|
|
---@field progress? number
|
|
|
|
|
---@field colorScheme? string
|
2023-03-10 19:38:34 +00:00
|
|
|
---@field icon? string | {[1]: IconProp, [2]: string};
|
2023-07-21 04:54:29 -07:00
|
|
|
---@field iconColor? string
|
2022-11-21 16:45:46 +11:00
|
|
|
---@field values? table<string | { label: string, description: string }>
|
2023-07-21 04:54:29 -07:00
|
|
|
---@field checked? boolean
|
2022-09-18 09:06:32 +10:00
|
|
|
---@field description? string
|
|
|
|
|
---@field defaultIndex? number
|
2022-10-29 15:52:59 +02:00
|
|
|
---@field args? {[any]: any}
|
2022-09-30 09:57:02 +02:00
|
|
|
---@field close? boolean
|
2022-09-18 09:06:32 +10:00
|
|
|
|
|
|
|
|
---@class MenuProps
|
|
|
|
|
---@field id string
|
|
|
|
|
---@field title string
|
|
|
|
|
---@field options MenuOptions[]
|
|
|
|
|
---@field position? MenuPosition
|
|
|
|
|
---@field disableInput? boolean
|
2022-09-25 14:26:04 +02:00
|
|
|
---@field canClose? boolean
|
|
|
|
|
---@field onClose? fun(keyPressed?: 'Escape' | 'Backspace')
|
2022-09-18 09:06:32 +10:00
|
|
|
---@field onSelected? MenuChangeFunction
|
|
|
|
|
---@field onSideScroll? MenuChangeFunction
|
2022-10-29 12:23:15 +02:00
|
|
|
---@field onCheck? MenuChangeFunction
|
2022-10-08 01:57:51 +02:00
|
|
|
---@field cb? MenuChangeFunction
|
2022-09-18 09:06:32 +10:00
|
|
|
|
|
|
|
|
---@param data MenuProps
|
|
|
|
|
---@param cb? MenuChangeFunction
|
2022-07-31 13:03:09 +02:00
|
|
|
function lib.registerMenu(data, cb)
|
2022-09-18 09:06:32 +10:00
|
|
|
if not data.id then error('No menu id was provided.') end
|
|
|
|
|
if not data.title then error('No menu title was provided.') end
|
|
|
|
|
if not data.options then error('No menu options were provided.') end
|
|
|
|
|
data.cb = cb
|
|
|
|
|
registeredMenus[data.id] = data
|
2022-07-31 12:54:40 +02:00
|
|
|
end
|
|
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
---@param id string
|
|
|
|
|
---@param startIndex? number
|
2022-09-17 11:03:10 -07:00
|
|
|
function lib.showMenu(id, startIndex)
|
2022-09-18 09:06:32 +10:00
|
|
|
local menu = registeredMenus[id]
|
|
|
|
|
if not menu then
|
|
|
|
|
error(('No menu with id %s was found'):format(id))
|
|
|
|
|
end
|
2025-01-19 16:06:25 +01:00
|
|
|
|
|
|
|
|
if table.type(menu.options) == 'empty' then
|
|
|
|
|
error(('Can\'t open empty menu with id %s'):format(id))
|
|
|
|
|
end
|
|
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
if not openMenu then
|
2022-11-25 18:05:55 -08:00
|
|
|
local control = cache.game == 'fivem' and 140 or 0xE30CD707
|
2023-11-03 07:34:10 -07:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
CreateThread(function()
|
|
|
|
|
while openMenu do
|
2022-09-25 14:26:04 +02:00
|
|
|
if openMenu.disableInput == nil or openMenu.disableInput then
|
2022-09-18 09:06:32 +10:00
|
|
|
DisablePlayerFiring(cache.playerId, true)
|
2023-11-03 07:34:10 -07:00
|
|
|
if cache.game == 'fivem' then
|
|
|
|
|
HudWeaponWheelIgnoreSelection() -- Not a REDM native
|
|
|
|
|
end
|
2022-11-25 18:05:55 -08:00
|
|
|
DisableControlAction(0, control, true)
|
2022-09-18 09:06:32 +10:00
|
|
|
end
|
|
|
|
|
Wait(0)
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
openMenu = menu
|
2023-03-15 21:04:39 +01:00
|
|
|
lib.setNuiFocus(not menu.disableInput, true)
|
2022-09-18 09:06:32 +10:00
|
|
|
|
|
|
|
|
SendNUIMessage({
|
|
|
|
|
action = 'setMenu',
|
|
|
|
|
data = {
|
|
|
|
|
position = menu.position,
|
|
|
|
|
canClose = menu.canClose,
|
|
|
|
|
title = menu.title,
|
|
|
|
|
items = menu.options,
|
|
|
|
|
startItemIndex = startIndex and startIndex - 1 or 0
|
|
|
|
|
}
|
|
|
|
|
})
|
2022-07-31 12:54:40 +02:00
|
|
|
end
|
2022-09-18 09:06:32 +10:00
|
|
|
---@param onExit boolean?
|
2022-08-04 13:25:47 +02:00
|
|
|
function lib.hideMenu(onExit)
|
2022-09-18 09:06:32 +10:00
|
|
|
local menu = openMenu
|
2023-03-01 07:25:28 +11:00
|
|
|
openMenu = nil
|
2022-11-21 16:45:46 +11:00
|
|
|
|
|
|
|
|
if not menu then return end
|
|
|
|
|
|
2023-03-01 07:25:28 +11:00
|
|
|
lib.resetNuiFocus()
|
2022-08-23 19:12:50 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
if onExit and menu.onClose then
|
|
|
|
|
menu.onClose()
|
|
|
|
|
end
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
SendNUIMessage({
|
|
|
|
|
action = 'closeMenu'
|
|
|
|
|
})
|
2022-08-04 13:25:47 +02:00
|
|
|
end
|
|
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
---@param id string
|
|
|
|
|
---@param options MenuOptions | MenuOptions[]
|
|
|
|
|
---@param index? number
|
2022-08-03 12:07:01 +02:00
|
|
|
function lib.setMenuOptions(id, options, index)
|
2022-09-18 09:06:32 +10:00
|
|
|
if index then
|
|
|
|
|
registeredMenus[id].options[index] = options
|
|
|
|
|
else
|
|
|
|
|
if not options[1] then error('Invalid override format used, expected table of options.') end
|
|
|
|
|
registeredMenus[id].options = options
|
|
|
|
|
end
|
2022-08-03 11:42:12 +02:00
|
|
|
end
|
|
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
---@return string?
|
2022-11-21 16:45:46 +11:00
|
|
|
function lib.getOpenMenu() return openMenu and openMenu.id end
|
2022-07-31 12:54:40 +02:00
|
|
|
|
|
|
|
|
RegisterNUICallback('confirmSelected', function(data, cb)
|
2022-09-18 09:06:32 +10:00
|
|
|
cb(1)
|
|
|
|
|
data[1] += 1 -- selected
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
if data[2] then
|
|
|
|
|
data[2] += 1 -- scrollIndex
|
|
|
|
|
end
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
local menu = openMenu
|
2022-09-17 11:03:10 -07:00
|
|
|
|
2022-11-21 16:45:46 +11:00
|
|
|
if not menu then return end
|
|
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
if menu.options[data[1]].close ~= false then
|
2023-03-01 07:25:28 +11:00
|
|
|
lib.resetNuiFocus()
|
2022-09-18 09:06:32 +10:00
|
|
|
openMenu = nil
|
|
|
|
|
end
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
if menu.cb then
|
2022-10-29 12:23:15 +02:00
|
|
|
menu.cb(data[1], data[2], menu.options[data[1]].args, data[3])
|
2022-09-18 09:06:32 +10:00
|
|
|
end
|
2022-08-04 13:12:58 +02:00
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
RegisterNUICallback('changeIndex', function(data, cb)
|
2022-09-18 09:06:32 +10:00
|
|
|
cb(1)
|
2022-11-21 16:45:46 +11:00
|
|
|
if not openMenu or not openMenu.onSideScroll then return end
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
data[1] += 1 -- selected
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
if data[2] then
|
|
|
|
|
data[2] += 1 -- scrollIndex
|
|
|
|
|
end
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-10-29 15:52:59 +02:00
|
|
|
openMenu.onSideScroll(data[1], data[2], openMenu.options[data[1]].args)
|
2022-07-31 12:54:40 +02:00
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
RegisterNUICallback('changeSelected', function(data, cb)
|
2022-09-18 09:06:32 +10:00
|
|
|
cb(1)
|
2022-11-21 16:45:46 +11:00
|
|
|
if not openMenu or not openMenu.onSelected then return end
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
data[1] += 1 -- selected
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-10-29 15:52:59 +02:00
|
|
|
|
|
|
|
|
local args = openMenu.options[data[1]].args
|
|
|
|
|
|
|
|
|
|
if args and type(args) ~= 'table' then
|
|
|
|
|
return error("Menu args must be passed as a table")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not args then args = {} end
|
|
|
|
|
if data[2] then args[data[3]] = true end
|
|
|
|
|
|
|
|
|
|
if data[2] and not args.isCheck then
|
2022-09-18 09:06:32 +10:00
|
|
|
data[2] += 1 -- scrollIndex
|
|
|
|
|
end
|
2022-08-05 00:58:19 +10:00
|
|
|
|
2022-10-29 15:52:59 +02:00
|
|
|
openMenu.onSelected(data[1], data[2], args)
|
2022-10-29 12:23:15 +02:00
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
RegisterNUICallback('changeChecked', function(data, cb)
|
|
|
|
|
cb(1)
|
2022-11-21 16:45:46 +11:00
|
|
|
if not openMenu or not openMenu.onCheck then return end
|
2022-10-29 12:23:15 +02:00
|
|
|
|
|
|
|
|
data[1] += 1 -- selected
|
|
|
|
|
|
2022-10-29 15:52:59 +02:00
|
|
|
openMenu.onCheck(data[1], data[2], openMenu.options[data[1]].args)
|
2022-07-31 12:54:40 +02:00
|
|
|
end)
|
|
|
|
|
|
2022-08-01 11:13:26 +02:00
|
|
|
RegisterNUICallback('closeMenu', function(data, cb)
|
2022-09-18 09:06:32 +10:00
|
|
|
cb(1)
|
2023-03-01 07:25:28 +11:00
|
|
|
lib.resetNuiFocus()
|
2022-08-08 17:17:29 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
local menu = openMenu
|
2022-11-21 16:45:46 +11:00
|
|
|
|
|
|
|
|
if not menu then return end
|
|
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
openMenu = nil
|
2022-08-23 19:12:50 +10:00
|
|
|
|
2022-09-18 09:06:32 +10:00
|
|
|
if menu.onClose then
|
2022-09-25 14:26:04 +02:00
|
|
|
menu.onClose(data --[[@as 'Escape' | 'Backspace' | nil]])
|
2022-09-18 09:06:32 +10:00
|
|
|
end
|
2022-09-25 14:26:04 +02:00
|
|
|
end)
|