#ui.group( title ) → group
A titled block of controls in the script's page. If the script has not made a tab, the group goes into a tab the runtime makes for it, named like the script; otherwise into the newest tab.
Groups, tabs, floating windows and every kind of control - plus custom controls you draw yourself.
A script puts its settings in the menu, in the right half of its page in the Scripts tab - or in a floating window of its own. Controls are objects: the call that makes one returns it, and from then on you ask it for its value whenever you need it.
local group = ui.group( "aim helper" )
local enabled = group:toggle( "enabled", true )
local distance = group:slider( "max distance", 100, 3000, 1200, { step = 50, format = "%d u" } )
local mode = group:choice( "mode", { "closest", "lowest health", "crosshair" } )
on( "move", function( cmd )
if not enabled:get( ) then
return
end
local limit = distance:get( ) -- a number
local picked = mode:value( ) -- "closest", "lowest health" or "crosshair"
end )You rarely wait for a callback. The usual pattern is the one above: build the controls once at load, keep them in locals, read them in handlers. The values are saved for you - see Saving.
Controls are made by a group: group:toggle( ... ), group:slider( ... ). A group is a titled block. Where groups appear depends on how they were made.
ui.group( title ) → groupA titled block of controls in the script's page. If the script has not made a tab, the group goes into a tab the runtime makes for it, named like the script; otherwise into the newest tab.
ui.tab( title ) → tabA page of groups. A script with several tabs shows a row of buttons above its controls to switch between them. Groups made with ui.group afterwards go into the newest tab.
t:group( title ) → groupA group inside a particular tab.
local general = ui.tab( "general" )
local looks = ui.tab( "looks" )
local timing = general:group( "timing" )
local colours = looks:group( "colours" )ui.window{ options } → windowA floating window, drawn over the menu. It is moved by its title strip, keeps the position you drag it to while the script runs, and belongs to the script - when the script unloads it goes too. options is a table:
| Field | Meaning | Default |
|---|---|---|
title | The title shown in the window. | "window" |
x, y | Where it first appears, in pixels from the top left of the screen. | 120, 120 |
w, h | Its size. At least 160 by 80. | 280, 240 |
resizable | Whether the user can resize it - down to 200 by 120 at the smallest. | false |
persistent | true keeps it on screen with the menu closed. Otherwise it is only shown while the menu is open. | false |
open | Whether it is open from the start. | false |
local hud = ui.window{ title = "session", x = 24, y = 300, w = 240, h = 120, persistent = true, open = true }
local box = hud:group( "this round" )w:group( title ) → groupA group inside the window.
w:open(), w:close(), w:toggle()Open, close or flip the window. Opening a window that is already open is harmless.
w:is_open() → booleanw:position( [x, y] ) → x, yMoves the window when given coordinates, and returns where it is now.
g:toggle( label [, default] ) → controlA checkbox. get() is true or false. A toggle can also have a hotkey, set in the menu the way the cheat's own toggles get theirs or from code with c:set_key.
g:slider( label, min, max [, default [, options]] ) → controlA slider between min and max; default is where it starts (the minimum if left out). options:
| Field | Meaning |
|---|---|
step | Snap to multiples of this, counted from min. |
format | How the value is shown, a printf-style field: "%d", "%.1f", "%d ms". |
integer | true for a slider of whole numbers. |
A slider is made of whole numbers by itself when step is a whole number of at least 1 and min is whole. For a whole-number slider get() returns an integer; otherwise a number. The format must contain exactly one field - %d for whole numbers, or %f, %g, %e (with optional width and precision, such as %.2f) for others; text around it is fine. Anything else is replaced by the default (%d, %.1f, or %.2f for steps below 0.1). If min is larger than max the two are swapped; if they are equal, max becomes min + 1.
g:choice( label, items [, default_index] ) → controlOne of several, from a drop-down. items is a non-empty list of strings. get() is the picked index, counting from 1; value() is the picked text.
local mode = group:choice( "mode", { "off", "assist", "full" }, 2 )
if mode:get( ) == 3 then end -- "full"
if mode:value( ) == "assist" then endg:multi( label, items [, defaults] ) → controlAny number of several, from a drop-down of checkboxes. defaults is a list of booleans. get() is a list of booleans, one per item; get( 2 ) is the second one alone; value() is the list of the picked texts.
g:color( label [, default [, alpha]] ) → controlA colour picker. default is a colour, white if left out; alpha (default true) says whether the picker has an alpha channel. get() is a colour.
g:input( label [, default [, options]] ) → controlA line of text. options.max is the longest text it takes (1 to 1024, 128 by default); options.hint is the grey text shown while it is empty. get() is the text.
g:key( label [, key [, mode]] ) → controlA key binding. key is a virtual key, 0 (none) by default; the user rebinds it in the menu. mode is "hold" (the default - active while the key is down), "toggle" (each press flips it) or "always". get() says whether it is active right now, and it follows the key whether or not the menu is open.
local boost = group:key( "boost key", input.vk.mouse4, "hold" )
on( "move", function( cmd )
if boost:get( ) then
cmd:press( btn.jump )
end
end )g:button( label, handler ) → controlA button. handler is called, with no arguments, when it is clicked.
g:label( text [, color] ) → controlA line of text in the group, in the menu's text colour unless you give one. c:rename( text ) changes it later.
g:spacer( [height] ) → controlEmpty space, 8 pixels high unless you say otherwise (0 to 400).
g:separator() → controlA thin dividing line.
g:list( label, items [, options] ) → controlg:image( texture [, width [, height]] ) → controlg:custom{ options } → controlA rectangle of your own, filled by a function you write. See Custom controls.
These methods exist on every control, with the exceptions noted. Calls that change the control return it, so they chain: group:slider( ... ):tooltip( "..." ).
c:get( [item] ) → valueThe control's value:
| Kind | get() returns |
|---|---|
| toggle | true / false |
| slider | a number - an integer for a whole-number slider |
| choice, list | the picked index, from 1 (0 for an empty list) |
| multi | a list of booleans; get( i ) the single boolean for item i |
| color | a colour |
| input | the text |
| key | whether the key is active right now |
| the rest | nil |
c:set( value ) → controlChanges the value, with the same shapes as get() returns: a boolean, a number (clamped to the slider's range), an index, a colour, a string, a key code. For a multi, set( list ) takes a list of booleans, and set( index, true ) changes one item. Setting a value from code does not call on_change.
c:value() → valueLike get(), but a choice or a list answers the picked text, and a multi answers the list of picked texts. Everything else is the same as get().
c:on_change( handler ) → controlCalls handler( control ) after the user changes the control in the menu. c:on_change( nil ) takes the handler away.
local theme = group:choice( "theme", { "dark", "light" } )
theme:on_change( function( control )
print( "theme is now " .. control:value( ) )
end )c:show( [visible] ) → controlShows or hides the control. show() and show( true ) show it; show( false ) hides it. A hidden control keeps its value and is still saved.
c:enable( [enabled] ) → controlEnables or disables it. A disabled control is shown as plain dim text - its label and current value - and cannot be changed.
c:tooltip( text ) → controlThe text shown when the pointer rests on the control.
c:rename( text ) → controlChanges the label that is shown. The control keeps its saved value, because values are stored under the label it was created with.
c:items( list ) → controlReplaces the entries of a choice, a multi or a list with a new list of strings. The picked index is kept as far as it still fits; a multi keeps its ticks when the number of items stays the same and starts clear when it changes. Any drop-down that is open is closed.
c:set_range( min, max ) → controlChanges the range of a slider. The value is clamped into it.
c:key() → integerThe virtual key bound to a toggle's hotkey, or the key of a key control; 0 when there is none.
c:set_key( key [, mode] ) → controlBinds a key. On a toggle, mode is "toggle", "hold" or "hold_off"; on a key control it is "hold", "toggle" or "always". Leave it out to keep the current mode.
c:click()Presses a button from code, calling its handler. Only buttons have one.
c:kind() → string"toggle", "slider", "choice", "multi", "color", "text", "key", "button", "label", "spacer", "separator", "list", "image" or "custom".
When none of the controls is the right one - a meter, a graph, a colour wheel, a little preview of what the script does - draw it yourself. g:custom reserves a rectangle in the group and calls your function every frame the control is visible:
local level = 0.5
group:custom{
height = 28,
draw = function( canvas, rect, io )
if io.down then
level = math.clamp( io.x / rect.w, 0, 1 )
end
canvas:fill( 0, 0, rect.w, rect.h, color( 0, 0, 0, 90 ), 6 )
canvas:fill( 0, 0, rect.w * level, rect.h, ui.accent( ), 6 )
canvas:text( 8, 6, string.format( "%d%%", math.floor( level * 100 + 0.5 ) ), color( 255, 255, 255 ), { shadow = true } )
end,
}The options table has two fields: height (8 to 800 pixels, 80 by default) and draw, which is required. The function receives three arguments.
canvas - a canvas that draws inside the control. Its origin 0, 0 is the control's top left corner and everything is cut off at its edges. It is the same canvas as in frame, except that c:glow does nothing and c:layer is refused.
rect - the control's rectangle as { x = 0, y = 0, w, h }: the width is whatever the group has room for, the height is the one you asked for.
io - what the user is doing, for this frame:
| Field | Meaning |
|---|---|
x, y | The mouse position relative to the control's top left corner. It keeps tracking while a drag that started on the control leaves it. |
hover | The pointer is over the control (and nothing is drawn on top of it). |
down | The left button is held while the pointer is over the control, or while a drag that started on it carries on outside. |
clicked | The left button went down this frame over the control. |
released | The left button went up this frame over the control, or at the end of a drag that started on it. |
right_clicked | The right button went down this frame over the control. |
focused | The last click landed on this control. |
scroll | Wheel movement this frame while the pointer is over it. |
keys | A list of the virtual keys pressed this frame. |
text | The text typed this frame, as one string. |
keys and text are only filled while the control has the focus - its last click - or, when no control has it, while the pointer is over this one. So a custom text box works the way a real one does.
The function runs on the render thread under the usual time budget. A custom control is not saved - if its state should survive a restart, keep it in a store.
The values of toggles, sliders, choices, multis, colours, text inputs and keys are saved, and come back the next time the script loads. A moment after you change one - not on every frame of a drag - the script's values are written to .data\<name>.values.json, and they are written again when the script unloads.
A value is stored under the tab, the group's title and the control's label it was created with. The consequences:
g:...( label ) call makes that control a new one with its default; the old value is simply no longer asked for.c:rename( text ) changes what is shown but not the stored name.To reset a script, switch it off and delete .data\<name>.values.json.
ui.notify( text [, options] )Shows a toast in the corner of the screen, over everything including the menu. options:
| Field | Meaning | Default |
|---|---|---|
title | A bold line above the text. | none |
seconds | How long it stays, 0.5 to 30. | 4 |
color | The colour of the stripe on its left. | the accent colour |
At most eight toasts are shown at once; a new one pushes the oldest out, so a script that notifies in a loop cannot bury the screen.
ui.notify( "saved", { title = "settings", seconds = 2, color = color( 120, 220, 140 ) } )ui.is_open() → booleanWhether the menu is showing.
ui.theme() → tableThe menu's colours, so a script can match them: a table of colours with the keys accent, text, text_dim, window, child, button, checkbox, slider, card and elevated.
ui.accent() → colorThe accent colour - the same as ui.theme().accent.