Violet

Game UI

On this page

core.game_ui finds the game's own windows and the controls inside them: buttons, check boxes, text boxes, drop-down lists, tabs, sliders, scroll bars and text labels. You can read what each control shows and operate it the way a player does, by clicking, pressing keys, typing, ticking boxes, choosing items and moving sliders.

Windows are returned as Window objects and controls as Control objects.

Objects follow one window or control

A Window or Control object always refers to the window or control it was created for. Once that window closes or the control is removed, is_valid() returns false, getters return nil, and actions return false, error_message. An object never starts referring to a different window.

Every call creates a new object, so compare two windows with get_handle() rather than ==. A handle stays the same for as long as the window is open, so you can store it and look the window up again later with core.game_ui.get_window().

To react to what happens in game windows, use the on_ui_event, on_window_open and on_window_close callbacks.

Finding windows

core.game_ui.get_windows() -> table

Returns every open top-level window as an array of Window objects. Windows nested inside another window are returned by window:get_child_windows() instead.


core.game_ui.find_window(filter: table | function | nil) -> Window | nil

Returns the first open window that matches filter, or nil if none does. filter is a table of the fields below, all of which must match, or a function that receives each Window and returns true to accept it. Without a filter, any window matches.

FieldTypeMatches windows that
kindstringhave this get_kind() value
ui_typenumberhave this get_ui_type() number
resourcestringhave a get_resource_path() that contains this text
control_textstringcontain a control, directly or in a child window, whose text contains this

Text matching ignores case. Raises an error when filter is not a table or a function, a field has the wrong type, kind is not one of the kinds listed under get_kind(), ui_type is not a whole number, or a text field is longer than 256 characters.

lua
local shop = core.game_ui.find_window({ kind = "shop" })

local wide = core.game_ui.find_window(function(window)
    local size = window:get_size()
    return size ~= nil and size.width > 500
end)

core.game_ui.find_windows(filter: table | function | nil) -> table

Same as find_window, but returns every matching window as an array, which is empty when none match.


core.game_ui.get_window(handle: number) -> Window | nil

Returns the window with this handle while it is open, or nil.


core.game_ui.get_control(handle: number) -> Control | nil

Returns the control with this handle while it exists, or nil.


core.game_ui.get_focused_control() -> Control | nil

Returns the control that has keyboard focus, such as the text box you are typing in, or nil when no control has it.

Window

window:is_valid() -> boolean

Returns true while the window is open.


window:get_handle() -> number | nil

Returns the number that identifies this window while it is open.


window:get_kind() -> string | nil

Returns what sort of window this is:

KindWindow
"npc_chat"A conversation with an NPC
"message_box"A notice or a yes/no question
"shop"An NPC shop
"storage"Storage
"trade"A trade with another player
"ui_window"Another standard game window
"dialog"Any other dialog box
"window"Any other window

window:get_ui_type() -> number | nil

Returns the number the game gives this kind of window, or nil for windows that don't have one. Every ui_window has one, and some windows of the other kinds do too. A window has the same number every time it opens, which makes the number a reliable way to recognise it. Numbers can change when the game updates.

To find a window's number, open the window and list what is open:

lua
for _, window in ipairs(core.game_ui.get_windows()) do
    core.log(string.format("%s  ui_type=%s  %s", window:get_kind(),
        tostring(window:get_ui_type()), window:get_resource_path() or "-"))
end

window:get_resource_path() -> string | nil

Returns the path of the window's artwork in the game's UI data, or nil when it has none. Like get_ui_type(), it tells windows apart. The resource filter matches part of it.


window:get_position() -> table | nil

Returns the top-left corner of the window on the game screen as a table with x and y fields.


window:get_size() -> table | nil

Returns the window's size as a table with width and height fields.


window:is_enabled() -> boolean | nil

Returns whether the window accepts input.


window:get_controls() -> table | nil

Returns the window's own controls as an array of Control objects. Controls inside child windows are not included; window:find_controls() searches both.


window:get_child_windows() -> table | nil

Returns the windows nested inside this one as an array of Window objects.


window:find_control(filter: table | function | nil) -> Control | nil

Returns the first control that matches filter, or nil if none does. It searches the window's own controls first, then those of its child windows, up to four levels deep. filter is a table of the fields below, all of which must match, or a function that receives each Control and returns true to accept it.

FieldTypeMatches controls that
idnumberhave this get_id()
typestringhave this get_type()
textstringhave a get_text() that contains this text, ignoring case
visiblebooleanare visible, when true

Raises an error when filter is not a table or a function, a field has the wrong type, type is not one of the types listed under get_type(), id is not a whole number, or text is longer than 256 characters.

lua
local window = core.game_ui.find_window({ kind = "npc_chat" })
if window then
    for _, control in ipairs(window:find_controls({ visible = true })) do
        core.log(string.format("%s  id=%d  %s", control:get_type(), control:get_id(), control:get_text() or ""))
    end
end

window:find_controls(filter: table | function | nil) -> table | nil

Same as find_control, but returns every matching control as an array.


window:click_button(id: number) -> boolean, string?

Clicks the button with this id in the window or one of its child windows, the same way as control:click(). Returns false, error_message when the window has closed, has no button with that id, or the button is hidden or disabled.

Control

control:is_valid() -> boolean

Returns true while the control exists.


control:get_handle() -> number | nil

Returns the number that identifies this control while it exists.


control:get_id() -> number | nil

Returns the control's id within its window. A window's layout fixes the ids of its controls, so a button has the same id every time its window opens.


control:get_type() -> string | nil

Returns what sort of control this is:

TypeControl
"button"A button
"check_box"A check box
"combo_box"A drop-down list
"edit"A single-line text box
"multiline_edit"A text box with several lines
"scroll_bar"A scroll bar
"slider"A slider
"static_text"A text label
"tab"A row of tabs
"control"Any other control

control:get_window() -> Window | nil

Returns the window that contains the control.


control:get_text() -> string | nil

Returns the text the control shows: the label of a button or check box, the contents of a text box, the text of a label, the selected item of a drop-down list or the name of the selected tab. Returns nil for other controls.


control:is_enabled() -> boolean | nil

Returns whether the control accepts input.


control:is_visible() -> boolean | nil

Returns whether the control is shown.


control:get_position() -> table | nil

Returns the top-left corner of the control on the game screen as a table with x and y fields.


control:get_size() -> table | nil

Returns the control's size as a table with width and height fields.


control:is_checked() -> boolean | nil

Returns whether a check box is ticked or a button is toggled on. Returns nil for other controls.


control:is_read_only() -> boolean | nil

Returns whether a text box is read-only, or nil for other controls. set_text() refuses read-only boxes.


control:is_password() -> boolean | nil

Returns whether a single-line text box hides what is typed in it, or nil for other controls. get_text() still returns its contents.


control:get_value() -> number | nil

Returns the position of a slider or scroll bar, or nil for other controls.


control:get_range() -> number, number | nil

Returns the lowest and highest positions a slider or scroll bar accepts, or nil for other controls.


control:get_items() -> table | nil

Returns the items of a drop-down list or the names of a row of tabs, as an array of strings in display order. Returns nil for other controls.


control:get_selected() -> number | nil

Returns the 0-based index of the selected item or tab, or nil for other controls.

Actions

Actions work like a player's input, and the window reacts as it would to a real click, key press or keystroke. Each returns true when the input was delivered, or false, error_message when nothing was done, for example because the control is hidden, disabled or gone.

control:click() -> boolean, string?

Clicks the middle of the control with the left mouse button.


control:right_click() -> boolean, string?

Clicks the middle of the control with the right mouse button.


control:press_key(key: number) -> boolean, string?

Presses and releases a key on the control. key is a Windows virtual-key code from 1 to 254. For example, 0x0D (Enter) in a text box does what pressing Enter while typing in it does.


control:set_text(text: string) -> boolean, string?

Replaces the contents of a text box by clearing it and typing text one character at a time. The box applies the same rules as it does to your typing, so it may shorten the text, skip characters it doesn't take (such as letters in a number box) or lower a number to its limit. Read get_text() afterwards to see what was entered. text can be up to 4096 characters. Characters outside the Western European (Windows-1252) set become the closest match or ?, and line breaks are only typed into multi-line boxes. Returns false, error_message for controls that are not text boxes and for read-only boxes.


control:set_checked(checked: boolean) -> boolean, string?

Ticks or clears a check box by clicking it when it isn't already in that state. Returns false, error_message when the control is not a check box or the click did not change it. Some check boxes work like option buttons and can't be cleared by clicking them.


control:set_value(value: number) -> boolean, string?

Moves a slider or scroll bar to value, kept within get_range(). The window reacts as it does when you drag the slider or scroll bar there and let go. Returns false, error_message for other controls.


control:select(item: number | string) -> boolean, string?

Selects an item in a drop-down list or a tab in a row of tabs, by 0-based index or by its exact text (ignoring case). The window reacts as it does when you choose it yourself. Returns false, error_message for other controls, an index out of range, text that matches no item, or an item the game won't let you choose.

Example

lua
-- Type an amount into the focused text box and confirm it with Enter.
local box = core.game_ui.get_focused_control()
if box and (box:get_type() == "edit" or box:get_type() == "multiline_edit") then
    local ok, err = box:set_text("100")
    if ok then
        box:press_key(0x0D)
    else
        core.log_error(err)
    end
end

-- Report every window that opens and every button clicked in it.
core.register_callback("on_window_open", function(window)
    core.log("opened " .. window:get_kind() .. " " .. (window:get_resource_path() or ""))
end)

core.register_callback("on_ui_event", function(event)
    if event.type == "click" and event.window then
        core.log("clicked button " .. event.control_id .. " in " .. event.window:get_kind())
    end
end)