UI.Chat / UI.Board

The two widgets in the kit that drive a richer, purpose-built movie rather than one of the six generic ui_*.gfx files — each wraps a Scaleform movie authored for a specific, larger job.

UI.Board — a two-pane list-plus-details view

local b = UI.Board{
    title = "CONTRACTS", hint = "UP/DOWN BROWSE   ESC CLOSE", focus = true,
    items = {
        { header = "AVAILABLE" },
        { label = "Oil Refinery Raid", k = 1 },
    },
    onSelect = function(it, i, board)
        if it.k == 1 then
            board:detail{ category = "DESTRUCTION", rewards = { "$8,000", "Fuel +300" },
                objectives = { "Destroy 4 storage tanks", "Stay undetected" },
                progress = 0, progressText = "NOT STARTED" }
        end
    end,
    onChoose = function(it, i, board) UI.Toast("Accepted: " .. it.label) end,
    onBack   = function(board) board:hide() end,
}

UI.Board drives contracts.gfx — the identical movie the Contract Framework’s Contract Board uses. It shares that movie’s two-pane shape (a category-grouped list on the left, a details panel on the right: category line, up to 4 reward lines, up to 8 objective lines, a progress bar plus progress text) and its own selection/scrolling logic is a near-exact copy of UI.List’s — same header/row split, same scrollbar-thumb math, same nearest() skip-headers-when-navigating behavior — just paired with the extra :detail() call the richer movie’s details pane needs.

opts matches UI.List’s shape (x/y/w/h, title, hint, items, empty, focus) plus detail (an initial details payload). Beyond :items()/:selected()/:select()/:title()/:hint() (identical to UI.List), the one new call is:

board:detail{
    category    = "OIL FIELD",
    rewards     = { "$5000", "Fuel +200" },       -- up to 4 shown
    objectives  = { "Destroy 3 tanks", "Reach the LZ" },   -- up to 8 shown
    progress    = 0.4,           -- 0..1
    progressText = "2/5",
}

onSelect fires on every cursor move exactly as it does on UI.List — the one difference is that UI.Board:items() also fires it once immediately, so the details pane is already populated the instant the board opens or its list is refreshed, rather than staying blank until the player first presses up/down. It’s the expected place to call :detail() with that row’s own data, which is exactly the pattern menudemo.lua’s “Contract Board” entry uses: three items, three different :detail{} payloads picked by an it.k tag on each row.

menudemo.lua’s “Chat Log” entry is UI.Chat’s own real usage as a lazily-created, reused instance parked in the top-right corner (x = 640 - 360 - 8, y = 8), pushed to on demand rather than rebuilt every time. For UI.Chat doing real work rather than just demoing the widget, see coopchat.lua — a full co-op text chat built on it, with a packed/chunked encoding to carry arbitrary text over Net.SendCustomEvent.

local ch = D.chat
if not ch then ch = UI.Chat{ title = "RADIO", x = 640 - 360 - 8, y = 8 }; D.chat = ch end
ch:show():push("Misha: slick menu, boss.")

UI.Chat — a scrolling log with an optional typed line

local ch = UI.Chat{ title = "RADIO", x = 640 - 360 - 8, y = 8 }
ch:push("Misha: slick menu, boss.")
ch:prompt(function(text) Loader.Printf("you said: " .. text) end)

Drives chat.gfx. opts: x/y (default 20, 400), w/h (default 360, 132), title, max (scrollback cap, default 60), onSubmit.

  • :push(text) word-wraps and appends to the log (UI.wrap, same wrapping utility UI.Toast uses), trimming the oldest lines past max. Only the last 5 lines are ever visible at once; the body auto-resizes to however many of those 5 slots are actually filled.
  • :prompt([onSubmit]) enters typed-input mode — same CHAR-table-driven typing as UI.Input, Enter pushes the typed text as a new log line and fires onSubmit with it, Esc cancels without pushing anything.
  • :title(s) / :clear().

Not related to the Co-op Text Chat deep dive — that one is built on MrxGuiTextBuffer, worked around a real engine crash, and syncs typed input across a live network connection between two players. UI.Chat is purely local, general-purpose scrolling-log display — no network sync of any kind — included in the kit for future reuse (a radio/flavor-text log, a local debug/event feed, anything that wants “a scrolling list of lines plus an optional typed prompt”) rather than built specifically as a chat solution.

See also

  • Contract Framework: The Contract Board — the original contracts.gfx caller; UI.Board is a second, independent driver for the identical movie.
  • UI.ListUI.Board’s selection/scrolling logic is copied from here, extended with :detail().
  • Co-op Text Chat — a different, MrxGuiTextBuffer-based chat mechanism; not confirmed to be related to UI.Chat.

Back to top

This site uses Just the Docs, a documentation theme for Jekyll.