From e801856d1a2e2fab3183d4bd08d66bfbad7229c7 Mon Sep 17 00:00:00 2001 From: Selt <83926739+seltonmt012@users.noreply.github.com> Date: Mon, 27 Jul 2026 21:18:26 +0200 Subject: [PATCH 1/2] fix(esx_menu_dialog, esx_menu_list): do not grab NUI focus after the menu closed Opening a menu schedules SetNuiFocus(true, true) 200 ms later so the NUI has time to render. Closing sets the focus off but does not cancel that timer, so a menu closed within those 200 ms leaves the timer to fire afterwards and turn the focus back on with nothing open: the player gets a cursor and loses movement and input until another menu is opened and closed. esx_menu_list never stored the timer id at all, so it could not be cancelled in any case. Checking inside the callback whether a menu is still open fixes both, and also covers a quick open-close-open where cancelling by id would drop a focus that is still wanted. esx_menu_default is unaffected, it has no timer. Tested on artifact 25770 with a dialog closed 50 ms after opening: before, IsNuiFocused() was still true 600 ms later; after, it stays false. --- [core]/esx_menu_dialog/client/main.lua | 4 +++- [core]/esx_menu_list/client/main.lua | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/[core]/esx_menu_dialog/client/main.lua b/[core]/esx_menu_dialog/client/main.lua index 82588182..cce26666 100644 --- a/[core]/esx_menu_dialog/client/main.lua +++ b/[core]/esx_menu_dialog/client/main.lua @@ -15,7 +15,9 @@ local function openMenu(namespace, name, data) }) local timeoutId = ESX.SetTimeout(200, function() - SetNuiFocus(true, true) + if next(OpenedMenus) then + SetNuiFocus(true, true) + end end) table.insert(Timeouts, timeoutId) diff --git a/[core]/esx_menu_list/client/main.lua b/[core]/esx_menu_list/client/main.lua index 690c8ef6..6bab13a3 100644 --- a/[core]/esx_menu_list/client/main.lua +++ b/[core]/esx_menu_list/client/main.lua @@ -12,7 +12,9 @@ CreateThread(function() data = data, }) SetTimeout(200, function() - SetNuiFocus(true, true) + if next(OpenedMenus) then + SetNuiFocus(true, true) + end end) end From f5ed52d03b4cddb8940afe9ba8e73c9e36c5f262 Mon Sep 17 00:00:00 2001 From: Selt <83926739+seltonmt012@users.noreply.github.com> Date: Thu, 30 Jul 2026 02:38:25 +0200 Subject: [PATCH 2/2] fix(esx_menu_dialog): drop the timeout id bookkeeping With the callback checking whether a menu is still open, cancelling the previous timers by id is no longer needed. The table it kept them in was never emptied, so every open iterated over every id ever created and called ClearTimeout on all of them. That is quadratic, and clearTimeout only marks an id in xLib's CancelledTimeouts, which is cleared when the timer fires. Ids that had already fired stayed in there for good, in a table shared by every resource. --- [core]/esx_menu_dialog/client/main.lua | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/[core]/esx_menu_dialog/client/main.lua b/[core]/esx_menu_dialog/client/main.lua index cce26666..24253846 100644 --- a/[core]/esx_menu_dialog/client/main.lua +++ b/[core]/esx_menu_dialog/client/main.lua @@ -1,10 +1,6 @@ -local Timeouts, OpenedMenus, MenuType = {}, {}, "dialog" +local OpenedMenus, MenuType = {}, "dialog" local function openMenu(namespace, name, data) - for i = 1, #Timeouts, 1 do - ESX.ClearTimeout(Timeouts[i]) - end - OpenedMenus[namespace .. "_" .. name] = true SendNUIMessage({ @@ -14,13 +10,11 @@ local function openMenu(namespace, name, data) data = data, }) - local timeoutId = ESX.SetTimeout(200, function() + ESX.SetTimeout(200, function() if next(OpenedMenus) then SetNuiFocus(true, true) end end) - - table.insert(Timeouts, timeoutId) end local function closeMenu(namespace, name)