2025-03-08 13:44:05 +00:00
--[[
Menu Opening Module
---------------------
This module provides a unified function to open menus using the configured menu system .
Supported systems include :
2025-04-20 19:17:40 +01:00
• jim_bridge ( built - in nui menu that works on any framework )
2025-03-08 13:44:05 +00:00
• ox ( or ox_context )
• qb ( using QBMenuExport )
• gta ( using WarMenu )
• esx ( using ESX.UI . Menu )
] ]
2025-03-01 12:58:43 +00:00
--- Opens a menu using the configured menu system.
---
--- This function translates code and creates menus in several different menu scripts, depending on the configured menu system specified in `Config.System.Menu`.
---
---@param Menu table A table containing the menu options to display.
--- Each menu item can include:
2025-03-08 13:44:05 +00:00
--- - header (`string`): The text to display for the menu item.
--- - txt (`string`, optional): Additional text or description.
--- - icon (`string`, optional): Icon to display with the menu item.
--- - onSelect (`function`, optional): Function to execute when the menu item is selected.
--- - arrow (`boolean`, optional): Whether to display an arrow next to the item (for certain menus).
--- - params (`table`, optional): Additional parameters, such as events and arguments.
--- - isMenuHeader (`boolean`, optional): Marks the item as a header.
--- - disabled (`boolean`, optional): Disables the menu item if `true`.
2025-03-01 12:58:43 +00:00
---
---@param data table A table containing configuration data for the menu.
2025-03-08 13:44:05 +00:00
--- - header (`string`): The header/title of the menu.
--- - headertxt (`string`, optional): Additional header text.
--- - onBack (`function`, optional): Function to call when the "Return" option is selected.
--- - onExit (`function`, optional): Function to call when the menu is exited.
--- - onSelected (`function`, optional): Function to call when a menu item is selected (for certain menu systems).
--- - canClose (`boolean`, optional): Whether the menu can be closed by the user.
2025-03-01 12:58:43 +00:00
---
---@usage
--- ```lua
--- openMenu({
--- { header = "Option 1", txt = "Description 1", onSelect = function() print("Option 1 selected") end },
--- { header = "Option 2", txt = "Description 2", onSelect = function() print("Option 2 selected") end },
--- }, {
--- header = "Main Menu",
--- headertxt = "Select an option",
--- onBack = function() print("Return selected") end,
--- onExit = function() print("Menu closed") end,
--- canClose = true,
--- })
--- ```
function openMenu ( Menu , data )
2025-07-04 15:20:02 +01:00
if Config.System . Menu == " ox " then
2025-03-01 12:58:43 +00:00
local index = nil
if data.onBack and not data.onSelected then
table.insert ( Menu , 1 , {
icon = " fas fa-circle-arrow-left " ,
title = " Return " ,
onSelect = data.onBack ,
label = " Return " ,
} )
end
for k in pairs ( Menu ) do
if data.onSelected and Menu [ k ] . arrow then
Menu [ k ] . icon = " fas fa-angle-right "
end
2025-03-08 13:44:05 +00:00
-- If no title, use header or txt as title/label.
2025-03-01 12:58:43 +00:00
if not Menu [ k ] . title then
if Menu [ k ] . header ~= nil and Menu [ k ] . header ~= " " then
Menu [ k ] . title = Menu [ k ] . header
Menu [ k ] . label = Menu [ k ] . header
if Menu [ k ] . txt then Menu [ k ] . description = Menu [ k ] . txt else Menu [ k ] . description = " " end
else
Menu [ k ] . title = Menu [ k ] . txt
Menu [ k ] . label = Menu [ k ] . txt
end
end
2025-03-08 13:44:05 +00:00
-- Copy parameters from 'params' if available.
2025-03-01 12:58:43 +00:00
if Menu [ k ] . params then
Menu [ k ] . event = Menu [ k ] . params.event
Menu [ k ] . args = Menu [ k ] . params.args or { }
end
if Menu [ k ] . isMenuHeader then
2025-03-26 21:43:48 +00:00
Menu [ k ] . readOnly = true
2025-03-01 12:58:43 +00:00
end
end
local menuID = ' Menu '
( data.onSelected and lib.registerMenu or lib.registerContext ) ( {
id = menuID ,
title = data.header .. br .. br .. ( data.headertxt and data.headertxt or " " ) ,
position = ' top-right ' ,
options = Menu ,
canClose = data.canClose and data.canClose or nil ,
onClose = ( data.onBack and data.onBack ) or ( data.onExit and data.onExit ) or nil ,
onExit = data.onExit and data.onExit or nil ,
onSelected = data.onSelected and ( function ( selected ) index = selected end ) or nil ,
} , data.onSelected and ( function ( x , y , args )
if Menu [ x ] . refresh then
if Menu [ x ] . onSelect then
Menu [ x ] . onSelect ( )
end
lib.showMenu ( menuID , index )
else
if Menu [ x ] . onSelect then
Menu [ x ] . onSelect ( )
else
lib.showMenu ( menuID , index )
end
end
end ) or nil )
if data.onSelected then
lib.showMenu ( menuID , 1 )
else
lib.showContext ( menuID )
end
elseif Config.System . Menu == " qb " then
if data.onBack then
table.insert ( Menu , 1 , {
icon = " fas fa-circle-arrow-left " ,
header = " " ,
txt = " Return " ,
params = {
isAction = true ,
event = data.onBack ,
} ,
} )
elseif data.canClose then
table.insert ( Menu , 1 , {
icon = " fas fa-circle-xmark " ,
header = " " ,
txt = " Close " ,
params = {
isAction = true ,
event = data.onExit and data.onExit or ( function ( ) exports [ QBMenuExport ] : closeMenu ( ) end ) ,
} ,
} )
end
if data.header ~= nil then
local tempMenu = { }
for k , v in pairs ( Menu ) do tempMenu [ k + 1 ] = v end
tempMenu [ 1 ] = { header = data.header , txt = data.headertxt or " " , isMenuHeader = true }
Menu = tempMenu
end
for k in pairs ( Menu ) do
if not Menu [ k ] . params or not Menu [ k ] . params.event then
2025-03-08 13:44:05 +00:00
Menu [ k ] . params = {
isAction = true ,
event = Menu [ k ] . onSelect or function ( ) end ,
}
2025-03-01 12:58:43 +00:00
end
if not Menu [ k ] . header then Menu [ k ] . header = " " end
if Menu [ k ] . arrow then Menu [ k ] . icon = " fas fa-angle-right " end
2025-03-06 23:49:07 +00:00
Menu [ k ] . isMenuHeader = Menu [ k ] . isMenuHeader or Menu [ k ] . disable
2025-03-01 12:58:43 +00:00
end
exports [ QBMenuExport ] : openMenu ( Menu )
elseif Config.System . Menu == " gta " then
2025-06-06 00:45:08 +01:00
WarMenu.CreateMenu ( tostring ( Menu ) , data.header , " " , {
2025-03-08 13:44:05 +00:00
titleColor = { 222 , 255 , 255 } ,
maxOptionCountOnScreen = 15 ,
width = 0.25 ,
x = 0.7 ,
} )
2025-03-01 12:58:43 +00:00
if WarMenu.IsAnyMenuOpened ( ) then return end
WarMenu.OpenMenu ( tostring ( Menu ) )
CreateThread ( function ( )
local close = true
while true do
if WarMenu.Begin ( tostring ( Menu ) ) then
if data.onBack then
if WarMenu.SpriteButton ( " Return " , ' commonmenu ' , " arrowleft " , 127 , 127 , 127 ) then
WarMenu.CloseMenu ( )
Wait ( 10 )
data.onBack ( )
end
end
for k in pairs ( Menu ) do
local pressed = WarMenu.Button ( Menu [ k ] . header )
if not Menu [ k ] . header then
Menu [ k ] . header = Menu [ k ] . txt
Menu [ k ] . txt = nil
end
if Menu [ k ] . txt and Menu [ k ] . txt ~= " " and WarMenu.IsItemHovered ( ) then
if Menu [ k ] . disabled or Menu [ k ] . isMenuHeader then
WarMenu.ToolTip ( " ~r~ " .. Menu [ k ] . txt , 0.18 , true )
else
WarMenu.ToolTip (
( Menu [ k ] . blip and " ~BLIP_ " .. " 8 " .. " ~ " or " " ) ..
Menu [ k ] . txt : gsub ( " %: " , " :~g~ " ) : gsub ( " % \n " , " \n ~s~ " ) , 0.18 ,
true )
end
end
if pressed and not Menu [ k ] . isMenuHeader then
WarMenu.CloseMenu ( )
close = false
2025-06-06 00:45:08 +01:00
Wait ( 10 )
2025-03-01 12:58:43 +00:00
Menu [ k ] . onSelect ( )
end
end
WarMenu.End ( )
else
return
end
if not WarMenu.IsAnyMenuOpened ( ) and close then
stopTempCam ( cam )
if data.onExit then data.onExit ( ) end
end
Wait ( 0 )
end
end )
elseif Config.System . Menu == " esx " then
for k in pairs ( Menu ) do
Menu [ k ] . label = Menu [ k ] . header
Menu [ k ] . name = " button " .. k
end
if data.canClose then
table.insert ( Menu , 1 , {
icon = " fas fa-circle-xmark " ,
label = " Close " ,
name = " close " ,
onSelect = data.onExit ,
} )
end
if data.onBack then
table.insert ( Menu , 1 , {
icon = " fas fa-circle-arrow-left " ,
label = " Return " ,
name = " return " ,
onSelect = data.onBack ,
} )
end
ESX.UI . Menu.Open ( " default " , getScript ( ) , " Example_Menu " , {
title = data.header ,
align = ' top-right ' ,
elements = Menu ,
} ,
function ( menuData , menu )
for k in pairs ( Menu ) do
if menuData.current . name == Menu [ k ] . name then
menu.close ( )
Wait ( 10 )
Menu [ k ] . onSelect ( )
end
end
end ,
function ( data , menu )
menu.close ( )
end )
2025-07-04 15:20:02 +01:00
elseif Config.System . Menu == " lation " then
if data.onBack then
table.insert ( Menu , 1 , {
icon = " fas fa-circle-arrow-left " ,
onSelect = data.onBack ,
header = " Return " ,
} )
end
for k in pairs ( Menu ) do
if data.onSelected and Menu [ k ] . arrow then
Menu [ k ] . icon = " fas fa-angle-right "
end
-- If no title, use header or txt as title/label.
if not Menu [ k ] . title then
if Menu [ k ] . header ~= nil and Menu [ k ] . header ~= " " then
Menu [ k ] . title = Menu [ k ] . header
Menu [ k ] . label = Menu [ k ] . header
if Menu [ k ] . txt then Menu [ k ] . description = Menu [ k ] . txt else Menu [ k ] . description = " " end
else
Menu [ k ] . title = Menu [ k ] . txt
Menu [ k ] . label = Menu [ k ] . txt
end
end
-- Copy parameters from 'params' if available.
if Menu [ k ] . params then
Menu [ k ] . event = Menu [ k ] . params.event
Menu [ k ] . args = Menu [ k ] . params.args or { }
end
if Menu [ k ] . isMenuHeader then
Menu [ k ] . readOnly = true
end
end
exports.lation_ui : registerMenu ( {
id = ' menu ' ,
title = data.header ,
subtitle = ( data.headertxt and data.headertxt or " " ) ,
options = Menu ,
} )
-- Show menu
exports.lation_ui : showMenu ( ' menu ' )
2025-03-01 12:58:43 +00:00
end
end
2025-03-08 13:44:05 +00:00
--- A line break constant used for menu header formatting.
2025-07-04 15:20:02 +01:00
br = ( Config.System . Menu == " ox " or Config.System . Menu == " gta " or Config.System . Menu == " lation " ) and " \n " or " <br> "
2025-03-01 12:58:43 +00:00
2025-03-08 13:44:05 +00:00
--- Checks if the current menu system is 'ox' or 'gta' for formatting purposes.
--- @return boolean boolean True if using ox or gta menus, otherwise false.
2025-03-01 12:58:43 +00:00
--- @usage
--- ```lua
--- if isOx() then
--- -- Use specific formatting
--- end
--- ```
function isOx ( ) return ( Config.System . Menu == " ox " or Config.System . Menu == " gta " ) end
--- Checks if any WarMenu menu is currently open.
---
2025-03-08 13:44:05 +00:00
--- @return boolean boolean Returns `true` if a WarMenu menu is open; otherwise, `false`.
2025-03-01 12:58:43 +00:00
---
--- @usage
--- ```lua
--- if isWarMenuOpen() then
--- -- Do something
--- end
--- ```
2025-02-21 13:47:15 +00:00
function isWarMenuOpen ( ) if Config.System . Menu == " gta " then return WarMenu.IsAnyMenuOpened ( ) else return false end end