Chem O’DunReferences and Guides

UIX callbacks

Every hook UI Extensions and HUD puts into X4's menus. UIX ships patched copies of the vanilla menu files with callback dispatch points added, and a mod registers a function against one by name: that is how a UI mod changes a menu without replacing the file, and how two mods change the same menu without fighting. Read out of the mod's own .xpl files at every one of its 33 releases from 8.0.0.1 onwards, so each hook carries the release it appeared in. The current ones are 8.0.4.10 (8.x) and 9.0.0.14 (9.x).

295 callbacks across 19 menus, 118 of which are given a return value the menu then uses. The mod's own readme says no list of them exists and to search the code, so this is that list, and the descriptions are written by hand: 13 of 295 so far.

Browse

Open a row for the registration call, what the hook is handed, what it may return and which releases have it.

Download uix-callbacks.lua

152 KB. This page as a Lua Language Server meta file, which is also the file it is built from: one table per menu, every callback with its contract above it. Point an editor at it as a library and a handler gets completion and signatures for what it is passed.

How to register one

A callback lives on the table that dispatches it, and there are two of those: the menu, and the global Helper. A menu is reached by the name it registers itself under, which is not its file name.

local function init()
    local m = Helper.getMenu("MapMenu")
    m.registerCallback("createPropertyOwned_on_start", function(config)
        -- ...
    end, "my_mod_id")
end
init()

The third argument is an id of your own. It is optional, and worth passing anyway: it is what m.deregisterCallback(name, nil, "my_mod_id") needs, and registering the same name twice under one id is ignored rather than doubled. A Helper hook is Helper.registerCallback(name, fn, id) with no menu in front of it.

Every card below carries the exact call for that hook, with its parameters filled in.

How to read a row
Kindthe return value is discarded: the hook only says that something happened and the return value is used, so what a callback returns changes what the menu does. 177 events, 118 overrides.
AggregationWhat happens when more than one mod registers against the same hook. Nothing at the call site announces it, and it is the field a reader cannot guess, so it is measured:
nonenothing is done with the return value. 177 of them.
last-winsevery registered callback runs and the last one to return decides. 65 of them.
unanimousevery registered callback has to agree, and one dissenter flips the value. 1 of them.
chainedeach callback is handed what the one before it returned. 23 of them.
short-circuitthe first truthy return stops the loop, so later callbacks never run. 15 of them.
multi-valueseveral values go in and the same several come back. 14 of them.
Registered ondispatched off the menu table, so it is registered on that menu.
dispatched off the global Helper table, so it is registered with Helper.registerCallback.
dispatched off a menu handed to a Helper function, so it is registered on whichever menu is calling.
AvailableThe 8.x and 9.x lines run in parallel - 8.0.4.10 and 9.0.0.14 are both current - so a card answers per line, and the row's NEW marks the latest additions to either (8.0.4.10 and 9.0.0.13).
every 8.x release present throughout that line.
9.x from 9.0.0.4 onwards added there, and still current.
never in the 8.x line a hook that line never had, so a mod using it will not run on it.
8.x up to …, gone since dropped. A hook that disappears takes every mod registered against it with it, silently: the registration still succeeds and is simply never called.
The Available in filter reads the same way: each line offers what its current release has, then what every release changed. 9.0.0.13 (+17) is the seventeen hooks that release introduced, not the 293 it carries - "present in" is true of nearly everything and so says nothing. 9.0.0.14 (0) means that release altered UIX without adding or dropping a hook, which is most of them.
CopyingAn open card carries Copy name, Copy registration and Copy link; the last gives a URL that reopens that card.

The version axis here is the mod's own release tags, not the game's. A UIX release usually follows a game version, but a callback appears when kuertee adds it, which is what a mod author is actually pinned to.

Where a description comes from, and how to write one

Nothing in UIX describes what a callback is for. The names carry a convention - [function]_[action], with _on_ and a present-tense verb for an event - and that is the whole of what exists, which is why most cards below say so. What descriptions there are have been written by hand into uix-callbacks.lua itself: the file offered above is both what this page is built from and where its prose lives, so a description and the hook it belongs to are never apart.

An entry takes three authored things, and nothing else in it is authored:

-- Menu: menu_transporter (ego_detailmonitor)
-- Menu name: TransporterMenu
-- Function: menu.display
-- Holder: menu
-- Kind: override
-- Aggregation: unanimous
-- Args: active
-- Returns: result
-- Return fields: active
-- Since: pre-8.0
-- Versions: 8.0.0.1..8.0.4.10, 9.0.0.3..9.0.0.14
-- Seen at: menu_transporter.xpl:705 (9.0.0.14)
-- Added by: kuertee
--- Decides whether the transporter room's "Go to" button is enabled.
---@param active any # what the menu decided on its own
---@return any # a table; only its `active` field is read
function menu_transporter.display_on_set_room_active(active) end

The --- lines are the description, and the text after the # on a ---@param or ---@return says what that one value is. Everything reading -- Key: value is generated from the extraction and rewritten whenever UIX moves on, Since: included - it is measured against every release, not remembered - while the authored kinds are carried across untouched. The file is on GitHub, and a description added to it is a pull request against that one file.

Using it in an editor

The Lua Language Server reads a meta file when it is listed as a workspace library. In VS Code that is .luarc.json beside the workspace root, or the same key in settings:

{
  "workspace.library": [ "path/to/uix-callbacks.lua" ]
}

The declarations exist so an editor has something to complete against and are never loaded by the game. Parameters are typed any deliberately: what a hook is handed is a menu's own local, and naming a type it does not have would be a guess an editor then enforces. For the wider set of X4 Lua definitions there is a packaged addon, X4-LuaLSAddon, and for the rest of the UI namespace the globals and C functions references.

Callbacks

NameMenuKindAggregationParameters