mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-17 06:56:03 +01:00
Some people accidentally, mistakenly, and without any malicious intent forget to attribute code from this resource to its source. This change will hopefully assist people, so that they do not make that mistake again.
354 lines
8.2 KiB
Lua
354 lines
8.2 KiB
Lua
--[[
|
|
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>
|
|
|
|
Copyright (c) 2025 Linden <https://github.com/thelindat/fivem>
|
|
]]
|
|
|
|
---@class RadialItem
|
|
---@field icon string | {[1]: IconProp, [2]: string};
|
|
---@field label string
|
|
---@field menu? string
|
|
---@field onSelect? fun(currentMenu: string | nil, itemIndex: number) | string
|
|
---@field [string] any
|
|
---@field keepOpen? boolean
|
|
---@field iconWidth? number
|
|
---@field iconHeight? number
|
|
|
|
---@class RadialMenuItem: RadialItem
|
|
---@field id string
|
|
|
|
---@class RadialMenuProps
|
|
---@field id string
|
|
---@field items RadialItem[]
|
|
---@field [string] any
|
|
|
|
local isOpen = false
|
|
|
|
---@type table<string, RadialMenuProps>
|
|
local menus = {}
|
|
|
|
---@type RadialMenuItem[]
|
|
local menuItems = {}
|
|
|
|
---@type table<{id: string, option: string}>
|
|
local menuHistory = {}
|
|
|
|
---@type RadialMenuProps?
|
|
local currentRadial = nil
|
|
|
|
---Open a the global radial menu or a registered radial submenu with the given id.
|
|
---@param id string?
|
|
---@param option number?
|
|
local function showRadial(id, option)
|
|
local radial = id and menus[id]
|
|
|
|
if id and not radial then
|
|
return error('No radial menu with such id found.')
|
|
end
|
|
|
|
currentRadial = radial
|
|
|
|
-- Hide current menu and allow for transition
|
|
SendNUIMessage({
|
|
action = 'openRadialMenu',
|
|
data = false
|
|
})
|
|
|
|
Wait(100)
|
|
|
|
-- If menu was closed during transition, don't open the submenu
|
|
if not isOpen then return end
|
|
|
|
SendNUIMessage({
|
|
action = 'openRadialMenu',
|
|
data = {
|
|
items = radial and radial.items or menuItems,
|
|
sub = radial and true or nil,
|
|
option = option
|
|
}
|
|
})
|
|
end
|
|
|
|
---Refresh the current menu items or return from a submenu to its parent.
|
|
local function refreshRadial(menuId)
|
|
if not isOpen then return end
|
|
|
|
if currentRadial and menuId then
|
|
if menuId == currentRadial.id then
|
|
return showRadial(menuId)
|
|
else
|
|
for i = 1, #menuHistory do
|
|
local subMenu = menuHistory[i]
|
|
|
|
if subMenu.id == menuId then
|
|
local parent = menus[subMenu.id]
|
|
|
|
for j = 1, #parent.items do
|
|
-- If we still have a path to the current submenu, refresh instead of returning
|
|
if parent.items[j].menu == currentRadial.id then
|
|
return -- showRadial(currentRadial.id)
|
|
end
|
|
end
|
|
|
|
currentRadial = parent
|
|
|
|
for j = #menuHistory, i, -1 do
|
|
menuHistory[j] = nil
|
|
end
|
|
|
|
return showRadial(currentRadial.id)
|
|
end
|
|
end
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
table.wipe(menuHistory)
|
|
showRadial()
|
|
end
|
|
|
|
---Registers a radial sub menu with predefined options.
|
|
---@param radial RadialMenuProps
|
|
function lib.registerRadial(radial)
|
|
menus[radial.id] = radial
|
|
radial.resource = GetInvokingResource()
|
|
|
|
if currentRadial then
|
|
refreshRadial(radial.id)
|
|
end
|
|
end
|
|
|
|
function lib.getCurrentRadialId()
|
|
return currentRadial and currentRadial.id
|
|
end
|
|
|
|
function lib.hideRadial()
|
|
if not isOpen then return end
|
|
|
|
SendNUIMessage({
|
|
action = 'openRadialMenu',
|
|
data = false
|
|
})
|
|
|
|
lib.resetNuiFocus()
|
|
table.wipe(menuHistory)
|
|
|
|
isOpen = false
|
|
currentRadial = nil
|
|
end
|
|
|
|
---Registers an item or array of items in the global radial menu.
|
|
---@param items RadialMenuItem | RadialMenuItem[]
|
|
function lib.addRadialItem(items)
|
|
local menuSize = #menuItems
|
|
local invokingResource = GetInvokingResource()
|
|
|
|
items = table.type(items) == 'array' and items or { items }
|
|
|
|
for i = 1, #items do
|
|
local item = items[i]
|
|
item.resource = invokingResource
|
|
|
|
if menuSize == 0 then
|
|
menuSize += 1
|
|
menuItems[menuSize] = item
|
|
else
|
|
for j = 1, menuSize do
|
|
if menuItems[j].id == item.id then
|
|
menuItems[j] = item
|
|
break
|
|
end
|
|
|
|
if j == menuSize then
|
|
menuSize += 1
|
|
menuItems[menuSize] = item
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if isOpen and not currentRadial then
|
|
refreshRadial()
|
|
end
|
|
end
|
|
|
|
---Removes an item from the global radial menu with the given id.
|
|
---@param id string
|
|
function lib.removeRadialItem(id)
|
|
local menuItem
|
|
|
|
for i = 1, #menuItems do
|
|
menuItem = menuItems[i]
|
|
|
|
if menuItem.id == id then
|
|
table.remove(menuItems, i)
|
|
break
|
|
end
|
|
end
|
|
|
|
if not isOpen then return end
|
|
|
|
refreshRadial(id)
|
|
end
|
|
|
|
---Removes all items from the global radial menu.
|
|
function lib.clearRadialItems()
|
|
table.wipe(menuItems)
|
|
|
|
if isOpen then
|
|
refreshRadial()
|
|
end
|
|
end
|
|
|
|
RegisterNUICallback('radialClick', function(index, cb)
|
|
cb(1)
|
|
|
|
local itemIndex = index + 1
|
|
local item, currentMenu
|
|
|
|
if currentRadial then
|
|
item = currentRadial.items[itemIndex]
|
|
currentMenu = currentRadial.id
|
|
else
|
|
item = menuItems[itemIndex]
|
|
end
|
|
|
|
local menuResource = currentRadial and currentRadial.resource or item.resource
|
|
|
|
if item.menu then
|
|
menuHistory[#menuHistory + 1] = { id = currentRadial and currentRadial.id, option = item.menu }
|
|
showRadial(item.menu)
|
|
elseif not item.keepOpen then
|
|
lib.hideRadial()
|
|
end
|
|
|
|
local onSelect = item.onSelect
|
|
|
|
if onSelect then
|
|
if type(onSelect) == 'string' then
|
|
return exports[menuResource][onSelect](0, currentMenu, itemIndex)
|
|
end
|
|
|
|
onSelect(currentMenu, itemIndex)
|
|
end
|
|
end)
|
|
|
|
RegisterNUICallback('radialBack', function(_, cb)
|
|
cb(1)
|
|
|
|
local numHistory = #menuHistory
|
|
local lastMenu = numHistory > 0 and menuHistory[numHistory]
|
|
|
|
if not lastMenu then return end
|
|
|
|
menuHistory[numHistory] = nil
|
|
|
|
if lastMenu.id then
|
|
return showRadial(lastMenu.id, lastMenu.option)
|
|
end
|
|
|
|
currentRadial = nil
|
|
|
|
-- Hide current menu and allow for transition
|
|
SendNUIMessage({
|
|
action = 'openRadialMenu',
|
|
data = false
|
|
})
|
|
|
|
Wait(100)
|
|
|
|
-- If menu was closed during transition, don't open the submenu
|
|
if not isOpen then return end
|
|
|
|
SendNUIMessage({
|
|
action = 'openRadialMenu',
|
|
data = {
|
|
items = menuItems,
|
|
option = lastMenu.option
|
|
}
|
|
})
|
|
end)
|
|
|
|
RegisterNUICallback('radialClose', function(_, cb)
|
|
cb(1)
|
|
|
|
if not isOpen then return end
|
|
|
|
lib.resetNuiFocus()
|
|
|
|
isOpen = false
|
|
currentRadial = nil
|
|
end)
|
|
|
|
RegisterNUICallback('radialTransition', function(_, cb)
|
|
Wait(100)
|
|
|
|
-- If menu was closed during transition, don't open the submenu
|
|
if not isOpen then return cb(false) end
|
|
|
|
cb(true)
|
|
end)
|
|
|
|
local isDisabled = false
|
|
|
|
---Disallow players from opening the radial menu.
|
|
---@param state boolean
|
|
function lib.disableRadial(state)
|
|
isDisabled = state
|
|
|
|
if isOpen and state then
|
|
return lib.hideRadial()
|
|
end
|
|
end
|
|
|
|
lib.addKeybind({
|
|
name = 'ox_lib-radial',
|
|
description = locale('open_radial_menu'),
|
|
defaultKey = 'z',
|
|
onPressed = function()
|
|
if isDisabled then return end
|
|
|
|
if isOpen then
|
|
return lib.hideRadial()
|
|
end
|
|
|
|
if #menuItems == 0 or IsNuiFocused() or IsPauseMenuActive() then return end
|
|
|
|
isOpen = true
|
|
|
|
SendNUIMessage({
|
|
action = 'openRadialMenu',
|
|
data = {
|
|
items = menuItems
|
|
}
|
|
})
|
|
|
|
lib.setNuiFocus(true)
|
|
SetCursorLocation(0.5, 0.5)
|
|
|
|
while isOpen do
|
|
DisablePlayerFiring(cache.playerId, true)
|
|
DisableControlAction(0, 1, true)
|
|
DisableControlAction(0, 2, true)
|
|
DisableControlAction(0, 142, true)
|
|
DisableControlAction(2, 199, true)
|
|
DisableControlAction(2, 200, true)
|
|
Wait(0)
|
|
end
|
|
end,
|
|
-- onReleased = lib.hideRadial,
|
|
})
|
|
|
|
AddEventHandler('onClientResourceStop', function(resource)
|
|
for i = #menuItems, 1, -1 do
|
|
local item = menuItems[i]
|
|
|
|
if item.resource == resource then
|
|
table.remove(menuItems, i)
|
|
end
|
|
end
|
|
end)
|